天天看點

SOFABoot源碼解析之啟動原理(2)-源碼解析

        以SOFABoot自帶的RPC案例sofaboot-sample-with-rpc作為SOFABoot應用(其實也是SpringBoot應用)啟動源碼分析的案例。

        在此提前說明,源碼分析主要分析主流程,以及本人認為比較重要的一些内容,對于其它部分,大家可以基于本文檔,自行研讀。

        在該案例中,采用WEB環境下使用的Spring應用上下文環境org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext。

        在SofaBootRpcDemoApplication的main方法中,調用SpringApplication的靜态方法(S   ofaBootRpcDemoApplication.class, args),啟動應用: 

1.  publicstatic ConfigurableApplicationContext run(Object[] sources, String[] args) {

2.       return new SpringApplication(sources).run(args);

3.  }

一、  建立SpringApplication執行個體

        在run方法中,首先建立SpringApplication執行個體: 

1.  publicSpringApplication(Object... sources) {

2.       initialize(sources);

3.  }

        在SpringApplication構造函數中,調用initialize方法進行SpringApplication執行個體初始化工作: 

1.  privatevoid initialize(Object[] sources) {

2.       if(sources != null && sources.length > 0) {

3.           this.sources.addAll(Arrays.asList(sources));

4.       }

5.       this.webEnvironment= deduceWebEnvironment();

6.       setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

8.       setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));

9.       this.mainApplicationClass= deduceMainApplicationClass();

10. }

        在SpringApplication執行個體初始化的時候,主要做以下幾件事情:

        1.   根據classpath裡面是否存在某個特征類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應該建立一個為Web應用使用的ApplicationContext類型;

        2.   使用SpringFactoriesLoader在應用的classpath中查找并加載所有可用的ApplicationContextInitializer;

        3.   使用SpringFactoriesLoader在應用的classpath中查找并加載所有可用的ApplicationListener;

        4.   推斷并設定main方法的定義類;

二、  啟動SOFABoot應用

        SpringApplication執行個體初始化完成後,開始執行run方法,啟動SOFABoot應用。 

1.  public ConfigurableApplicationContextrun(String... args) {

2.       StopWatchstopWatch = new StopWatch();

3.       stopWatch.start();

4.       ConfigurableApplicationContextcontext = null;

5.       FailureAnalyzersanalyzers = null;

6.       configureHeadlessProperty();

7.       SpringApplicationRunListenerslisteners = getRunListeners(args);

8.       listeners.started();

9.       try{

10.          ApplicationArgumentsapplicationArguments = new DefaultApplicationArguments(args);

12.          ConfigurableEnvironmentenvironment = prepareEnvironment(listeners,

13.                  applicationArguments);

14.          BannerprintedBanner = printBanner(environment);

15.          context = createApplicationContext();

16.          analyzers= new FailureAnalyzers(context);

17.          prepareContext(context, environment,listeners, applicationArguments,

18.                  printedBanner);

19.          refreshContext(context);

20.          afterRefresh(context,applicationArguments);

21.          listeners.finished(context,null);

22.          stopWatch.stop();

23.          if(this.logStartupInfo) {

24.              newStartupInfoLogger(this.mainApplicationClass)

25.                      .logStarted(getApplicationLog(),stopWatch);

26.          }

27.          returncontext;

28.      }

29.      catch(Throwable ex) {

30.          handleRunFailure(context,listeners, analyzers, ex);

31.          throw new IllegalStateException(ex);

32.      }

33. }

        啟動SOFABoot應用的主要工作就是建立和重新整理Spring應用上下文。此處采用AnnotationConfigEmbeddedWebApplicationContext作為Spring應用上下文的實作類。

        啟動SOFABoot應用的主要步驟如下:

        (一)    建立StopWatch執行個體,并啟動,記錄任務的運作時間;

        (二)    設定系統屬性java.awt.headless;

        (三)    通過SpringFactoriesLoader類loadFactoryNames方法,查找并加載所有spring.factories檔案中定義的SpringApplicationRunListener接口的實作,并調用started方法啟動監聽器;

      (四)   建立Spring應用上下文AnnotationConfigEmbeddedWebApplicationContext:       1.  protectedConfigurableApplicationContext createApplicationContext() {

2.       Class<?>contextClass = this.applicationContextClass;

3.       if(contextClass == null) {

4.           try{

5.               contextClass =Class.forName(this.webEnvironment

6.                       ?DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);

7.           }

8.           catch(ClassNotFoundException ex) {

9.               thrownew IllegalStateException(……略);

10.          }

11.      }

12.      return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);

13. }

        此處是WEB環境,是以建立AnnotationConfigEmbeddedWebApplicationContext執行個體: 

1.  publicAnnotationConfigEmbeddedWebApplicationContext() {

2.       this.reader = new AnnotatedBeanDefinitionReader(this);

3.       this.scanner= new ClassPathBeanDefinitionScanner(this);

4.  }

        在AnnotationConfigEmbeddedWebApplicationContext構造函數中,建立AnnotatedBeanDefinitionReader執行個體,用來處理含有注解的類。建立ClassPathBeanDefinitionScanner執行個體,用來掃描指定路徑下含有注解的類。由于SOFAbBoot主要采用JavaConfig的配置形式,是以此處主要看一下AnnotatedBeanDefinitionReader執行個體化過程,以了解注解相關處理器的注冊過程: 

1.    publicAnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environmentenvironment) {

2.            ……略       

3.           AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

4.  }

    在Spring應用上下文中注冊所有注解相關的Post Processors: 

1.  public staticSet<BeanDefinitionHolder> registerAnnotationConfigProcessors(

2.           BeanDefinitionRegistryregistry, Object source) {

3.   

4.       DefaultListableBeanFactorybeanFactory = unwrapDefaultListableBeanFactory(registry);

5.       ……略   

6.       Set<BeanDefinitionHolder>beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);

7.       if(!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)){

8.           RootBeanDefinitiondef = new RootBeanDefinition(ConfigurationClassPostProcessor.class);

9.           def.setSource(source);

10.          beanDefs.add(registerPostProcessor(registry,def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));

11.      }

12.  

13.      if(!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {

14.          RootBeanDefinitiondef = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);

15.          def.setSource(source);

16.          beanDefs.add(registerPostProcessor(registry,def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));

17.      }

18.  

19.      if(!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {

20.          RootBeanDefinitiondef = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);

21.          def.setSource(source);

22.          beanDefs.add(registerPostProcessor(registry,def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));

23.      }

24.  

25.      //Check for JSR-250 support, and if present add theCommonAnnotationBeanPostProcessor.

26.      if(jsr250Present &&!registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {

27.          RootBeanDefinitiondef = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);

28.          def.setSource(source);

29.          beanDefs.add(registerPostProcessor(registry,def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));

30.      }

31.      ……略   

32.      if(!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {

33.          RootBeanDefinition def = newRootBeanDefinition(EventListenerMethodProcessor.class);

34.          def.setSource(source);

35.          beanDefs.add(registerPostProcessor(registry,def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));

36.      }

37.      if(!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {

38.          RootBeanDefinitiondef = new RootBeanDefinition(DefaultEventListenerFactory.class);

39.          def.setSource(source);

40.          beanDefs.add(registerPostProcessor(registry,def, EVENT_LISTENER_FACTORY_BEAN_NAME));

41.      }

42.  

43.      returnbeanDefs;

44. }

        與注解相關的PostProcessors主要包括ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,EventListenerMethodProcessor,PersistenceAnnotationBeanPostProcessor,DefaultEventListenerFactory。

        其中,ConfigurationClassPostProcessor實作了BeanFactoryPostProcessor接口。當重新整理Spring上下文(調用AbstractApplicationContext類refresh方法)過程中,調用AbstractApplicationContext類invokeBeanFactoryPostProcessors方法時,被調用執行。ConfigurationClassPostProcessor解析@Configuration注解,調用ConfigurationClassParser類的parse方法,處理@ComponentScan、@Import、@ImportResource、@Bean注解。

        AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor, RequiredAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor實作了BeanPostProcessor接口,在建立Bean執行個體時被調用。例如:在重新整理Spring上下文(調用AbstractApplicationContext類refresh方法)過程中,調用finishBeanFactoryInitialization方法時,在執行個體化所有單例Bean對象時被調用。

        EventListenerMethodProcessor,DefaultEventListenerFactory是事件監聽器相關的PostProcessors。

        至此,建立AnnotationConfigEmbeddedWebApplicationContext執行個體的主要步驟完成。

         (五)   準備Spring應用上下文:       1.  private voidprepareContext(ConfigurableApplicationContext context,

2.           ConfigurableEnvironmentenvironment, SpringApplicationRunListeners listeners,

3.           ApplicationArgumentsapplicationArguments, Banner printedBanner) {

4.       context.setEnvironment(environment);

5.       postProcessApplicationContext(context);

6.       applyInitializers(context);

7.       listeners.contextPrepared(context);

8.       if(this.logStartupInfo) {

9.           logStartupInfo(context.getParent()== null);

10.          logStartupProfileInfo(context);

11.      }

12.  

13.      //Add boot specific singleton beans

14.      context.getBeanFactory().registerSingleton("springApplicationArguments",

15.              applicationArguments);

16.      if(printedBanner != null) {

17.          context.getBeanFactory().registerSingleton("springBootBanner",printedBanner);

18.      }

19.  

20.      //Load the sources

21.      Set<Object>sources = getSources();

22.      Assert.notEmpty(sources,"Sources must not be empty");

23.      load(context, sources.toArray(newObject[sources.size()]));

24.      listeners.contextLoaded(context);

25. }

        1.   設定Spring應用上下文Enviroment;

        2.   設定Spring應用上下文屬性,如beanNameGenerator、resourceLoader等;

        3.   依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的ApplicationContextInitializer接口的實作的initialize方法,完成Spring應用上下文的初始化工作; 

        4.   依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的SpringApplicationRunListener接口的實作的contextPrepared方法。該方法在Spring應用上下文建立完,并且前期準備工作完成,但加載資源以前,完成Spring應用上下文的一些處理工作;

        5.   建立BeanDefinitionLoader執行個體(構造函數中依次建立AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、ClassPathBeanDefinitionScanner),然後調用該執行個體的load方法注冊run方法中參數指定的source資源(此處為SofaBootRpcDemoApplication)到Spring應用上下文,這個資源是ConfigurationClassPostProcessor類處理的入口配置類,可以是一個或多個;

        6.   依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的SpringApplicationRunListener接口的實作的contextLoaded方法。該方法在Spring應用上下文已經被加載,但還沒有重新整理之前,完成Spring應用上下文的一些處理工作。

        (六)   重新整理Spring應用上下文: 1.  public void refresh() throws BeansException,IllegalStateException {

2.       synchronized(this.startupShutdownMonitor) {

3.           //Prepare this context for refreshing.

4.           prepareRefresh();

5.   

6.           //Tell the subclass to refresh the internal bean factory.

7.           ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory();

8.   

9.           //Prepare the bean factory for use in this context.

10.          prepareBeanFactory(beanFactory);

11.  

12.          try{

13.              //Allows post-processing of the bean factory in context subclasses.

14.              postProcessBeanFactory(beanFactory);

15.  

16.              //Invoke factory processors registered as beans in the context.

17.              invokeBeanFactoryPostProcessors(beanFactory);

18.  

19.              //Register bean processors that intercept bean creation.

20.              registerBeanPostProcessors(beanFactory);

21.  

22.              //Initialize message source for this context.

23.              initMessageSource();

24.  

25.              //Initialize event multicaster for this context.

26.              initApplicationEventMulticaster();

27.  

28.              //Initialize other special beans in specific context subclasses.

29.              onRefresh();

30.  

31.              //Check for listener beans and register them.

32.              registerListeners();

33.  

34.              //Instantiate all remaining (non-lazy-init) singletons.

35.              finishBeanFactoryInitialization(beanFactory);

36.  

37.              //Last step: publish corresponding event.

38.              finishRefresh();

39.          }

40.  

41.          catch(BeansException ex) {

42.              if(logger.isWarnEnabled()) {

43.                  logger.warn("Exceptionencountered during context initialization - " +

44.                          "cancellingrefresh attempt: " + ex);

45.              }

46.  

47.              //Destroy already created singletons to avoid dangling resources.

48.              destroyBeans();

49.  

50.              //Reset 'active' flag.

51.              cancelRefresh(ex);

52.  

53.              //Propagate exception to caller.

54.              throwex;

55.          }

56.  

57.          finally{

58.              //Reset common introspection caches in Spring's core, since we

59.              //might not ever need metadata for singleton beans anymore...

60.              resetCommonCaches();

61.          }

62.      }

63. }

     看到這段代碼,Spring開發人員都應該比較熟悉了。這段代碼為Spring應用上下文的具體實作類調用父類AbstractApplicationContext的refresh方法進行重新整理操作。對于整個Spring應用上下文重新整理過程,大家應該都比較熟悉了,在此不詳述了。

         此時,我們主要關注invokeBeanFactoryPostProcessors(beanFactory)方法: 1.  protected voidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {

2.       PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory,getBeanFactoryPostProcessors());

3.   

4.       ……略

5.  }

      該方法通過PostProcessorRegistrationDelegate調用所有實作BeanFactoryPostProcessor接口的實作類,完成beanFactory的一些處理工作。

1.  public static voidinvokeBeanFactoryPostProcessors(

2.           ConfigurableListableBeanFactorybeanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

3.   

4.       ……略

5.   

6.           //First, invoke the BeanDefinitionRegistryPostProcessors that implementPriorityOrdered.

7.          ……略

8.           invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors,registry);

9.   

10.          //Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.

11.          ……略

12.          invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors,registry);

13.  

14.          //Finally, invoke all other BeanDefinitionRegistryPostProcessors until no furtherones appear.

15.          ……略

16.  

17.          //Now, invoke the postProcessBeanFactory callback of all processors handled sofar.

18.          invokeBeanFactoryPostProcessors(registryPostProcessors,beanFactory);

19.          invokeBeanFactoryPostProcessors(regularPostProcessors,beanFactory);

20.      }

21.  

22.      else{

23.          //Invoke factory processors registered with the context instance.

24.          invokeBeanFactoryPostProcessors(beanFactoryPostProcessors,beanFactory);

25.     }

26.  

27.      ……略

28.      //Separate between BeanFactoryPostProcessors that implement PriorityOrdered,

29.      //Ordered, and the rest.

30.      ……略

31.      //First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.

32.      ……略

33.      invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors,beanFactory);

34.  

35.      //Next, invoke the BeanFactoryPostProcessors that implement Ordered.

36.      ……略

37.      invokeBeanFactoryPostProcessors(orderedPostProcessors,beanFactory);

38.  

39.      //Finally, invoke all other BeanFactoryPostProcessors.

40.      ……略

41.      invokeBeanFactoryPostProcessors(nonOrderedPostProcessors,beanFactory);

42.  

43.      ……略

44. }         在invokeBeanDefinitionRegistryPostProcessors方法中,調用實作了PriorityOrdered接口和BeanDefinitionRegistryPostProcessor接口的BeanFactoryPostProcessor的實作類ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法: 1.  public voidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {

2.      

3.        RootBeanDefinition iabpp = newRootBeanDefinition(ImportAwareBeanPostProcessor.class);

4.       iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

5.       registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME,iabpp);

6.   

7.       RootBeanDefinitionecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);

8.       ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

9.       registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME,ecbpp);

10.        ……略

11.  

12.      processConfigBeanDefinitions(registry);

13. }

   在Spring應用上下文中注冊BeanPostProcessor接口的實作類ImportAwareBeanPostProcessor、EnhancedConfigurationBeanPostProcessor;

        調用processConfigBeanDefinitions方法,開始處理@Configuration配置類。

        注意 ,此處是整個應用中所有@Configuration配置類的處理入口。在整個@Configuration配置類處理過程中,循環處理所有@Configuration配置類,直至所有@Configuration配置類處理完。         1.  public void processConfigBeanDefinitions(BeanDefinitionRegistryregistry) {

2.       List<BeanDefinitionHolder>configCandidates = new ArrayList<BeanDefinitionHolder>();

3.       String[]candidateNames = registry.getBeanDefinitionNames();

4.   

5.       for(String beanName : candidateNames) {

6.           BeanDefinitionbeanDef = registry.getBeanDefinition(beanName);

7.           if(ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||

8.                   ConfigurationClassUtils.isLiteConfigurationClass(beanDef)){

9.               ……略

10.          }

11.          elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory)) {

12.              configCandidates.add(newBeanDefinitionHolder(beanDef, beanName));

13.          }

14.      }

15.       ……略

16.       ConfigurationClassParser parser = new ConfigurationClassParser(

17.              this.metadataReaderFactory,this.problemReporter, this.environment,

18.              this.resourceLoader,this.componentScanBeanNameGenerator, registry);

19.       ……略

20.      do {

21.          parser.parse(candidates);

22.          parser.validate();

23.  

24.          ……略

25.            // Read the model and create beandefinitions based on its content

26.          if(this.reader == null) {

27.              this.reader= new ConfigurationClassBeanDefinitionReader(

28.                      registry,this.sourceExtractor, this.resourceLoader, this.environment,

29.                      this.importBeanNameGenerator,parser.getImportRegistry());

30.          }

31.          this.reader.loadBeanDefinitions(configClasses);

32.          alreadyParsed.addAll(configClasses);

33.  

34.          candidates.clear();

35.          ……略

36.            for (ConfigurationClassconfigurationClass : alreadyParsed) {

37.                  alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());

38.              }

39.              for(String candidateName : newCandidateNames) {

40.                  if(!oldCandidateNames.contains(candidateName)) {

41.                      BeanDefinitionbeanDef = registry.getBeanDefinition(candidateName);

42.                      if(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory) &&

43.                              !alreadyParsedClasses.contains(beanDef.getBeanClassName())){

44.                          candidates.add(newBeanDefinitionHolder(beanDef, candidateName));

45.                      }

46.                  }

47.              }       

48.      }

49.      while (!candidates.isEmpty());

50.  

51.      ……略

52. }

        @Configuration配置類主要處理步驟:

        1.   依次周遊DefaultListableBeanFactory中所有BeanDefinition,查找帶有@Configuration注解的BeanDefinition,放入configCandidates。然後根據@order進行排序,此時隻有一個包含@Configuration注解的類,即SofaBootRpcDemoApplication;

        2.   構造@Configuration注解的解析類ConfigurationClassParser;

        3.   依次周遊集合candidates中所有@Configuration配置類,逐一處理配置類中的注解,此時隻有SofaBootRpcDemoApplication。 1.  public voidparse(Set<BeanDefinitionHolder> configCandidates) {

2.      

3.        this.deferredImportSelectors = newLinkedList<DeferredImportSelectorHolder>();

4.   

5.       for (BeanDefinitionHolder holder :configCandidates) {

6.           BeanDefinitionbd = holder.getBeanDefinition();

7.           try{

8.               if(bd instanceof AnnotatedBeanDefinition) {

9.                   parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());

10.              }

11.              elseif (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {

12.                  parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());

13.              }

14.              else{

15.                  parse(bd.getBeanClassName(),holder.getBeanName());

16.              }

17.          }

18.          catch(BeanDefinitionStoreException ex) {

19.              throwex;

20.          }

21.          catch(Throwable ex) {

22.              thrownew BeanDefinitionStoreException(……略);

23.          }

24.      }

25.      processDeferredImportSelectors();

26. }

          依次處理每個@Configuration配置類,具體處理操作在doProcessConfigurationClass方法中實作,由于該方法負責具體處理每個@Configuration配置類,稍後在後面具體介紹。

        處理完傳入的@Configuration配置類以後,處理實作DeferredImportSelector接口的ImportSelector類,主要用于有條件的加載某些類,如使用@Conditional注解。 1.  private voidprocessDeferredImportSelectors() {

2.       List<DeferredImportSelectorHolder>deferredImports = this.deferredImportSelectors;

3.       ……略

4.       for(DeferredImportSelectorHolder deferredImport : deferredImports) {

5.           ConfigurationClassconfigClass = deferredImport.getConfigurationClass();

6.           try{

7.               String[] imports =deferredImport.getImportSelector().selectImports(configClass.getMetadata());

8.               processImports(configClass,asSourceClass(configClass), asSourceClasses(imports), false);

9.           }

10.          catch(BeanDefinitionStoreException ex) {

11.              throwex;

12.          }

13.          catch(Throwable ex) {

14.              thrownew BeanDefinitionStoreException(……略);

15.          }

16.      }

17. }

          依次處理所有實作DeferredImportSelector接口的ImportSelector類。

        第一次循環時,隻有一個類,即由SofaBootRpcDemoApplication類導入的EnableAutoConfigurationImportSelector類,調用其selectImports方法,解析出所有支援自動配置的@Configuration配置類:

1.  public String[]selectImports(AnnotationMetadata metadata) {

2.       if(!isEnabled(metadata)) {

3.           returnNO_IMPORTS;

4.       }

5.       try{

6.           AnnotationAttributesattributes = getAttributes(metadata);

7.           List<String> configurations =getCandidateConfigurations(metadata,

8.                   attributes);

9.           configurations= removeDuplicates(configurations);

10.          Set<String>exclusions = getExclusions(metadata, attributes);

11.          configurations.removeAll(exclusions);

12.          configurations= sort(configurations);

13.          recordWithConditionEvaluationReport(configurations,exclusions);

14.          returnconfigurations.toArray(new String[configurations.size()]);

15.      }

16.      catch(IOException ex) {

17.          thrownew IllegalStateException(ex);

18.      }

19. }           調用getCandidateConfigurations方法,擷取所有@Configuration配置類的名字:         1.  protected List<String>getCandidateConfigurations(AnnotationMetadata metadata,

2.           AnnotationAttributesattributes) {

3.       List<String> configurations =SpringFactoriesLoader.loadFactoryNames(

4.               getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());

5.       ……略

6.         return configurations;

7.  }         通過SpringFactoriesLoader的loadFactoryNames方法,從所有的spring.fatories檔案中,加載key為org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有@Configuration配置類的名字。 1.  public static List<String>loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {

2.       StringfactoryClassName = factoryClass.getName();

3.       try{

4.           Enumeration<URL> urls = (classLoader !=null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

5.                   ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

6.           List<String>result = new ArrayList<String>();

7.           while (urls.hasMoreElements()) {

8.               URLurl = urls.nextElement();

9.               Propertiesproperties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));

10.              StringfactoryClassNames = properties.getProperty(factoryClassName);

11.          result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));

12.          }

13.          returnresult;

14.      }

15.      catch(IOException ex) {

16.          thrownew IllegalArgumentException(……略);

17.      }

18. }         擷取所有@Configuration配置類的名字以後,進行去重、排序、篩選等操作。然後調用processImports方法,處理通過EnableAutoConfigurationImportSelector類導入的@Configuration配置類: 1.  private voidprocessImports(ConfigurationClass configClass, SourceClass currentSourceClass,

2.           Collection<SourceClass>importCandidates, boolean checkForCircularImports) throws IOException {

3.   

4.         ……略

5.       if(checkForCircularImports && isChainedImportOnStack(configClass)) {

6.           ……略

7.       }

8.       else{

9.           this.importStack.push(configClass);

10.          try{

11.              for (SourceClass candidate : importCandidates){

12.                  if(candidate.isAssignable(ImportSelector.class)) {

13.                      //Candidate class is an ImportSelector -> delegate to it to determine imports

14.                      Class<?>candidateClass = candidate.loadClass();

15.                      ImportSelectorselector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);

16.                      ParserStrategyUtils.invokeAwareMethods(

17.                              selector,this.environment, this.resourceLoader, this.registry);

18.                      if(this.deferredImportSelectors != null && selector instanceofDeferredImportSelector) {

19.                          this.deferredImportSelectors.add(

20.                                  newDeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));

21.                      }

22.                      else{

23.                          String[]importClassNames = selector.selectImports(currentSourceClass.getMetadata());

24.                          Collection<SourceClass>importSourceClasses = asSourceClasses(importClassNames);

25.                          processImports(configClass,currentSourceClass, importSourceClasses, false);

26.                      }

27.                  }

28.                  elseif (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {

29.                      //Candidate class is an ImportBeanDefinitionRegistrar ->

30.                      //delegate to it to register additional bean definitions

31.                      Class<?>candidateClass = candidate.loadClass();

32.                      ImportBeanDefinitionRegistrarregistrar =

33.                              BeanUtils.instantiateClass(candidateClass,ImportBeanDefinitionRegistrar.class);

34.                      ParserStrategyUtils.invokeAwareMethods(

35.                              registrar,this.environment, this.resourceLoader, this.registry);

36.                      configClass.addImportBeanDefinitionRegistrar(registrar,currentSourceClass.getMetadata());

37.                  }

38.                  else{

39.                      //Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->

40.                      //process it as an @Configuration class

41.                      this.importStack.registerImport(

42.                              currentSourceClass.getMetadata(),candidate.getMetadata().getClassName());

43.                      processConfigurationClass(candidate.asConfigClass(configClass));

44.                  }

45.              }

46.          }

47.          catch(BeanDefinitionStoreException ex) {

48.              throwex;

49.          }

50.          catch(Throwable ex) {

51.              ……略

52.          }

53.          finally{

54.              this.importStack.pop();

55.          }

56.      }

57. }

        processImports方法主要邏輯:

        1)   如果導入的配置類實作了DeferredImportSelector接口,則建立DeferredImportSelectorHolder執行個體,并置入deferredImportSelectors連結清單中,待上述processConfigBeanDefinitions方法下一個do{*}while(*)循環中處理。

        2)   如果導入的配置類實作了ImportSelector接口,則調用該類的selectImports方法導入新的@Configuration配置類,并嵌套調用processImports方法,處理導入的@Configuration配置類。

        3)   如果導入的配置類實作了ImportBeanDefinitionRegistrar接口,則建立ImportBeanDefinitionRegistrar執行個體,并在處理@Configuration配置類過程中,委托該執行個體注冊額外的BeanDefinition。

        4)   如果以上都不是,則導入的配置類為基本的@Configuration配置類,直接調用processConfigurationClass處理,最終具體解析操作在doProcessConfigurationClass方法中實作。

1.  protected final SourceClassdoProcessConfigurationClass(ConfigurationClass configClass, SourceClasssourceClass) throws IOException {

2.       //Recursively process any member (nested) classes first

3.       processMemberClasses(configClass,sourceClass);

4.   

5.       //處理@PropertySource注解

6.       for(AnnotationAttributes propertySource :AnnotationConfigUtils.attributesForRepeatable(

7.               sourceClass.getMetadata(),PropertySources.class, org.springframework.context.annotation.PropertySource.class)){

8.           if(this.environment instanceof ConfigurableEnvironment) {

9.               processPropertySource(propertySource);

10.          }

11.          else{

12.              logger.warn("[email protected] annotation on [" + sourceClass.getMetadata().getClassName()+

13.                      "].Reason: Environment must implement ConfigurableEnvironment");

14.          }

15.      }

16.  

17.      // 處理@ComponentScan注解

18.      Set<AnnotationAttributes>componentScans = AnnotationConfigUtils.attributesForRepeatable(

19.              sourceClass.getMetadata(),ComponentScans.class, ComponentScan.class);

20.      if(!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(),ConfigurationPhase.REGISTER_BEAN)) {

21.          for(AnnotationAttributes componentScan : componentScans) {

22.              //The config class is annotated with @ComponentScan -> perform the scanimmediately

23.              Set<BeanDefinitionHolder>scannedBeanDefinitions =

24.                      this.componentScanParser.parse(componentScan,sourceClass.getMetadata().getClassName());

25.              //Check the set of scanned definitions for any further config classes and parserecursively if necessary

26.              for(BeanDefinitionHolder holder : scannedBeanDefinitions) {

27.                  if(ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(),this.metadataReaderFactory)) {

28.                      parse(holder.getBeanDefinition().getBeanClassName(),holder.getBeanName());

29.                  }

30.              }

31.          }

32.      }

33.  

34.      //處理@Import注解

35.      processImports(configClass, sourceClass,getImports(sourceClass), true);

36.  

37.      //處理@ImportResource注解

38.      if(sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {

39.          AnnotationAttributesimportResource =

40.                  AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(),ImportResource.class);

41.          String[]resources = importResource.getStringArray("locations");

42.          Class<?extends BeanDefinitionReader> readerClass =importResource.getClass("reader");

43.          for(String resource : resources) {

44.              StringresolvedResource = this.environment.resolveRequiredPlaceholders(resource);

45.              configClass.addImportedResource(resolvedResource,readerClass);

46.          }

47.      }

48.  

49.      //處理@Bean方法

50.      Set<MethodMetadata>beanMethods =sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());

51.      for(MethodMetadata methodMetadata : beanMethods) {

52.          configClass.addBeanMethod(newBeanMethod(methodMetadata, configClass));

53.      }

54.  

55.      ……略

56.  

57.      //No superclass -> processing is complete

58.      returnnull;

59. }

        針對每個@Configuration配置類,依次處理@Configuration配置類中的@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean注解,主要解析過程如下:

        1)   @PropertySource處理過程:調用processPropertySource方法處理屬性源,在此不詳述;

        2)   @ComponentScan處理過程:解析@Configuration配置類中@ComponentScan、@ComponentScans注解,并根據設定的basePackages掃描指定的包路徑,并加載、注冊所有帶有@Component注解及@Component衍生注解(如@Controller、@Service、@Repository、@Configuration等)Java類的Bean定義到Spring應用上下文;

        3)   @Import處理過程:參考上述processImports方法描述。需要注意,在處理某個配置類的@Import注解過程中,如果發現@Import導入的類實作了DeferredImportSelector接口,則建立DeferredImportSelectorHolder執行個體,并置入deferredImportSelectors連結清單中,待下一次循環處理。以此類推,直至所有的@Configuration配置類加載完畢。例如:在處理SofaBootRpcDemoApplication的@Import注解時,由于在SofaBootRpcDemoApplication類@SpringBootApplication注解中引用了@EnableAutoConfiguration注解,而它又引用了@Import(value={EnableAutoConfigurationImportSelector.class})。由于EnableAutoConfigurationImportSelector實作了DeferredImportSelector接口,是以在此建立DeferredImportSelectorHolder執行個體,并置入deferredImportSelectors連結清單中,待所有@Configuration注解類解析完成以後再處理。

        4)   @ImportResource處理過程:解析locations屬性,并依次擷取locations指定目錄下Spring XML配置檔案,設定configClass類的importedResource屬性;

        5)   @Bean處理過程:讀取@Configuration配置類中@Bean注解的方法,并增加到configClass的beanMethod集合。

        至此,處理完現有candidates中所有@Configuration配置類。

        在回看一下processConfigBeanDefinitions方法: 1.  public voidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry) {

2.       List<BeanDefinitionHolder>configCandidates = new ArrayList<BeanDefinitionHolder>();

3.       String[]candidateNames = registry.getBeanDefinitionNames();

4.   

5.       for(String beanName : candidateNames) {

6.           BeanDefinitionbeanDef = registry.getBeanDefinition(beanName);

7.           if(ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||

8.                   ConfigurationClassUtils.isLiteConfigurationClass(beanDef)){

9.               ……略

10.          }

11.          elseif (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory)) {

12.              configCandidates.add(newBeanDefinitionHolder(beanDef, beanName));

13.          }

14.      }

15.       ……略

16.       ConfigurationClassParser parser = newConfigurationClassParser(

17.              this.metadataReaderFactory,this.problemReporter, this.environment,

18.              this.resourceLoader,this.componentScanBeanNameGenerator, registry);

19.       ……略

20.      do {

21.          parser.parse(candidates);

22.          parser.validate();

23.  

24.          ……略

25.            // Read the model and create beandefinitions based on its content

26.          if(this.reader == null) {

27.              this.reader= new ConfigurationClassBeanDefinitionReader(

28.                      registry,this.sourceExtractor, this.resourceLoader, this.environment,

29.                      this.importBeanNameGenerator,parser.getImportRegistry());

30.          }

31.          this.reader.loadBeanDefinitions(configClasses);

32.  

33.          alreadyParsed.addAll(configClasses);

34.          candidates.clear();

35.          ……略

36.            for (ConfigurationClassconfigurationClass : alreadyParsed) {

37.                  alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());

38.              }

39.              for(String candidateName : newCandidateNames) {

40.                  if(!oldCandidateNames.contains(candidateName)) {

41.                      BeanDefinitionbeanDef = registry.getBeanDefinition(candidateName);

42.                      if(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory) &&

43.                              !alreadyParsedClasses.contains(beanDef.getBeanClassName())){

44.                          candidates.add(newBeanDefinitionHolder(beanDef, candidateName));

45.                      }

46.                  }

47.              }       

48.      }

49.      while (!candidates.isEmpty());

50.  

51.      ……略

52. }

        處理完現有candidates中所有@Configuration配置類以後,調用ConfigurationClassBeanDefinitionReader的loadBeanDefinitions方法加載、注冊@Configuration配置類本身及其中的@Bean注解對應的類。

1.  public voidloadBeanDefinitions(Set<ConfigurationClass> configurationModel) {

2.       TrackedConditionEvaluatortrackedConditionEvaluator = new TrackedConditionEvaluator();

3.       for(ConfigurationClass configClass : configurationModel) {

4.           loadBeanDefinitionsForConfigurationClass(configClass,trackedConditionEvaluator);

5.       }

6.  }         @Configuration配置類的具體加載方法如下: 1.  private void loadBeanDefinitionsForConfigurationClass(ConfigurationClassconfigClass,

2.           TrackedConditionEvaluatortrackedConditionEvaluator) {

3.   

4.       ……略

5.   

6.       if(configClass.isImported()) {

7.           registerBeanDefinitionForImportedConfigurationClass(configClass);

8.       }

9.       for(BeanMethod beanMethod : configClass.getBeanMethods()) {

10.          loadBeanDefinitionsForBeanMethod(beanMethod);

11.      }

12.      loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

13.      loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

14. }

        @Configuration配置類主要加載過程如下:

        1)   把@Configuration配置類自身注冊到Spring應用上下文;

        2)   加載@Bean注解對應的BeanDefinition;

        3)   加載@ImportResource注解指定的XML檔案或Groovy檔案中配置的BeanDefinition;

        4)   從ImportBeanDefinitionRegistrar加載BeanDefinition,如AutoConfigurationPackages;

        在此就不詳述注冊過程,感興趣的同學可以自己看看。

        接下來先清空candidates集合,然後從Spring應用上下文中擷取所有已經注冊的BeanDefinitionNames,然後與已經解析的@Configuration配置類對比,查找未處理的@Configuration配置類,并置入candidates集合,待下一次循環處理。此處循環處理下去,直至所有的@Configuration配置類處理完畢。

        至此,ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法執行完畢,加載完所有配置的BeanDefinition。

        不在詳述AbstractApplicationContext的refresh方法中其它步驟,感興趣的同學可以自己看看。

        (七)    Spring應用上下文重新整理以後的處理:

        依次調用ApplicationRunner接口和CommandLineRunner接口的實作,完成重新整理Spring上下文以後的一些處理操作。

        (八)    終止監聽

        依次調用通過SpringFactoriesLoader從spring.factories檔案中加載的SpringApplicationRunListener接口的實作的finished方法,結束監聽器。

        (九)    結束run方法

        調用StopWatch執行個體的stop方法,結束記錄任務的運作時間。

        到此為止,SOFABoot應用啟動完成。

三、總結

        通過以上源碼分析,可以總結出以下幾個部分,作為基于SOFABoot架構進行開發時的基礎和擴充的方向。

          (一) EnableAutoConfiguration

        基于SOFABoot架構開發的子產品,通過在META-INF/spring.factories檔案中配置EnableAutoConfiguration實作SOFABoot子產品自動配置功能。

        在SOFABoot應用啟動時,自動加載SOFABoot子產品META-INF/spring.factories檔案中配置的@Configuration配置類,以此作為子產品加載的入口,把子產品中相關的Bean加載到Spring應用上下文。

        參考配置格式為:

1.  # Auto Configure

2.  org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

3.  org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\

4.  org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

5.  org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

6.  org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\

7.  org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\

8.  org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\

9.  org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

10. org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\

11. org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\

        這些支援自動配置的Bean在ApplicationContext重新整理過程中被注冊到Spring應用上下文,具體加載過程參考源碼解析部分。

           (二)  ApplicationContextInitializer

        如果希望對Spring應用上下文進行一些個性化的初始化操作,則實作ApplicationContextInitializer接口,在initialize方法中完成個性化的初始化操作。

        在META-INF/spring.factories檔案配置ApplicationContextInitializer接口的實作,格式為:

1.  org.springframework.context.ApplicationContextInitializer=\

2.  xxx.yyyy.zzz.XxxInitializer,\

3.  xxx.yyyy.zzz.YyyInitializer

        這些ApplicationContextInitializer接口的實作類在Spring應用上下文準備階段被調用執行,即SpringApplication的prepareContext方法中。

         (三) ApplicationListener

        如果希望監聽Spring應用的事件,當事件發生時,針對事件進行一些個性化操作,則實作ApplicationListener接口,在onApplicationEvent方法中處理發生的Application事件。

        在META-INF/spring.factories檔案配置ApplicationListener接口的實作,格式為:

1.  org.springframework.context.ApplicationListener=\

2.  xxx.yyyy.zzz.XxxListener

        當ApplicationEvent事件發生時,這些ApplicationListener接口的實作被調用。

          (四) SpringApplicationRunListener

        如果希望監聽SpringBoot的org.springframework.boot.SpringApplication類的run方法,當SpringApplication啟動時,進行一些個性化操作,則實作SpringApplicationRunListener接口,針對具體情況實作相應的方法。

1.  public interfaceSpringApplicationRunListener {

2.   

3. 

6.  void started();

7.   

8. 

11. void environmentPrepared(ConfigurableEnvironmentenvironment);

12.  

13.

16. voidcontextPrepared(ConfigurableApplicationContext context);

17.  

18.

21. voidcontextLoaded(ConfigurableApplicationContext context);

22.  

23.

26. void finished(ConfigurableApplicationContextcontext, Throwable exception);

27.  

28. }

          在SpringApplication的run方法中,通過SpringFactoriesLoader類loadFactoryNames方法,查找并加載所有spring.factories檔案中定義的SpringApplicationRunListener接口的實作。

        SpringApplicationRunListener接口中各個方法的調用順序為:

        1.   在SpringApplication的run方法中,調用started方法;

        2.   在SpringApplication的prepareEnvironment方法中,調用environmentPrepared方法;

        3.   在SpringApplication的prepareContext方法中,先調用contextPrepared方法,然後是Spring上下文資源加載,最後調用contextLoaded方法;

        4.   在SpringApplication的run方法中,調用finished方法。

繼續閱讀