spring容器的refresh方法分析


spring源碼版本5.0.5

Spring容器創建之后,會調用它的refresh方法刷新Spring應用的上下文。

首先整體查看AbstractApplicationContext#refresh源碼

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //刷新前的預處理;
            prepareRefresh();

            //獲取BeanFactory;默認實現是DefaultListableBeanFactory,在創建容器的時候創建的
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            //BeanFactory的預准備工作(BeanFactory進行一些設置,比如context的類加載器,BeanPostProcessor和XXXAware自動裝配等)
            prepareBeanFactory(beanFactory);

            try {
                //BeanFactory准備工作完成后進行的后置處理工作
                postProcessBeanFactory(beanFactory);

                //執行BeanFactoryPostProcessor的方法;
                invokeBeanFactoryPostProcessors(beanFactory);

                //注冊BeanPostProcessor(Bean的后置處理器),在創建bean的前后等執行
                registerBeanPostProcessors(beanFactory);

                //初始化MessageSource組件(做國際化功能;消息綁定,消息解析);
                initMessageSource();

                //初始化事件派發器
                initApplicationEventMulticaster();

                //子類重寫這個方法,在容器刷新的時候可以自定義邏輯;如創建Tomcat,Jetty等WEB服務器
                onRefresh();

                //注冊應用的監聽器。就是注冊實現了ApplicationListener接口的監聽器bean,這些監聽器是注冊到ApplicationEventMulticaster中的
                registerListeners();

                //初始化所有剩下的非懶加載的單例bean
                finishBeanFactoryInitialization(beanFactory);

                //完成context的刷新。主要是調用LifecycleProcessor的onRefresh()方法,並且發布事件(ContextRefreshedEvent)
                finishRefresh();
            }

            ......
    }

prepareRefresh方法

表示在真正做refresh操作之前需要准備做的事情:

  • 設置Spring容器的啟動時間,
  • 開啟活躍狀態,撤銷關閉狀態,。
  • 初始化context environment(上下文環境)中的占位符屬性來源。
  • 驗證環境信息里一些必須存在的屬性

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

讓這個類(AbstractApplicationContext)的子類刷新內部bean工廠。

  • AbstractRefreshableApplicationContext容器:實際上就是重新創建一個bean工廠,並設置工廠的一些屬性。
  • GenericApplicationContext容器:獲取創建容器的就創建的bean工廠,並且設置工廠的ID.

prepareBeanFactory方法

上一步已經把工廠建好了,但是還不能投入使用,因為工廠里什么都沒有,還需要配置一些東西。看看這個方法的注釋

    /**
     * Configure the factory's standard context characteristics,
     * such as the context's ClassLoader and post-processors.
     * @param beanFactory the BeanFactory to configure
     */

他說配置這個工廠的標准環境,比如context的類加載器和post-processors后處理器。

    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //設置BeanFactory的類加載器
        beanFactory.setBeanClassLoader(getClassLoader());
        //設置支持表達式解析器
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        //添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        //設置忽略的自動裝配的接口EnvironmentAware、EmbeddedValueResolverAware、xx,因為ApplicationContextAwareProcessor#invokeAwareInterfaces已經把這5個接口的實現工作做了
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

        //注冊可以解析的自動裝配;我們能直接在任何組件中自動注入:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
        //其他組件中可以通過 @autowired 直接注冊使用
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        //添加BeanPostProcessor【ApplicationListenerDetector】后置處理器,在bean初始化前后的一些工作
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        //給BeanFactory中注冊一些能用的組件;
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            //環境信息ConfigurableEnvironment
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
          //系統屬性,systemProperties【Map<String, Object>】
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
          //系統環境變量systemEnvironment【Map<String, Object>】
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

 postProcessBeanFactory方法

上面對bean工廠進行了許多配置,現在需要對bean工廠進行一些處理。不同的Spring容器做不同的操作。比如GenericWebApplicationContext容器的操作會在BeanFactory中添加ServletContextAwareProcessor用於處理ServletContextAware類型的bean初始化的時候調用setServletContext或者setServletConfig方法(跟ApplicationContextAwareProcessor原理一樣)。

GenericWebApplicationContext#postProcessBeanFactory源碼:

    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        if (this.servletContext != null) {
            beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
            beanFactory.ignoreDependencyInterface(ServletContextAware.class);
        }
        WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext);
    }

AnnotationConfigServletWebServerApplicationContext#postProcessBeanFactory方法

    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.postProcessBeanFactory(beanFactory);
        // 查看basePackages屬性,如果設置了會使用ClassPathBeanDefinitionScanner去掃描basePackages包下的bean並注冊
        if (this.basePackages != null && this.basePackages.length > 0) {
            this.scanner.scan(this.basePackages);
        }
        // 查看annotatedClasses屬性,如果設置了會使用AnnotatedBeanDefinitionReader去注冊這些bean
        if (!this.annotatedClasses.isEmpty()) {
            this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
        }
    }

invokeBeanFactoryPostProcessors方法

先介紹兩個接口:

  • BeanFactoryPostProcessor:用來修改Spring容器中已經存在的bean的定義,使用ConfigurableListableBeanFactory對bean進行處理
  • BeanDefinitionRegistryPostProcessor:繼承BeanFactoryPostProcessor,作用跟BeanFactoryPostProcessor一樣,只不過是使用BeanDefinitionRegistry對bean進行處理

在Spring容器中找出實現了BeanFactoryPostProcessor接口的processor並執行。Spring容器會委托給PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法執行。


1.在springboot的web程序初始化AnnotationConfigServletWebServerApplicationContext容器時,會初始化內部屬性AnnotatedBeanDefinitionReader reader,這個reader構造的時候會在BeanFactory中注冊一些post processor,包括BeanPostProcessor和BeanFactoryPostProcessor(比如ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor):

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

2.在使用mybatis時,一般配置了MapperScannerConfigurer的bean,這個bean就是繼承的BeanDefinitionRegistryPostProcessor,所以也是這個地方把掃描的mybatis的接口注冊到容器中的。

invokeBeanFactoryPostProcessors方法處理BeanFactoryPostProcessor的邏輯如下:

從Spring容器中找出BeanDefinitionRegistryPostProcessor類型的bean(這些processor是在容器剛創建的時候通過構造AnnotatedBeanDefinitionReader的時候注冊到容器中的),然后按照優先級分別執行,優先級的邏輯如下:

  1. 實現PriorityOrdered接口的BeanDefinitionRegistryPostProcessor先全部找出來,然后排序后依次執行
  2. 實現Ordered接口的BeanDefinitionRegistryPostProcessor找出來,然后排序后依次執行
  3. 沒有實現PriorityOrdered和Ordered接口的BeanDefinitionRegistryPostProcessor找出來執行並依次執行

    接下來從Spring容器內查找BeanFactoryPostProcessor接口的實現類,然后執行(如果processor已經執行過,則忽略),這里的查找規則跟上面查找BeanDefinitionRegistryPostProcessor一樣,先找PriorityOrdered,然后是Ordered,最后是兩者都沒。

        這里需要說明的是ConfigurationClassPostProcessor這個processor是優先級最高的被執行的processor(實現了PriorityOrdered接口)。這個ConfigurationClassPostProcessor會去BeanFactory中找出所有有@Configuration注解的bean,然后使用ConfigurationClassParser去解析這個類。ConfigurationClassParser內部有個Map<ConfigurationClass, ConfigurationClass>類型的configurationClasses屬性用於保存解析的類,ConfigurationClass是一個對要解析的配置類的封裝,內部存儲了配置類的注解信息、被@Bean注解修飾的方法、@ImportResource注解修飾的信息、ImportBeanDefinitionRegistrar等都存儲在這個封裝類中。

        這里ConfigurationClassPostProcessor最先被處理還有另外一個原因是如果程序中有自定義的BeanFactoryPostProcessor,那么這個PostProcessor首先得通過ConfigurationClassPostProcessor被解析出來,然后才能被Spring容器找到並執行。(ConfigurationClassPostProcessor不先執行的話,這個Processor是不會被解析的,不會被解析的話也就不會執行了)。

  1. 處理@PropertySources注解:進行一些配置信息的解析
  2. 處理@ComponentScan注解:使用ComponentScanAnnotationParser掃描basePackage下的需要解析的類(@SpringBootApplication注解也包括了@ComponentScan注解,只不過basePackages是空的,空的話會去獲取當前@Configuration修飾的類所在的包),並注冊到BeanFactory中(這個時候bean並沒有進行實例化,而是進行了注冊。具體的實例化在finishBeanFactoryInitialization方法中執行)。對於掃描出來的類,遞歸解析
  3. 處理@Import注解:先遞歸找出所有的注解,然后再過濾出只有@Import注解的類,得到@Import注解的值。比如查找@SpringBootApplication注解的@Import注解數據的話,首先發現@SpringBootApplication不是一個@Import注解,然后遞歸調用修飾了@SpringBootApplication的注解,發現有個@EnableAutoConfiguration注解,再次遞歸發現被@Import(EnableAutoConfigurationImportSelector.class)修飾,還有@AutoConfigurationPackage注解修飾,再次遞歸@AutoConfigurationPackage注解,發現被@Import(AutoConfigurationPackages.Registrar.class)注解修飾,所以@SpringBootApplication注解對應的@Import注解有2個,分別是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)。找出所有的@Import注解之后,開始處理邏輯:
    1. 遍歷這些@Import注解內部的屬性類集合
    2. 如果這個類是個ImportSelector接口的實現類,實例化這個ImportSelector,如果這個類也是DeferredImportSelector接口的實現類,那么加入ConfigurationClassParser的deferredImportSelectors屬性中讓第6步處理。否則調用ImportSelector的selectImports方法得到需要Import的類,然后對這些類遞歸做@Import注解的處理
    3. 如果這個類是ImportBeanDefinitionRegistrar接口的實現類,設置到配置類的importBeanDefinitionRegistrars屬性中
    4. 其它情況下把這個類入隊到ConfigurationClassParser的importStack(隊列)屬性中,然后把這個類當成是@Configuration注解修飾的類遞歸重頭開始解析這個類
  4. 處理@ImportResource注解:獲取@ImportResource注解的locations屬性,得到資源文件的地址信息。然后遍歷這些資源文件並把它們添加到配置類的importedResources屬性中
  5. 處理@Bean注解:獲取被@Bean注解修飾的方法,然后添加到配置類的beanMethods屬性中
  6. 處理DeferredImportSelector:處理第3步@Import注解產生的DeferredImportSelector,進行selectImports方法的調用找出需要import的類,然后再調用第3步相同的處理邏輯處理

        這里@SpringBootApplication注解被@EnableAutoConfiguration修飾,@EnableAutoConfiguration注解被@Import(EnableAutoConfigurationImportSelector.class)修飾,所以在第3步會找出這個@Import修飾的類EnableAutoConfigurationImportSelector,這個類剛好實現了DeferredImportSelector接口,接着就會在第6步被執行。第6步selectImport得到的類就是自動化配置類。

        EnableAutoConfigurationImportSelector的selectImport方法會在spring-boot-autoconfigure包的META-INF里面的spring.factories文件中找出key為org.springframework.boot.autoconfigure.EnableAutoConfiguration對應的值,有109個,這109個就是所謂的自動化配置類(XXXAutoConfiguration)。(如果引入了mybatis和pagehelper,也會在對應的XXXautoconfigure包的META-INF里面的spring.factories找到EnableAutoConfiguration,這樣可能最后得到的自動配置類會大於109個。)然后在過濾排除一下不需要的配置,最后返回實際用到的。

 

        ConfigurationClassParser解析完成之后,被解析出來的類會放到configurationClasses屬性中。然后使用ConfigurationClassBeanDefinitionReader去解析這些類。

這個時候這些bean只是被加載到了Spring容器中。下面這段代碼是ConfigurationClassBeanDefinitionReader的解析bean過程:這個時候這些bean只是被加載到了Spring容器中。下面這段代碼是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions的解析bean過程:

    public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
        TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
        for (ConfigurationClass configClass : configurationModel) {
            //對每一個配置類,調用loadBeanDefinitionsForConfigurationClass方法
            loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
        }
    }
    private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
            TrackedConditionEvaluator trackedConditionEvaluator) {
        //使用條件注解判斷是否需要跳過這個配置類
        if (trackedConditionEvaluator.shouldSkip(configClass)) {
            //跳過配置類的話在Spring容器中移除bean的注冊
            String beanName = configClass.getBeanName();
            if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
                this.registry.removeBeanDefinition(beanName);
            }
            this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
            return;
        }

        if (configClass.isImported()) {
            //如果自身是被@Import注釋所import的,注冊自己
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
        //注冊方法中被@Bean注解修飾的bean
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }
        //注冊@ImportResource注解注釋的資源文件中的bean
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        //注冊@Import注解中的ImportBeanDefinitionRegistrar接口的registerBeanDefinitions
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }

        invokeBeanFactoryPostProcessors方法總結來說就是從Spring容器中找出BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor接口的實現類並按照一定的規則順序進行執行。 其中ConfigurationClassPostProcessor這個BeanDefinitionRegistryPostProcessor優先級最高,它會對項目中的@Configuration注解修飾的類(@Component、@ComponentScan、@Import、@ImportResource修飾的類也會被處理)進行解析,解析完成之后把這些bean注冊到BeanFactory中。需要注意的是這個時候注冊進來的bean還沒有實例化。

下面這圖就是對ConfigurationClassPostProcessor后置器的總結:

 

 registerBeanPostProcessors方法

        從Spring容器中找出的BeanPostProcessor接口的bean,並設置到BeanFactory的屬性中。之后bean被實例化的時候會調用這個BeanPostProcessor。

        該方法委托給了PostProcessorRegistrationDelegate類的registerBeanPostProcessors方法執行。這里的過程跟invokeBeanFactoryPostProcessors類似:

  1. 先找出實現了PriorityOrdered接口的BeanPostProcessor並排序后加到BeanFactory的BeanPostProcessor集合中
  2. 找出實現了Ordered接口的BeanPostProcessor並排序后加到BeanFactory的BeanPostProcessor集合中
  3. 沒有實現PriorityOrdered和Ordered接口的BeanPostProcessor加到BeanFactory的BeanPostProcessor集合中

        這些已經存在的BeanPostProcessor在postProcessBeanFactory方法中已經說明,都是由AnnotationConfigUtils的registerAnnotationConfigProcessors方法注冊的。這些BeanPostProcessor包括有AutowiredAnnotationBeanPostProcessor(處理被@Autowired注解修飾的bean並注入)、RequiredAnnotationBeanPostProcessor(處理被@Required注解修飾的方法)、CommonAnnotationBeanPostProcessor(處理@PreDestroy、@PostConstruct、@Resource等多個注解的作用)等。

        如果是自定義的BeanPostProcessor,已經被ConfigurationClassPostProcessor注冊到容器內。

        這些BeanPostProcessor會在這個方法內被實例化(通過調用BeanFactory的getBean方法,如果沒有找到實例化的類,就會去實例化)。

initMessageSource方法

初始化MessageSource組件(做國際化功能;消息綁定,消息解析),這個接口提供了消息處理功能。主要用於國際化/i18n。

initApplicationEventMulticaster方法

在Spring容器中初始化事件廣播器,事件廣播器用於事件的發布。

程序首先會檢查bean工廠中是否有bean的名字和這個常量(applicationEventMulticaster)相同的,如果沒有則說明沒有那么就使用默認的ApplicationEventMulticaster 的實現:SimpleApplicationEventMulticaster

onRefresh方法

一個模板方法,不同的Spring容器做不同的事情。

比如web程序的容器ServletWebServerApplicationContext中會調用createWebServer方法去創建內置的Servlet容器。

目前SpringBoot只支持3種內置的Servlet容器:

  • Tomcat
  • Jetty
  • Undertow

registerListeners方法

        注冊應用的監聽器。就是注冊實現了ApplicationListener接口的監聽器bean,這些監聽器是注冊到ApplicationEventMulticaster中的。這不會影響到其它監聽器bean。在注冊完以后,還會將其前期的事件發布給相匹配的監聽器。

    protected void registerListeners() {
        //1、從容器中拿到所有已經創建的ApplicationListener
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            //2、將每個監聽器添加到事件派發器中;
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        // 1.獲取所有還沒有創建的ApplicationListener
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            //2、將每個監聽器添加到事件派發器中;
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // earlyApplicationEvents 中保存之前的事件,
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                //3、派發之前步驟產生的事件;
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

finishBeanFactoryInitialization方法

實例化BeanFactory中已經被注冊但是未實例化的所有實例(懶加載的不需要實例化)。

比如invokeBeanFactoryPostProcessors方法中根據各種注解解析出來的類,在這個時候都會被初始化。

實例化的過程各種BeanPostProcessor開始起作用。

后面在詳細分析此步驟

finishRefresh方法

refresh做完之后需要做的其他事情。

  • 初始化生命周期處理器,並設置到Spring容器中(LifecycleProcessor)
  • 調用生命周期處理器的onRefresh方法,這個方法會找出Spring容器中實現了SmartLifecycle接口的類並進行start方法的調用
  • 發布ContextRefreshedEvent事件告知對應的ApplicationListener進行響應的操作

如果是web容器ServletWebServerApplicationContext還會啟動web服務和發布消息

    protected void finishRefresh() {
        super.finishRefresh();
        WebServer webServer = startWebServer();
        if (webServer != null) {
            publishEvent(new ServletWebServerInitializedEvent(webServer, this));
        }
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM