Spring相关介绍

本文详细介绍了Spring相关知识,包括SpringMVC执行流程,从用户请求到视图渲染的各环节;阐述了bean生命周期的多个阶段;对比了Spring IOC容器BeanFactory和ApplicationContext的特点;还提及Spring扩展接口及WebFlux、REST API等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、springMVC执行流程:
    1.用户发起请求到前端控制器(DispatcherServlet),该控制器会过滤出哪些请求可以访问Servlet、哪些不能访问,就是url-pattern的作用,并且会加载springmvc.xml配置文件
    2.前端控制器会找到处理器映射器(HandlerMapping),通过HandlerMapping完成url到controller映射的组件,简单来说就是将在springmvc.xml中配置的或者注解的url与对应的处理类找到并进行存储,用map<url,handler>这样的方式来存储
    3.HandlerMapping有了映射关系,并且找到url对应的处理器,HandlerMapping就会将其处理器(Handler)返回,在返回前,会加上很多拦截器
    4.DispatcherServlet拿到Handler后,找到HandlerAdapter(处理器适配器),通过它来访问处理器,并执行处理器
    5.执行处理器
    6.处理器会返回一个ModelAndView对象给HandlerAdapter
    7.通过HandlerAdapter将ModelAndView对象返回给前端控制器(DispatcherServlet)
    8.前端控制器请求视图解析器(ViewResolver)去进行视图解析,根据逻辑视图名解析成真正的视图(jsp),其实就是将ModelAndView对象中存放视图的名称进行查找,找到对应的页面形成视图对象
    9.返回视图对象到前端控制器
    10.视图渲染,就是将ModelAndView对象中的数据放到request域中,用来让页面加载数据的(通过第8步,通过名称找到了对应的页面,通过第10步,request域中有了所需要的数据,那么就能够进行视图渲染了。最后将其返回即可)

               图示:

                           


    
2、bean生命周期:
    1.是否有实现了BeanFactoryPostProcessor接口的实现类bean,回调该bean中的postPrcessorBeanFactory()方法来对Bean的配置元数据进行更改
    2.实例化bean对象(bean的实例化是使用反射实现的)
    3.bean属性注入
    4.检查是否实现Aware相关接口(如BeanNameAware、BeanFactoryAware、ApplicationContextAware),设置相关依赖
    5.若BeanPostProcessor后置处理器实例与bean实例关联,调用postProcessBeforeInitialization方法
    6.是否带有@PostConstruct方法
    7.是否实现InitializingBean接口,调用afterPropertySet方法
    8.是否配置有自定义init-method
    9.若BeanPostProcessor后置处理器实例与bean实例关联,调用postProcessAfterInitialization方法
    10.bean的使用过程....................
    11.是否带有@PreDestroy方法
    12.是否实现DisposableBean接口,调用destroy方法
    13.是否配置有自定义destroy-method


找工作的时候有些人会被问道Spring中Bean的生命周期,其实也就是考察一下对Spring是否熟悉,工作中很少用到其中的内容,那我们简单看一下。
在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean也类似,如下
    1、实例化一个Bean--也就是我们常说的new;
    2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
    3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
    4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
    
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
    6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
    7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
    8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
    注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
    9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
    10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
以上10步骤可以作为面试或者笔试的模板,另外我们这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了。



3、Spring IOC容器BeanFactory、ApplicationContext:
  BeanFactory:
        实现BeanFactory接口的简单容器,具备最基本功能
        设计路径:
            BeanFactory -> HierarchicalBeanFactory -> ConfigurableBeanFactory,这是一个主要的BeanFactory设计路径
            BeanFactory:基本规范,比如说getBean()这样的方法
            HierarchicalBeanFactory:管理双亲Ioc容器规范,比如说getParentBeanFactory()这样的方法
            ConfigurableBeanFactory:定义BeanFactory的配置.ConfigurableBeanFactory中定义了太多太多的api,比如通过setParentBeanFactory()设置双亲Ioc容器,又比如类加载器、类型转化、属性编辑器、BeanPostProcessor、作用域、bean定义、处理bean依赖关系、合并其他ConfigurableBeanFactory、bean如何销毁。ConfigurableBeanFactory同时继承了HierarchicalBeanFactory 和 SingletonBeanRegistry 这两个接口,即同时继承了分层和单例类注册的功能。


    ApplicationContext:
        实现ApplicationContext接口的复杂容器,具备高级功能
        设计路径:
            BeanFactory -> ListableBeanFactory/HierarchicalBeanFactory -> ApplicationContext -> ConfigurableApplicationContext,这是一个主要的ApplicationContext设计路径
            ListableBeanFactory:细化了许多BeanFactory的功能,比如说getBeanDefinitionNames()
            ApplicationContext:通过继承EnvironmentCapable、MessageSource(国际化的消息访问 )、ResourceLoader、ApplicationEventPublisher接口,添加了许多高级特性
    BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean,而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean
    BeanFactory和ApplicationContext都支持BeanPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册

4、Spring扩展:
    Spring留给了我们大量的扩展接口供开发者去自定义自己的功能,甚至于AOP就是在Spring预留的扩展接口中实现的,意思是只要基于Spring IOC,遵守Spring对扩展接口的约定,那么就能实现自己想要的功能
        BeanFactoryPostProcessor接口
            Spring允许在Bean创建之前,读取Bean的元属性,并根据自己的需求对元属性进行改变,比如将Bean的scope从singleton改变为prototype
            BeanFactoryPostProcessor接口中只有postProcessBeanFactory方法。实现了该接口的类,可以在Bean被创建之前,获取容器中Bean的定义信息,并且可以进行修改。
            实现类中的postProcessBeanFactory方法只会被执行一次,且先于BeanPostProcessor接口的方法。
        InstantiationAwareBeanPostProcessor接口
            在bean实例化前后做一些操作
            InstantiationAwareBeanPostProcessor接口中,常用的方法是postProcessBeforeInstantiation和postProcessAfterInstantiation。每个Bean的实例化(即调用构造函数)之前和之后,会分别调用实现了该接口的类中的postProcessBeforeInstantiation和postProcessAfterInstantiation方法。
        BeanNameAware接口
            BeanNameAware中只有一个setBeanName方法,实现了BeanNameAware接口的类,可以在该Bean被加载的过程中获取当前Bean的id
        BeanFactoryAware接口
            BeanFactoryAware接口中只有一个setBeanFactory方法。实现了BeanFactoryAware接口的类,可以在该Bean被加载的过程中获取加载该Bean的BeanFactory,同时也可以获取这个BeanFactory中加载的其它Bean
        ApplicationContextAware接口
            ApplicationContextAware中只有一个setApplicationContext方法。实现了ApplicationContextAware接口的类,可以在该Bean被加载的过程中获取Spring的应用上下文ApplicationContext,通过ApplicationContext可以获取Spring容器内的很多信息
        BeanPostProcessor接口
            在每个bean初始化成前后做操作
            BeanPostProcessor接口中有两个方法,分别为postProcessBeforeInitialization和postProcessAfterInitialization。实现了BeanPostProcessor接口的类,会在每个Bean初始化(即调用setter)之前和之后,分别调用这个类中的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,实现初始化的逻辑控制。
        InitializingBean接口
            在属性设置完毕后做一些初始化操作
            InitializingBean接口中只有一个afterPropertiesSet方法,从方法的名称上很容易理解,这个方法是在Bean的属性都设置值后被调用,用于完成一些初始化工作
            当然,在Spring的配置文件中init-method的配置也是在Bean的属性都设置值后被调用,用于完成一些初始化工作,不过在执行顺序上,接口的方法先于配置
            值得注意的是,这两种方式都是用于完成一些初始化工作,所以相应的方法中不要编写一些复杂且执行时间很长的逻辑
        DisposableBean接口
            在关闭容器前做一些操作
            DisposableBean接口中只有一个destroy方法,该方法会在Bean被销毁、生命周期结束之前被调用,用于做一些销毁的收尾工作。同样,在Spring的配置文件中destroy-method配置也完成同样的工作,不过在执行顺序上,接口的方法先于配置
        FactoryBean接口 
            FactoryBean接口可以实现Bean实例化的个性定制,让Spring容器加载我们想要的Bean。实现了FactoryBean接口的类,可以通过实现getObject方法,实现加载我们想要的Bean


    Spring五个接口十个扩展点:
        BeanPostProcessor:实例化Bean后置处理器
            postProcessBeforeInitialization方法:Bean实例化、依赖注入完毕,初始化之前完成一些定制的任务
                如:BeanValidationPostProcessor完成@Valid注解Bean验证,InitDestroyAnnotationBeanPostProcessor完成@PostConstruct注解的初始化方法调用,ApplicationContextAwareProcessor完成一些Aware接口的注入(如EnvironmentAware、ResourceLoaderAware、ApplicationContextAware),其返回值将替代原始的Bean对象
            postProcessAfterInitialization方法:Bean实例化、依赖注入、初始化完毕时执行
                如:AspectJAwareAdvisorAutoProxyCreator完成xml风格的AOP配置的目标对象包装到AOP代理对象,AnnotationAwareAspectJAutoProxyCreator完成@Aspectj注解风格的AOP配置的目标对象包装到AOP代理对象,其返回值将替代原始的Bean对象
        InstantiationAwareBeanPostProcessor:实例化Bean后置处理器(继承BeanPostProcessor)
            postProcessBeforeInstantiation方法:在实例化目标对象之前执行,可以自定义实例化逻辑,如返回一个代理对象等,
            postProcessAfterInitialization方法:Bean实例化完毕后执行的后处理操作,所有初始化逻辑、装配逻辑之前执行
            postProcessPropertyValues方法:完成其他定制的一些依赖注入和依赖检查等
        SmartInstantiationAwareBeanPostProcessor:智能实例化Bean后置处理器(继承InstantiationAwareBeanPostProcessor)
            predictBeanType方法:预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过Bean定义无法得到Bean类型信息时就调用该回调方法来决定类型信息
            determineCandidateConstructors方法:检测Bean的构造器,可以检测出多个候选构造器,再有相应的策略决定使用哪一个,如AutowiredAnnotationBeanPostProcessor实现将自动扫描通过@Autowired/@Value注解的构造器从而可以完成构造器注入
            getEarlyBeanReference方法:处理循环依赖
        MergedBeanDefinitionPostProcessor:合并Bean定义后置处理器(继承BeanPostProcessor)
            postProcessMergedBeanDefinition方法:执行Bean定义的合并,在实例化完Bean之后执行
        DestructionAwareBeanPostProcessor:销毁Bean后置处理器(继承BeanPostProcessor)
            postProcessBeforeDestruction方法:销毁后处理回调方法,该回调只能应用到单例Bean,如InitDestroyAnnotationBeanPostProcessor完成@PreDestroy注解的销毁方法调用
    Spring内置的一些BeanPostProcessor:
        ApplicationContextAwareProcessor:容器启动时会自动注册,在postProcessBeforeInitialization回调方法中进行实施,注入那些实现ApplicationContextAware、MessageSourceAware、ResourceLoaderAware、EnvironmentAware、EmbeddedValueResolverAware、ApplicationEventPublisherAware标识接口的Bean需要的相应实例
        CommonAnnotationBeanPostProcessor:CommonAnnotationBeanPostProcessor继承InitDestroyAnnotationBeanPostProcessor,当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持
        AutowiredAnnotationBeanPostProcessor:当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对JSR-330规范注解的支持和Spring自带注解的支持@Autowired、@Value、@Inject
        RequiredAnnotationBeanPostProcessor:当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对@Required注解的方法进行依赖检查支持
        PersistenceAnnotationBeanPostProcessor:当在配置文件有<context:annotation-config>或<context:component-scan>会自动注册,提供对JPA @javax.persistence.PersistenceUnit和@javax.persistence.PersistenceContext注解进行依赖注入的支持
        AbstractAutoProxyCreator:AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator都是继承AbstractAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator提供对(<aop:config>)声明式AOP的支持,AnnotationAwareAspectJAutoProxyCreator提供对(<aop:aspectj-autoproxy>)注解式(@AspectJ)AOP的支持
        BeanValidationPostProcessor:提供对JSR-303验证规范支持
        MethodValidationPostProcessor:提供对方法参数/方法返回值的进行验证(即前置条件/后置条件的支持),通过JSR-303注解验证
        ScheduledAnnotationBeanPostProcessor:当配置文件中有<task:annotation-driven>自动注册或@EnableScheduling自动注册,提供对注解@Scheduled任务调度的支持
        AsyncAnnotationBeanPostProcessor:当配置文件中有<task:annotation-driven>自动注册或@EnableAsync自动注册,提供对@Async和EJB3.1的@javax.ejb.Asynchronous注解的异步调用支持
        ServletContextAwareProcessor:在使用Web容器时自动注册,类似于ApplicationContextAwareProcessor,当你的Bean实现了ServletContextAware/ServletConfigAware会自动调用回调方法注入ServletContext/ServletConfig
  

 扩展Spring的几种方式:
        1.基于XML配置的扩展
            1.1.定义schema:
                要支持XML的配置方式,首先需要定义一套XML Schema来描述组件所提供的功能,所以Schema中就需要描述我们期望用户提供的namespace以及namespace之间的排序等元数据

                <?xml version="1.0" encoding="UTF-8" standalone="no"?>
                <xsd:schema xmlns="http://www.ctrip.com/schema/apollo"
                    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                    targetNamespace="http://www.ctrip.com/schema/apollo"
                    elementFormDefault="qualified"
                    attributeFormDefault="unqualified">
                    <xsd:annotation>
                        <xsd:documentation><![CDATA[ Namespace support for Ctrip Apollo Configuration Center. ]]></xsd:documentation>
                    </xsd:annotation>
                    <xsd:element name="config">
                        <xsd:annotation>
                            <xsd:documentation>
                                <![CDATA[ Apollo configuration section to integrate with Spring.]]>
                            </xsd:documentation>
                        </xsd:annotation>
                        <xsd:complexType>
                            <xsd:attribute name="namespaces" type="xsd:string" use="optional">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        <![CDATA[
                                            The comma-separated list of namespace names to integrate with Spring property sources.
                                            If not specified, then default to application namespace.
                                        ]]>
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:attribute>
                            <xsd:attribute name="order" type="xsd:int" use="optional">
                                <xsd:annotation>
                                    <xsd:documentation>
                                        <![CDATA[
                                            The order of the config, default to Ordered.LOWEST_PRECEDENCE, which is Integer.MAX_VALUE.
                                            If there are properties with the same name in different apollo configs, the config with smaller order wins.
                                        ]]>
                                    </xsd:documentation>
                                </xsd:annotation>
                            </xsd:attribute>
                        </xsd:complexType>
                    </xsd:element>
                </xsd:schema>


            1.2.创建NamespaceHandler:
                除了XML Schema,我们还需要创建一个自定义的NamespaceHandler来负责解析用户在XML中的配置,继承NamespaceHandlerSupport,在init方法中注册处理我们自定义节点的BeanDefinitionParser

    public class MyNamespaceHandler extends NamespaceHandlerSupport {
        @Override
        public void init() {
            registerBeanDefinitionParser("config", new BeanParser());
        }
    }


            1.3.自定义BeanDefinitionParser:
                自定义的BeanDefinitionParser负责解析xml中的config节点信息,记录用户的配置信息,为后面和Spring整合做好铺垫
                记录用户配置的namespace和order
                向Spring注册Bean:ConfigPropertySourcesProcessor,这个bean后面会实际处理用户配置的namespace和order,从而完成配置注入到Spring中的功能

                public class BeanParser extends AbstractSingleBeanDefinitionParser {
                    @Override
                    protected Class<?> getBeanClass(Element element) {
                        return ConfigPropertySourcesProcessor.class;
                    }
                    @Override
                    protected boolean shouldGenerateId() {
                        return true;
                    }
                    @Override
                    protected void doParse(Element element, BeanDefinitionBuilder builder) {
                        String namespaces = element.getAttribute("namespaces");
                        // default to application
                        if (Strings.isNullOrEmpty(namespaces)) {
                            namespaces = ConfigConsts.NAMESPACE_APPLICATION;
                        }
                        int order = Ordered.LOWEST_PRECEDENCE;
                        String orderAttribute = element.getAttribute("order");

                        if (!Strings.isNullOrEmpty(orderAttribute)) {
                            try {
                                order = Integer.parseInt(orderAttribute);
                            } catch (Throwable ex) {
                                throw new IllegalArgumentException(String.format("Invalid order: %s for namespaces: %s", orderAttribute, namespaces));
                            }
                        }
                        PropertySourcesProcessor.addNamespaces(NAMESPACE_SPLITTER.splitToList(namespaces), order);
                    }
                }


            1.4.注册Spring handler和Spring schema:
                基于XML配置扩展Spring的主体代码基本就是上面这些,剩下的就是要让Spring解析xml配置文件的过程中识别我们的自定义节点,并且转交到我们的NamespaceHandler处理
                META-INF/spring.handlers
                    首先需要在META-INF目录下创建一个spring.handlers文件,来配置我们自定义的XML Schema Namespace到我们自定义的NamespaceHandler映射关系
                    如:http\://www.ctrip.com/schema/apollo=com.ctrip.framework.apollo.spring.config.NamespaceHandler
                META-INF/spring.schemas
                    我们还需要在META-INF目录下创建一个spring.schemas,来配置我们自定义的XML Schema地址到实际Jar包中的classpath映射关系(避免Spring真的去服务器上下载不存在的文件)。
                    http\://www.ctrip.com/schema/apollo-1.0.0.xsd=/META-INF/apollo-1.0.0.xsd
                    http\://www.ctrip.com/schema/apollo.xsd=/META-INF/apollo-1.0.0.xsd
            
            1.5.基于XML配置的使用样例如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:apollo="http://www.ctrip.com/schema/apollo"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.ctrip.com/schema/apollo http://www.ctrip.com/schema/apollo.xsd">
                    
           <apollo:config namespaces="application" order="1"/>
    </beans>


        2.基于Java配置的扩展
            通过这种方式,我们在开发Spring项目的过程中再也不需要去配置繁琐的xml文件了,只需要在Configuration类中配置就可以了,大大的简化了Spring的使用,这也是Spring Boot默认的配置方式,所以建议也支持这一特性
            @Import注解:
                支持Java配置扩展的关键点就是@Import注解,Spring 3.0提供了这个注解用来支持在Configuration类中引入其它的配置类,包括Configuration类,ImportSelector和ImportBeanDefinitionRegistrar的实现类,我们可以通过这个注解来引入自定义的扩展Bean
            自定义注解:
                和基于XML配置类似的,我们需要提供给用户一个注解来配置需要注入到Spring Property Sources的namespaces和order,下面就是Apollo提供的@EnableApolloConfig注解,允许用户传入namespaces和order信息

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(ApolloConfigRegistrar.class)
    public @interface EnableApolloConfig {
        String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
        int order() default Ordered.LOWEST_PRECEDENCE;
    }


                这里的关键点是在注解上使用了@Import(ApolloConfigRegistrar.class),从而Spring在处理@EnableApolloConfig时会实例化并调用ApolloConfigRegistrar的方法
            自定义ImportBeanDefinitionRegistrar实现:
                ImportBeanDefinitionRegistrar接口定义了registerBeanDefinitions方法,从而允许我们向Spring注册必要的Bean
                Apollo的自定义ImportBeanDefinitionRegistrar实现(ApolloConfigRegistrar)主要做了两件事情:1.记录用户配置的namespace和order,2.向Spring注册Bean:PropertySourcesProcessor,这个bean后面会实际处理用户配置的namespace和order,从而完成配置注入到Spring中的功能

    public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableApolloConfig.class.getName()));
        String[] namespaces = attributes.getStringArray("value");
        int order = attributes.getNumber("order");
        PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);
        BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(), PropertySourcesProcessor.class);
        }
    }


            基于Java配置的使用样例如下:

@Configuration
@EnableApolloConfig(value = "application", order = 1)
public class AppConfig {}


        3.Spring容器的扩展点
            前面两点简单介绍了扩展Spring的两种方式:基于XML和基于Java的配置,通过这两种方式,我们可以在运行时收集到用户的配置信息,同时向Spring注册实际处理这些配置信息的Bean
            但这些注册进去的Bean实际上是如何工作的?我们通过什么方式能使我们的程序逻辑和Spring容器紧密合作,并无缝插入到用户bean的生命周期中?
            这里简单介绍Spring容器最常用的两个扩展点:BeanFactoryPostProcessor和BeanPostProcessor
            扩展BeanFactoryPostProcessor:
                BeanFactoryPostProcessor提供了一个方法postProcessBeanFactory,这个方法会被Spring在容器初始化过程中调用,调用时机是所有bean的定义信息都已经初始化好,但是这些bean还没有实例化
                Apollo就利用这个时间点把配置信息注入到Spring Property Sources中,从而用户的bean在真正实例化时,所有需要的配置信息已经准备好了

public class PropertySourcesProcessor implements BeanFactoryPostProcessor {
    private static final AtomicBoolean PROPERTY_SOURCES_INITIALIZED = new AtomicBoolean(false);
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (!PROPERTY_SOURCES_INITIALIZED.compareAndSet(false, true)) {
            // already initialized
            return;
        }
        // initialize and inject Apollo config to Spring Property Sources
        initializePropertySources();
    }
}


            扩展BeanPostProcessor:
                BeanPostProcessor提供了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,主要针对bean初始化提供扩展
                postProcessBeforeInitialization会在每一个bean实例化之后、初始化(如afterPropertiesSet方法)之前被调用,postProcessAfterInitialization则在每一个bean初始化之后被调用
                我们常用的@Autowired注解就是通过(AutowiredAnnotationBeanPostProcessor)postProcessBeforeInitialization实现的
                Apollo提供了@ApolloConfig注解来实现实例化时注入Config对象实例,所以实现逻辑和@Autowired类似
                    public class ApolloAnnotationProcessor implements BeanPostProcessor {
                        @Override
                        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                            Class clazz = bean.getClass();
                            processFields(bean, clazz.getDeclaredFields());
                            return bean;
                        }
                        private void processFields(Object bean, Field[] declaredFields) {
                            for(Field field : declaredFields) {
                                ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);
                                if (annotation == null) {
                                    continue;
                                }
                                Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()), "Invalid type: %s for field: %s, should be Config", field.getType(), field);
                                String namespace = annotation.value();
                                Config config = ConfigService.getConfig(namespace);
                                ReflectionUtils.makeAccessible(field);
                                ReflectionUtils.setField(field, bean, config);
                            }
                        }
                        @Override
                        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                            return bean;
                        }
                    }
                通过上面的代码就会发现Apollo在用户bean初始化前会根据@ApolloConfig的配置注入对应namespace的Config实例5、Filter、Interceptor、Aspect区别:
    Filter:
        Filter使用户可以改变一个request和修改一个response,Filter不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,也可以在离开servlet时处理response,filter其实是一个”servlet chaining”(servlet 链)
        从doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)方法的参数可以看出,我们只能得到原始的request和response对象,不能得到这个请求被哪个Controller以及哪个方法处理了
        自定义Filter需要两个步骤:实现Filter[javax.servlet.Filter]接口,实现doFilter方法;添加@Configuration注解,将自定义Filter加入过滤链
            web.xml配置方式:
                <filter>
                    <filter-name>TestFilter</filter-name>
                    <filter-class>com.cppba.filter.TestFilter</filter-class>
                </filter>
                <filter-mapping>
                    <filter-name>TestFilter</filter-name>
                    <url-pattern>/*</url-pattern>
                    <init-param>
                        <param-name>paramName</param-name>
                        <param-value>paramValue</param-value>
                    </init-param>
                </filter-mapping>
        
            @Configuration中增加一个@bean:
            @Bean
            public FilterRegistrationBean testFilterRegistration() {
                FilterRegistrationBean registration = new FilterRegistrationBean();
                registration.setFilter(new TestFilter());
                registration.addUrlPatterns("/*");
                registration.addInitParameter("paramName", "paramValue");
                registration.setName("testFilter");
                registration.setOrder(1);
                return registration;
            }
             
            Application启动类中增加@ServletComponentScan:
            @Component
            @Order(1)
            @WebFilter(filterName = "testFilter1", urlPatterns = "/*")
            public class TestFilter implements Filter {
                @Override
                public void init(FilterConfig filterConfig) throws ServletException {
             
                }
                @Override
                public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {
                    System.out.println("TestFilter1");
                    filterChain.doFilter(servletRequest,servletResponse);
                }
                @Override
                public void destroy() {
     
                }
            }
    Interceptor:
        自定义Interceptor需要实现HandlerInterceptor接口、或者继承HandlerInterceptorAdapter类,重写preHandle、postHandle、afterCompletion方法
        preHandle在业务处理器处理请求之前被调用,postHandle在业务处理器处理请求执行完成后生成视图之前执行,afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 
        注册自定义Interceptor,把自定义Interceptor注入spring容器,实现WebMvcConfigurer或继承WebMvcConfigurerAdapter,重写addInterceptors方法,将自定义Interceptor进行注册

            @Component
            public class TimeInterceptor extends HandlerInterceptorAdapter {
                private final NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("startTimeThreadLocal");
                @Override
                public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                    System.out.println("time interceptor preHandle");
                    HandlerMethod handlerMethod = (HandlerMethod) handler;
                    // 获取处理当前请求的 handler 信息
                    System.out.println("handler 类:" + handlerMethod.getBeanType().getName());
                    System.out.println("handler 方法:" + handlerMethod.getMethod().getName());
                    MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
                    for (MethodParameter methodParameter : methodParameters) {
                        String parameterName = methodParameter.getParameterName();
                        // 只能获取参数的名称,不能获取到参数的值
                        System.out.println("parameterName: " + parameterName);
                    }
                    // 把当前时间放入 threadLocal
                    startTimeThreadLocal.set(System.currentTimeMillis());

                    return true;
                }
                @Override
                public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                    System.out.println("time interceptor postHandle");
                }
                @Override
                public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                    // 从 threadLocal 取出刚才存入的 startTime
                    Long startTime = startTimeThreadLocal.get();
                    long endTime = System.currentTimeMillis();

                    System.out.println("time interceptor consume " + (endTime - startTime) + " ms");                    System.out.println("time interceptor afterCompletion");
                }
            }
            
            @Configuration
            public class WebConfig extends WebMvcConfigurerAdapter {
                @Autowired
                private TimeInterceptor timeInterceptor;
                @Override
                public void addInterceptors(InterceptorRegistry registry) {
                    registry.addInterceptor(timeInterceptor);
                }
            }


        通过Interceptor中方法上的handler参数,我们就可以得到这个请求被哪个Controller以及哪个方法处理了,但是不能直接获取到这个方法上的参数值
    Aspcet:
        Aspcet不需要注册,只需要申明切面、定义切入点

        @Aspect
        @Component
        public class TimeAspect {
            //定义切入点
            @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) " +
                    "|| @annotation(org.springframework.web.bind.annotation.PostMapping) " +
                    "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
                    "|| @annotation(org.springframework.web.bind.annotation.PutMapping)" +
                    "|| @annotation(org.springframework.web.bind.annotation.PatchMapping)" +
                    "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
            public void logAspect() {}
            
            @Around("logAspect()")
            public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {

                System.out.println("time aspect start");
                Object[] args = pjp.getArgs();
                for (Object arg : args) {
                    System.out.println("arg is " + arg);
                }
                long startTime = System.currentTimeMillis();
                Object object = pjp.proceed();
                long endTime = System.currentTimeMillis();
                System.out.println("time aspect consume " + (endTime - startTime) + " ms");
                System.out.println("time aspect end");
                return object;
            }
        }


    总结:
        Filter:和框架无关,是java web里面的,可以控制最初的http请求,但是更细一点的类和方法控制不了。
        Interceptor:可以控制请求的控制器和方法,但控制不了请求方法里的参数。 
        Aspect : 可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得
        Filter->Interceptor->ControllerAdvice->Aspect->Controller

6、代理创建器
    三种代理创建器(BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator)
    BeanNameAutoProxyCreator是三种自动代理创建器之一,它是根据拦截器和设置的Bean的名称表达式做匹配来创建代理,下面是个例子

    public class MyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println(getClass() + "调用方法前");
            Object ret=invocation.proceed();
            System.out.println(getClass() + "调用方法后");
            return ret;
        }
    }
    public interface UserService {
        void print();
    }
    public class UserServiceImpl implements UserService {
        public void print(){
            System.out.println(getClass() + "#print");
        }
    }
    @Configuration
    public class AppConfig {
        // 要创建代理的目标Bean
        @Bean
        public UserService userService(){
            return new UserServiceImpl();
        }
        // 创建Advice或Advisor
        @Bean
        public Advice myMethodInterceptor(){
            return new MyMethodInterceptor();
        }
        // 使用BeanNameAutoProxyCreator来创建代理
        @Bean
        public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
            BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
            // 设置要创建代理的那些Bean的名字
            beanNameAutoProxyCreator.setBeanNames("userSer*");
            // 设置拦截链名字(这些拦截器是有先后顺序的)
            beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor");
            return beanNameAutoProxyCreator;
        }
    }
    public class Main {
        public static void main(String[] args) {
            ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
            UserService userService= applicationContext.getBean(UserService.class);
            userService.print();
        }
    }

xml方式:

    <bean id="log4jdbcInterceptor" class="net.sf.log4jdbc.DataSourceSpyInterceptor" />
    <bean id="dataSourceLog4jdbcAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="interceptorNames">
            <list>
                <value>log4jdbcInterceptor</value>
            </list>
        </property>
        <property name="beanNames">
            <list>
                <value>dataSource</value>
            </list>
        </property>
    </bean>

7、Spring5.0
    基于Java 8,支持Java 9
    响应式编程,使用Spring WebFlux提供响应式Web编程支持,Webflux是一个全新的非堵塞的函数式Reactive Web框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好
    HTTP/2支持
    WebFlux简介:
        WebFlux模块中包含了对反应式 HTTP、服务器推送事件和WebSocket的客户端和服务器端的支持,在服务器端,WebFlux支持两种不同的编程模型:第一种是Spring MVC中使用的基于Java注解的方式,第二种是基于Java 8的lambda表达式的函数式编程模型。
        这两种编程模型只是在代码编写方式上存在不同。它们运行在同样的反应式底层架构之上,因此在运行时是相同的。
        WebFlux需要底层提供运行时的支持,WebFlux可以运行在支持Servlet 3.1非阻塞IO API的Servlet容器上,或是其他异步运行时环境,如 Netty 和 Undertow
        Java注解编程模型:
            @RestController
            public class BasicController {
                @GetMapping("/hello_world")
                public Mono<String> sayHelloWorld() {
                    return Mono.just("Hello World");
                }
            }
            WebFlux所使用的类型是与反应式编程相关的Flux和Mono等,而不是简单的对象,对于简单的Hello World示例来说,这两者之间并没有什么太大的差别,对于复杂的应用来说,反应式编程和负压的优势会体现出来,可以带来整体的性能的提升
        REST API:
            @Service
            class UserService {
                private final Map<String, User> data = new ConcurrentHashMap<>();
                Flux<User> list() {
                    return Flux.fromIterable(this.data.values());
                }
                Flux<User> getById(final Flux<String> ids) {
                    return ids.flatMap(id -> Mono.justOrEmpty(this.data.get(id)));
                }
                Mono<User> getById(final String id) {
                    return Mono.justOrEmpty(this.data.get(id))
                            .switchIfEmpty(Mono.error(new ResourceNotFoundException()));
                }
                Mono<User> createOrUpdate(final User user) {
                    this.data.put(user.getId(), user);
                    return Mono.just(user);
                }
                Mono<User> delete(final String id) {
                    return Mono.justOrEmpty(this.data.remove(id));
                }
            }
            @RestController
            @RequestMapping("/user")
            public class UserController {
                private final UserService userService;
                @Autowired
                public UserController(final UserService userService) {
                    this.userService = userService;
                }
                @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found")
                @ExceptionHandler(ResourceNotFoundException.class)
                public void notFound() {
                }
                @GetMapping("")
                public Flux<User> list() {
                    return this.userService.list();
                }
                @GetMapping("/{id}")
                public Mono<User>getById(@PathVariable("id") final String id) {
                    return this.userService.getById(id);
                }
                @PostMapping("")
                public Mono<User> create(@RequestBody final User user) {
                    return this.userService.createOrUpdate(user);
                }
                @PutMapping("/{id}")
                public Mono<User>  update(@PathVariable("id") final String id, @RequestBody final User user) {
                    Objects.requireNonNull(user);
                    user.setId(id);
                    return this.userService.createOrUpdate(user);
                }

                @DeleteMapping("/{id}")
                public Mono<User>  delete(@PathVariable("id") final String id) {
                    return this.userService.delete(id);
                }
            }
        服务器推送事件:
        函数式编程模型:

8、BeanFactory和FactoryBean:
    BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean,在Spring中所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的,但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似 
    BeanFactory定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,也就是Spring IOC所遵守的最底层和最基本的编程规范,在Spring代码中BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等
    一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案
    Spring为此提供了一个FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑,FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现,它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利
    一个Bean想要实现FactoryBean,必须实现以下三个接口:
        Object getObject()        返回由FactoryBean创建的Bean的实例
        boolean isSingleton()    确定由FactoryBean创建的Bean的作用域是singleton还是prototype
        getObjectType()        返回FactoryBean创建的Bean的类型
    有一点需要注意,如果将一个实现了FactoryBean的类成功配置到了spring上下文中,那么通过该类对象的名称从spring的applicationContext或者beanFactory获取bean时,获取到的是FactoryBean创建的bean实例,而不是FactoryBean自己,如果想通过spring拿到FactoryBean,需要在名称前加&符号,如:applicationContext.getBean("&appleFactoryBean")
    还有一点需要注意,FactoryBean管理的bean实际上也是由spring进行配置、实例化、管理,因此由FactoryBean管理的bean不能再次配置到spring配置文件中(xml、java类配置、注解均不可以),否则会报异常,如:
        //@Component  这里不可以加注解 !!!!!!
        public class AppleBean{
        }
        
        @Component
        public class AppleFactoryBean implements FactoryBean{
            public Object getObject() throws Exception {
                return new AppleBean();
            }
            public Class<?> getObjectType() {
                return AppleBean.class;
            }
            public boolean isSingleton() {
                return false;
            }
        }
    
9、定制自己的异常页面:
    定义一个MyErrorPageRegistrar实现ErrorPageRegistrar接口:

@Component
public class MyErrorPageRegistrar implements ErrorPageRegistrar {
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        //具体的错误码错误异常页面
        ErrorPage e404 = new ErrorPage(HttpStatus.NOT_FOUND,"/404.html");
        ErrorPage e500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,"/500.html");
        //指定具体异常的错误定制页面
        ErrorPage argspage = new ErrorPage(IllegalArgumentException.class,"/argsException.html");
        registry.addErrorPages(e404,e500,argspage);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值