SpringBoot啟動時自動配置類的加載流程


  自定義過starter的同學應該都知道,自動配置類需要用 EnableAutoConfiguration  注解修飾,並且需要將自動配置類配置在spring.factories中。但自動配置類是如何被SpringBoot加載的呢?

  網上有些文章已經講述了  EnableAutoConfiguration  注解類由  @Import(AutoConfigurationImportSelector.class)  修飾,已經SpringBoot是從spring.factories文件中讀取自動配置類的。我這里結合SpringBoot的啟動流程,記述一下自動配置類的加載過程。

  首先,從SpringBoot項目的啟動類的SpringBootApplication.run(#,#)方法查看源碼,可以看到最終執行的是以下代碼:

 1 /**
 2      * Run the Spring application, creating and refreshing a new
 3      * {@link ApplicationContext}.
 4      * @param args the application arguments (usually passed from a Java main method)
 5      * @return a running {@link ApplicationContext}
 6      */
 7     public ConfigurableApplicationContext run(String... args) {
 8         StopWatch stopWatch = new StopWatch();
 9         stopWatch.start();
10         ConfigurableApplicationContext context = null;
11         Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
12         configureHeadlessProperty();
13         SpringApplicationRunListeners listeners = getRunListeners(args);
14         listeners.starting();
15         try {
16             ApplicationArguments applicationArguments = new DefaultApplicationArguments(
17                     args);
18             ConfigurableEnvironment environment = prepareEnvironment(listeners,
19                     applicationArguments);
20             configureIgnoreBeanInfo(environment);
21             Banner printedBanner = printBanner(environment);
22             context = createApplicationContext();
23             exceptionReporters = getSpringFactoriesInstances(
24                     SpringBootExceptionReporter.class,
25                     new Class[] { ConfigurableApplicationContext.class }, context);
26             prepareContext(context, environment, listeners, applicationArguments,
27                     printedBanner);
28             refreshContext(context);
29             afterRefresh(context, applicationArguments);
30             stopWatch.stop();
31             if (this.logStartupInfo) {
32                 new StartupInfoLogger(this.mainApplicationClass)
33                         .logStarted(getApplicationLog(), stopWatch);
34             }
35             listeners.started(context);
36             callRunners(context, applicationArguments);
37         }
38         catch (Throwable ex) {
39             handleRunFailure(context, ex, exceptionReporters, listeners);
40             throw new IllegalStateException(ex);
41         }
42 
43         try {
44             listeners.running(context);
45         }
46         catch (Throwable ex) {
47             handleRunFailure(context, ex, exceptionReporters, null);
48             throw new IllegalStateException(ex);
49         }
50         return context;
51     }

  可以看到SpringBoot啟動時的主要內容是創建Environment、ApplicationContext以及對應事件的發布。代碼詳情,這里不涉及。我們這關注SpringBoot是在哪一步加載自動配置類的。而這一步就是第28行的  refreshContext(context);  追蹤 refreshContext(context); 可以發現,實際執行的代碼是 AbstractApplicationContext 的 refresh 方法, 代碼如下:

 

 1 @Override
 2     public void refresh() throws BeansException, IllegalStateException {
 3         synchronized (this.startupShutdownMonitor) {
 4             // Prepare this context for refreshing.
 5             prepareRefresh();
 6 
 7             // Tell the subclass to refresh the internal bean factory.
 8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 9 
10             // Prepare the bean factory for use in this context.
11             prepareBeanFactory(beanFactory);
12 
13             try {
14                 // Allows post-processing of the bean factory in context subclasses.
15                 postProcessBeanFactory(beanFactory);
16 
17                 // Invoke factory processors registered as beans in the context.
18                 invokeBeanFactoryPostProcessors(beanFactory);
19 
20                 // Register bean processors that intercept bean creation.
21                 registerBeanPostProcessors(beanFactory);
22 
23                 // Initialize message source for this context.
24                 initMessageSource();
25 
26                 // Initialize event multicaster for this context.
27                 initApplicationEventMulticaster();
28 
29                 // Initialize other special beans in specific context subclasses.
30                 onRefresh();
31 
32                 // Check for listener beans and register them.
33                 registerListeners();
34 
35                 // Instantiate all remaining (non-lazy-init) singletons.
36                 finishBeanFactoryInitialization(beanFactory);
37 
38                 // Last step: publish corresponding event.
39                 finishRefresh();
40             }
41 
42             catch (BeansException ex) {
43                 if (logger.isWarnEnabled()) {
44                     logger.warn("Exception encountered during context initialization - " +
45                             "cancelling refresh attempt: " + ex);
46                 }
47 
48                 // Destroy already created singletons to avoid dangling resources.
49                 destroyBeans();
50 
51                 // Reset 'active' flag.
52                 cancelRefresh(ex);
53 
54                 // Propagate exception to caller.
55                 throw ex;
56             }

 

  關注第18行的  invokeBeanFactoryPostProcessors(beanFactory); ,這一步會執行實現了 BeanFactoryPostProcessor 接口的類;這個接口是Spring初始化bean時對外暴露的入口,它可以修改bean工廠內所有的beandefinition(未實例化)數據,可以隨心所欲的修改屬性。追蹤代碼,可以看到實際執行的是 PostProcessorRegistrationDelegate  的  invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)  方法。這個方法比較復雜,分為了好幾部分去處理,截取其中我們關心的部分即可(其實還包含了優先級、屬性等類似處理過程)。

 1         // Do not initialize FactoryBeans here: We need to leave all regular beans
 2             // uninitialized to let the bean factory post-processors apply to them!
 3             // Separate between BeanDefinitionRegistryPostProcessors that implement
 4             // PriorityOrdered, Ordered, and the rest.
 5             List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
 6 
 7             // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
 8             String[] postProcessorNames =
 9                     beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
10             for (String ppName : postProcessorNames) {
11                 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
12                     currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
13                     processedBeans.add(ppName);
14                 }
15             }
16             sortPostProcessors(currentRegistryProcessors, beanFactory);
17             registryProcessors.addAll(currentRegistryProcessors);
18             invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
19             currentRegistryProcessors.clear();

  第18行的代碼是我們要關注的,在這一步時,currentRegistryProcessors只有一個元素: ConfigurationClassPostProcessor ; 下一步執行的具體代碼就是 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 方法;然后執行到 processConfigBeanDefinitions(BeanDefinitionRegistry registry) 方法,這個方法內容比較多,也比較復雜,這里也只貼出我們需要關注的幾行代碼:

 1 // Parse each @Configuration class
 2         ConfigurationClassParser parser = new ConfigurationClassParser(
 3                 this.metadataReaderFactory, this.problemReporter, this.environment,
 4                 this.resourceLoader, this.componentScanBeanNameGenerator, registry);
 5 
 6         Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
 7         Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
 8         do {
 9             parser.parse(candidates);
10             parser.validate();

  這里new了一個 ConfigurationClassParser 實例,並且執行了它的  parse(Set<BeanDefinitionHolder> configCandidates)  方法, 注意這里傳入的參數雖然是一個Set,但實際只有我們的啟動類一個元素。這個方法的代碼如下:

 

 1 public void parse(Set<BeanDefinitionHolder> configCandidates) {
 2         for (BeanDefinitionHolder holder : configCandidates) {
 3             BeanDefinition bd = holder.getBeanDefinition();
 4             try {
 5                 if (bd instanceof AnnotatedBeanDefinition) {
 6                     parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
 7                 }
 8                 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
 9                     parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
10                 }
11                 else {
12                     parse(bd.getBeanClassName(), holder.getBeanName());
13                 }
14             }
15             catch (BeanDefinitionStoreException ex) {
16                 throw ex;
17             }
18             catch (Throwable ex) {
19                 throw new BeanDefinitionStoreException(
20                         "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
21             }
22         }
23 
24         this.deferredImportSelectorHandler.process();
25     }

  在這個方法里,實際執行了第6行的代碼。這里不是我們的目標,先不關注;注意第24行的代碼,好像和我們看到了文章開頭提到的 AutoConfigurationImportSelector 有點關聯了,沒錯,這里就是實際加載自動配置類的地方。在這個process方法里,可以看到這里的importSelector元素就是  AutoConfigurationImportSelector ,到了這里我們有理由推測,SpringBoot是查找所有由 AutoConfigurationImportSelector 修飾的類來加載自動配置類的。繼續往下追蹤代碼,關注 getImports 方法:

 1 /**
 2          * Return the imports defined by the group.
 3          * @return each import with its associated configuration class
 4          */
 5         public Iterable<Group.Entry> getImports() {
 6             for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
 7                 this.group.process(deferredImport.getConfigurationClass().getMetadata(),
 8                         deferredImport.getImportSelector());
 9             }
10             return this.group.selectImports();
11         }

  關注第7行內的process方法,點進去會發現這是 DeferredImportSelector 類的內部接口 Group 的接口方法,它的實現類有兩個: AutoConfigurationImportSelector的內部類AutoConfigurationGroup  和  ConfigurationClassParser的內部類DefaultDeferredImportSelectorGroup ;這里執行到的代碼是 AutoConfigurationGroup ,代碼如下:

 1 @Override
 2         public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
 3             Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
 4                     () -> String.format("Only %s implementations are supported, got %s",
 5                             AutoConfigurationImportSelector.class.getSimpleName(),
 6                             deferredImportSelector.getClass().getName()));
 7             AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
 8  .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
 9             this.autoConfigurationEntries.add(autoConfigurationEntry);
10             for (String importClassName : autoConfigurationEntry.getConfigurations()) {
11                 this.entries.putIfAbsent(importClassName, annotationMetadata);
12             }
13         }

  可以看到,這里傳入的參數必須是 AutoConfigurationImportSelector 實例,否則會拋出異常,所以EnableAutoConfiguration  注解類必須由  @Import(AutoConfigurationImportSelector.class)  修飾;進入第8行的 getAutoConfigurationEntry 方法:

 1 /**
 2      * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 3      * of the importing {@link Configuration @Configuration} class.
 4      * @param autoConfigurationMetadata the auto-configuration metadata
 5      * @param annotationMetadata the annotation metadata of the configuration class
 6      * @return the auto-configurations that should be imported
 7      */
 8     protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
 9             AnnotationMetadata annotationMetadata) {
10         if (!isEnabled(annotationMetadata)) {
11             return EMPTY_ENTRY;
12         }
13         AnnotationAttributes attributes = getAttributes(annotationMetadata);
14         List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
15         configurations = removeDuplicates(configurations);
16         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
17         checkExcludedClasses(configurations, exclusions);
18         configurations.removeAll(exclusions);
19         configurations = filter(configurations, autoConfigurationMetadata);
20         fireAutoConfigurationImportEvents(configurations, exclusions);
21         return new AutoConfigurationEntry(configurations, exclusions);
22     }

  從方法名可以看到這里有加載配置類候選的邏輯,有過濾掉不需要加載的邏輯。我們這關注加載配置候選的邏輯,關於過濾(即條件加載機制)可以參考這篇文章 https://blog.csdn.net/weixin_33915554/article/details/89702088 ; 進入第14行的 getCandidateConfigurations 方法,如果看過SpringBoot源碼的同學,就會有一種熟悉的感覺了:

 1 /**
 2      * Return the auto-configuration class names that should be considered. By default
 3      * this method will load candidates using {@link SpringFactoriesLoader} with
 4      * {@link #getSpringFactoriesLoaderFactoryClass()}.
 5      * @param metadata the source metadata
 6      * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
 7      * attributes}
 8      * @return a list of candidate configurations
 9      */
10     protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
11         List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
12                 getBeanClassLoader());
13         Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
14                 + "are using a custom packaging, make sure that file is correct.");
15         return configurations;
16     }

  這里的 SpringFactoriesLoader.loadFactoryNames 方法,就是根據類名從spring.factories中查找對應的實現類名。我們還可以看一下 getSpringFactoriesLoaderFactoryClass 的代碼:

1 /**
2      * Return the class used by {@link SpringFactoriesLoader} to load configuration
3      * candidates.
4      * @return the factory class
5      */
6     protected Class<?> getSpringFactoriesLoaderFactoryClass() {
7         return EnableAutoConfiguration.class;
8     }

  沒錯,返回的就是 EnableAutoConfiguration.class 。好了,現在我們真實的從源碼看到了一些文章說的:EnableAutoConfiguration  注解類由  @Import(AutoConfigurationImportSelector.class)  修飾, SpringBoot從spring.facories中加載自動配置類的說法。

 

 

  到這里為止,我們已經知道了SpringBoot獲取自動配置類全限定名的過程。回到 ConfigurationClassParser.DeferredImportSelectorGroupingHandler的processGroupImports 方法,繼續看拿到所有的自動配置類名后的操作,也就是forEach里面的內容,它解決一些循環依賴的問題,最終將所有的自動配置類信息都放到了 configurationClasses  中。 ConfigurationClassParser 的parse方法執行完畢后,回到 ConfigurationClassPostProcessor的processConfigBeanDefinitions 方法,繼續看下面一部分代碼。

 1 // Parse each @Configuration class
 2         ConfigurationClassParser parser = new ConfigurationClassParser(
 3                 this.metadataReaderFactory, this.problemReporter, this.environment,
 4                 this.resourceLoader, this.componentScanBeanNameGenerator, registry);
 5 
 6         Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
 7         Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
 8         do {
 9             parser.parse(candidates);
10             parser.validate();
11 
12             Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
13             configClasses.removeAll(alreadyParsed);
14 
15             // Read the model and create bean definitions based on its content
16             if (this.reader == null) {
17                 this.reader = new ConfigurationClassBeanDefinitionReader(
18                         registry, this.sourceExtractor, this.resourceLoader, this.environment,
19                         this.importBeanNameGenerator, parser.getImportRegistry());
20             }
21             this.reader.loadBeanDefinitions(configClasses);
22             alreadyParsed.addAll(configClasses);

  第21行的 ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitions 方法,將自動配置類解析成beanDefination,注冊到 registry 中;看一下代碼:

 1 /**
 2      * Read a particular {@link ConfigurationClass}, registering bean definitions
 3      * for the class itself and all of its {@link Bean} methods.
 4      */
 5     private void loadBeanDefinitionsForConfigurationClass(
 6             ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
 7 
 8         if (trackedConditionEvaluator.shouldSkip(configClass)) {
 9             String beanName = configClass.getBeanName();
10             if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
11                 this.registry.removeBeanDefinition(beanName);
12             }
13             this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
14             return;
15         }
16 
17         if (configClass.isImported()) {
18             registerBeanDefinitionForImportedConfigurationClass(configClass);
19         }
20         for (BeanMethod beanMethod : configClass.getBeanMethods()) {
21             loadBeanDefinitionsForBeanMethod(beanMethod);
22         }
23 
24         loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
25         loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
26     }

  自動配置類走的是第18行的邏輯,后續的功能主要是一些裝配BeanDefination的工作,這里不再詳述。

  

  到這里為止,SpringBoot加載自動配置類的邏輯結束。

 


免責聲明!

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



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