這些不知道,別說你熟悉 Spring( 二 )


在該背景下出現了 SpringBoot,SpringBoot 可以說是穩住了 Java 的地位 。SpringBoot 提供了自動裝配功能 , 自動裝配簡單來說就是將某種功能(如 web 相關、redis 相關、logging 相關等)打包在一起,統一管理依賴包版本,并且約定好相關功能 Bean 的裝配規則,使用者只需引入一個依賴,通過少量注解或簡單配置就可以使用第三方組件提供的功能了 。
在 SpringBoot 中這類功能組件有一個好聽的名字叫做 starter 。比如 spring-boot-starter-web、spring-boot-starter-data-redis、spring-boot-starter-logging 等 。starter 里會通過 @Configuration + @Bean + @ConditionalOnXXX 等注解定義要注入 Spring 中的 Bean , 然后在 spring.factories 文件中配置為 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的實現,就可以完成自動裝配了 。
具體裝配流程怎么樣的呢?
其實也很簡單,基本都是 Spring 中的知識,沒啥新穎的 。主要依托于@EnableAutoConfiguration 注解,該注解上會 Import 一個 AutoConfigurationImportSelector , 看下繼承關系,該類繼承于 DeferredImportSelector 。

這些不知道,別說你熟悉 Spring

文章插圖
主要方法為 getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {// 1if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);// 2List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);// 3Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 4configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions); }方法解讀
  1. 通過 spring.boot.enableautoconfiguration 配置項判斷是否啟用自動裝配 , 默認為 true
  2. 使用上述說的 SpringFactoriesLoader.loadFactoryNames() 加載所有 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的實現類的全限定類名,借助 HashSet 進行去重
  3. 獲取 @EnableAutoConfiguration 注解上配置的要 exclude 的類,然后排除這些特定類
  4. 通過 @ConditionalOnXXX 進行過濾,滿足條件的類才會留下 , 封裝到 AutoConfigurationEntry 里返回
那 getAutoConfigurationEntry() 方法在哪兒調用呢?
public void refresh() throws BeansException, IllegalStateException {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh(); }以上是 Spring 容器刷新時的幾個關鍵步驟,在步驟二 invokeBeanFactoryPostProcessors() 中會調用所有已經注冊的 BeanFactoryPostProcessor 進行處理 。此處調用也是有順序的 , 優先會調用所有 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(),BeanDefinitionRegistryPostProcessor 是一個特殊的 BeanFactoryPostProcessor,然后再調用所有 BeanFactoryPostProcessor#postProcessBeanFactory() 。
ConfigurationClassPostProcessor 是 BeanDefinitionRegistryPostProcessor 的一個實現類,該類主要用來處理 @Configuration 注解標注的類 。我們用 @Configuration 標注的類會被 ConfigurationClassParser 解析包裝成 ConfigurationClass 對象,然后再調用 ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass() 進行 BeanDefination 的注冊 。
其中 ConfigurationClassParser 解析時會遞歸處理源配置類上的注解(@PropertySource、@ComponentScan、@Import、@ImportResource)、 @Bean 標注的方法、接口上的 default 方法,進行 ConfigurationClass 類的補全填充,同時如果該配置類有父類,同樣會遞歸進行處理 。具體代碼請看 ConfigurationClassParser#doProcessConfigurationClass() 方法

推薦閱讀