淺析SpringBoot自動裝配的實現
背景
眾所周知,如下即可啟動一個最簡單的Spring應用。查看@SpringBootApplication
註解的源碼,發現這個註解上有一個重要的註解@EnableAutoConfiguration
,而這個註解就是SpringBoot實現自動裝配的基礎
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
@EnableAutoConfiguration
EnableAutoConfiguration
註解上通過@Import
引入瞭兩個類,org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
及org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar
。通過@Import
標註的類,會在解析@Import
所在的配置類時,將標註類引入容器解析,並進行註冊。
有眾多的組件都是通過在配置類上加@EnableAutoConfiguration
註解將組件引入的
ImportBeanDefinitionRegistrar
實現瞭org.springframework.context.annotation.ImportBeanDefinitionRegistrar
及org.springframework.boot.context.annotation.DeterminableImports
AutoConfigurationImportSelector
實現瞭org.springframework.context.annotation.DeferredImportSelector
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { .... } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { ... }
解析
起始
通過BeanFactoryPostProcessor
對需要註冊的Bean進行解析。即org.springframework.context.support.AbstractApplicationContext#refresh
,在AbstractApplicationContext#invokeBeanFactoryPostProcessors
方法調用時,就開始瞭對服務配置bean的解析,為對象的生成做準備
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { ... try { ... invokeBeanFactoryPostProcessors(beanFactory); } catch (BeansException ex) { finally { } }
具體解析
調用org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
,通過獲取到的BeanFactoryPostProcessor
實現類對各種配置類進行解析,具體的BeanFactoryPostProcessor
解析後面我們在具體分析。
這裡有一個很重要的類org.springframework.context.annotation.ConfigurationClassPostProcessor
,首先會調用postProcessBeanDefinitionRegistry
方法
// ConfigurationClassPostProcessor類部門源碼 /** * Derive further bean definitions from the configuration classes in the registry. */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { "postProcessBeanFactory already called on this post-processor against " + registry); this.registriesPostProcessed.add(registryId); // 處理配置類 processConfigBeanDefinitions(registry); } * Build and validate a configuration model based on the registry of * {@link Configuration} classes. public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { ... // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // configCandidates為待解析的Configuration類,如配置瞭@SpringBootApplication的類 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse"); // 開始解析 parser.parse(candidates); parser.validate(); ... while (!candidates.isEmpty());
通過源碼可知,具體的解析操作是在org.springframework.context.annotation.ConfigurationClassParser
類中
public void parse(Set<BeanDefinitionHolder> configCandidates) { for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { // 將配置類進行解析。以當前配置類為原配置類,解析@PropertySource、@ComponentScan、@Import、@ImportResource、 // @Bean等標註的類或方法,生成對應的 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); else { parse(bd.getBeanClassName(), holder.getBeanName()); } ... } // 解析通過@Import引入的配置類,自動配置類的解析也在於此 this.deferredImportSelectorHandler.process(); } public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { Predicate<String> exclusionFilter = grouping.getCandidateFilter(); // grouping.getImports()方法獲取到瞭所有配置的可用自動配置類,然後遍歷,以配置類原點又開始一輪解析。自動裝配就是在此處 grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { // import的解析 processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (BeanDefinitionStoreException ex) { throw ex; catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); });
通過DeferredImportSelectorGrouping.getImports()
方法解析。在org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
方法中開始瞭autoConfiguration的解析。
/** * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata} * of the importing {@link Configuration @Configuration} class. * @param annotationMetadata the annotation metadata of the configuration class * @return the auto-configurations that should be imported */ protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 解析@EnableAutoConfiguration註解中的屬性exclude、excludeName AnnotationAttributes attributes = getAttributes(annotationMetadata); // 使用SpringFactoriesLoader獲取META-INF/spring.properties中配置的EnableAutoConfiguration實現類,獲取所有配置的自動裝配類 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去重 configurations = removeDuplicates(configurations); // 獲取需要排除的自動裝配類 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); //getConfigurationClassFilter()方法就是獲取spring.factories中配置的AutoConfigurationImportFilter實現類。然後調用filter //法對自動裝配類進行有效性校驗 configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
再繼續看org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.ConfigurationClassFilter#filter
List<String> filter(List<String> configurations) { long startTime = System.nanoTime(); String[] candidates = StringUtils.toStringArray(configurations); boolean skipped = false; for (AutoConfigurationImportFilter filter : this.filters) { // autoConfigurationMetadata為通過META-INF/spring-autoconfigure-metadata.properties配置文件的內容 // 使用filter及autoConfigurationMetadata對candidates進行校驗 boolean[] match = filter.match(candidates, this.autoConfigurationMetadata); for (int i = 0; i < match.length; i++) { if (!match[i]) { candidates[i] = null; skipped = true; } } } if (!skipped) { return configurations; } ... return result; }
再繼續看match方法,org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match
@Override public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory); // 抽象方法,不同的filter進行不同的處理。這裡會獲取每一個自動裝配類的條件判斷情況 ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata); boolean[] match = new boolean[outcomes.length]; for (int i = 0; i < outcomes.length; i++) { match[i] = (outcomes[i] == null || outcomes[i].isMatch()); if (!match[i] && outcomes[i] != null) { logOutcome(autoConfigurationClasses[i], outcomes[i]); if (report != null) { report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]); } } } return match; }
通過match方法,經過多種filter的過濾,返回的就是每一個自動配置類是否可用
結論
SpringBoot
項目有一個子項目org.springframework.boot:spring-boot-autoconfigure:xx
,這個子項目主要就是做自動裝配的。SpringBoot
提前配置瞭眾多已經實現自動配置功能的配置類(org.springframework.boot.autoconfigure.EnableAutoConfiguration
接口的實現類)。當容器啟動的時候,通過SpringFactoriesLoader
將配置類加載進容器中- 啟動中,容器通過
BeanFactoryPostProcessor
接口解析、修改對象的定義。有一個很重要的配置解析實現類org.springframework.context.annotation.ConfigurationClassPostProcessor
,用來解析項目中標註@Configuration
的類 - 在進行配置類解析時(即解析配置瞭
@SpringBootApplication
註解的類),需要經過解析類的@PropertySource
、@ComponentScan
、@Import
、@ImportResource
、@Bean
、接口默認實現、父類等(org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
)。對於自動裝配來說,最重要的就是解析@Import
- 通過
@Import
引入瞭org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
,在進行解析@Import
引入的配置類時,org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
獲取到所有配置的自動裝配類(通過META-INF/spring.factories
文件配置EnableAutoConfiguration
實現類),通過org.springframework.context.annotation.Condition
定義過濾器,判斷自動裝配置是否需要自動裝配。默認的過濾器有OnClassCondition
、OnWebApplicationCondition
、OnBeanCondition
,對應常見的condition註解ConditionalOnClass
、ConditionalOnBean
、@ConditionalOnWebApplication
。 - 通過過濾判斷,將需要自動配置的類進行configuration解析,從而將需要配置的類轉換成對應的
BeanDefinition
進行註冊
備註
- SpringBoot將自動裝配類及過濾條件通過配置文件的形式放在瞭
META-INF
目錄下,META-INF/spring.factories
和META-INF/spring-autoconfigure-metadata.properties
- 在
BeanFactoryPostProcessor
進行調用時,有兩種處理。首先是通過BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
解析更多的BeanDefinition
,在這裡就包含瞭所有標註類的掃描解析,自動裝配類的解析,自動裝配類引入類的解析。在進行BeanFactoryPostProcessor#postProcessBeanFactory
調用,進行CGLIB-enhanced配置類。這裡最重要的一個類就是org.springframework.context.annotation.ConfigurationClassPostProcessor
,以下為此類的繼承關系
到此這篇關於SpringBoot是如何做到自動裝配的的文章就介紹到這瞭,更多相關SpringBoot自動裝配內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- SpringBoot原理之自動配置機制詳解
- SpringBoot開發實戰之自動配置
- Springboot @Configuration與自動配置詳解
- SpringBoot自動配置深入探究實現原理
- SpringBoot自動配置原理分析