SpringBoot系列文章簡介

  SpringBoot系列文章簡介
  
  SpringBoot源碼閱讀輔助篇:
  
  Spring IoC容器與應用上下文的設計與實現
  
  SpringBoot啓動流程源碼分析:
  
  SpringBoot啓動流程分析(一):SpringApplication類初始化過程
  
  SpringBoot啓動流程分析(二):SpringApplication的run方法
  
  SpringBoot啓動流程分析(三):SpringApplication的run方法之prepareContext()方法
  
  SpringBoot啓動流程分析(四):IoC容器的初始化過程
  
  SpringBoot啓動流程分析(五):SpringBoot自動裝配原理實現
  
  SpringBoot啓動流程分析(六):IoC容器依賴注入
  
  筆者註釋版Spring Framework與SpringBoot源碼git傳送門:請不要吝嗇小星星
  
  spring-framework-5.0.8.RELEASE
  
  SpringBoot-2.0.4.RELEASE
  
  自定義Starter:
  
  SpringBoot應用篇(一):自定義starter
  
  1、前言
  
  上一篇文章,經過分析refresh()方法中的invokeBeanFactoryPostProcessors()方法,分析了IoC容器的初始化過程,這一節從代碼上以下所示,接上一節ConfigurationClassParser類中的parse()方法,接着分析SpringBoot的自動裝配原理。
  
  複製代碼
  
  1 // ConfigurationClassParser類
  
  2 public void parse(Set<BeanDefinitionHolder> configCandidates) {
  
  3     this.deferredImportSelectors = new LinkedList<>();
  
  4     for (BeanDefinitionHolder holder : configCandidates) {
  
  5         BeanDefinition bd = holder.getBeanDefinition();
  
  6         try {
  
  7             // 若是是SpringBoot項目進來的,bd其實就是前面主類封裝成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的實現類)
  
  8             if (bd instanceof AnnotatedBeanDefinition) {
  
  9                 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
  
  10             } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
  
  11                 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
  
  12             } else {
  
  13                 parse(bd.getBeanClassName(), holder.getBeanName());
  
  14             }
  
  15         } catch (BeanDefinitionStoreException ex) {
  
  16             throw ex;
  
  17         } catch (Throwable ex) {
  
  18             throw new BeanDefinitionStoreException(
  
  19                     "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
  
  20         }
  
  21     }
  
  22     // 加載默認的配置---》(對springboot項目來講這裏就是自動裝配的入口了)
  
  23     processDeferredImportSelectors();
  
  24 }
  
  複製代碼
  
  2、SpringBoot自動裝配原理。
  
  2.一、@SpringBootApplication註解
  
  對這個註解詳細你們必定很是熟悉了。再來好好看看這個註解。
  
  複製代碼
  
  1 @Target(ElementType.TYPE)
  
  2 @Retention(RetentionPolicy.RUNTIME)
  
  3 @Documented
  
  4 @Inherited
  
  5 @SpringBootConfiguration
  
  6 @EnableAutoConfiguration
  
  7 @ComponentScan(excludeFilters = {
  
  8         @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  
  9         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  
  10 public @interface SpringBootApplication {
  
  11     ...
  
  12 }
  
  複製代碼
  
  接着看@EnableAutoConfiguration
  
  複製代碼
  
  1 @Target(ElementType.TYPE)
  
  2 @Retention(RetentionPolicy.RUNTIME)
  
  3 @Documented
  
  4 @Inherited
  
  5 @AutoConfigurationPackage
  
  6 @Import(AutoConfigurationImportSelector.class)
  
  7 public @interface EnableAutoConfiguration {
  
  8     ...
  
  9 }
  
  複製代碼
  
  OK,看到@Import(AutoConfigurationImportSelector.class)導入了一個重要的類AutoConfigurationImportSelector。
  
  2.二、AutoConfigurationImportSelector
  
  複製代碼
  
  1 // AutoConfigurationImportSelector類
  
  2 //自動裝配
  
  3 @Override
  
  4 public String[www.rmutk.net] selectImports(AnnotationMetadata annotationMetadata) {
  
  5     if (!isEnabled(annotationMetadata)) {
  
  6         return NO_IMPORTS;
  
  7     }
  
  8     AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
  
  9             .loadMetadata(this.beanClassLoader);
  
  10     AnnotationAttributes attributes = getAttributes(annotationMetadata);
  
  11     //獲取全部的自動配置類(META-INF/spring.factories中配置的key爲org.springframework.boot.autoconfigure.EnableAutoConfiguration的類)
  
  12     List<String> configurations = getCandidateConfigurations(annotationMetadata,
  
  13             attributes);
  
  14     configurations = removeDuplicates(configurations);
  
  15     //須要排除的自動裝配類(springboot的主類上 @SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})指定的排除的自動裝配類)
  
  16     Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  
  17     checkExcludedClasses(configurations,www.chaoyuL.com  exclusions);
  
  18     //將須要排除的類從 configurations remove掉
  
  19     configurations.removeAll(exclusions);
  
  20     configurations = filter(configurations, autoConfigurationMetadata);
  
  21     fireAutoConfigurationImportEvents(configurations, exclusions);
  
  22     return StringUtils.toStringArray(configurations);
  
  23 }
  
  複製代碼
  
  至於怎麼從章節一中提到的ConfigurationClassParser類中的parse()===>processDeferredImportSelectors()==>AutoConfigurationImportSelector#selectImports(),篇幅有限不作過多介紹。
  
  List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  
  咱們來看一下getCandidateConfigurations(www.oushengyul.com)方法是怎麼拿到這些自動配置類的。
  
  複製代碼
  
  // AutoConfigurationImportSelector類
  
  1 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
  
  2                                                   AnnotationAttributes attributes) {
  
  3     List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
  
  4             getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
  
  5     Assert.notEmpty(configurations,
  
  6             "No auto configuration classes found in META-INF/spring.factories. If you "
  
  7                     + "are using a custom packaging, make sure that file is correct.");
  
  8     return configurations;
  
  9 }
  
  複製代碼
  
  是否是又看到一個十分熟悉的方法loadFactoryNames(),沒錯,其實咱們在分析SpringBoot啓動流程的第一篇文章的時候,就已經分析了,SpringBoot是如何從META-INF/spring.factories中加載指定key的value的。ok,咱們在這裏再次回顧一遍。
  
  看看loadFactoryNames()方法
  
  // SpringFactoriesLoader類
  
  1 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
  
  2     String factoryClassName = factoryClass.getName();
  
  3     return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList(www.chaoyuepint.com));
  
  4 }
  
  debug,看看要從META-INF/spring.factories中加載的類的key,以下圖所示:org.springframework.boot.autoconfigure.EnableAutoConfiguration
  
  回到selectImports()方法,debug,跳過List<www.oushenggw.com String> configurations = getCandidateConfigurations(annotationMetadata,attributes);看一下configurations
  
  居然有110個,那這些類都在哪裏呢?看spring-boot-autoconfigure(固然在SpringBoot的工程中,也不止這一個依賴包中存在該配置文件)工程下的META-INF/spring.factories,咱們能看到org.springframework.boot.autoconfigure.EnableAutoConfiguration定義了一大堆。
  
  其中還有一個com.demo.starter.config.DemoConfig是我自定義的starter。以下所示,我在測試工程中添加了自定義starter的依賴,因此SpringBoot就能掃描到。
  
  1 <dependency>
  
  2     <groupId>com.demo</groupId>
  
  3     <artifactId>demo-spring-boot-starter<www.muyuyulept.com /artifactId>
  
  4     <version>0.0.1-RELEASE</version>
  
  5 </dependency>
  
    繼續看Set<String> exclusions = getExclusions(annotationMetadata, attributes);方法,該方法是排除主類上@SpringBootApplication註解上排除的自動裝配的類。好比咱們在該註解上排除咱們自定義starter的自動裝配的類,@SpringBootApplication(exclude = {com.demo.starter.config.DemoConfig.class})(固然也能夠用excludeName進行排除),那麼在後面的configurations.removeAll(exclusions);方法中將會刪除咱們的com.demo.starter.config.DemoConfig.class。
  
  configurations = filter(configurations, autoConfigurationMetadata);該行代碼將會過濾掉不須要裝配的類。過濾的邏輯有不少,好比咱們經常使用的@ConditionXXX註解。以下所示:
  
  複製代碼
  
  1 @ConditionalOnBean:容器中有指定的Bean
  
  2 @ConditionalOnClass:當類路徑下有指定的類
  
  3 @ConditionalOnExpression:基於SpEL表達式做爲判斷條件
  
  4 @ConditionalOnJava:基於JVM版本做爲判斷條件
  
  5 @ConditionalOnJndi:在JNDI存在的條件下查找指定的位置
  
  6 @ConditionalOnMissingBean:當容器中沒有指定Bean的狀況下
  
  7 @ConditionalOnMissingClass:當類路徑下沒有指定的類
  
  8 @ConditionalOnNotWebApplication:當前項目不是Web項目
  
  9 @ConditionalOnProperty:配置文件中指定的屬性是否有指定的值
  
  10 @ConditionalOnResource:類路徑下是否有指定的資源
  
  11 @ConditionalOnSingleCandidate:當指定Bean在容器中只有一個,或者雖然有多個可是指定首選Bean
  
  12 @ConditionalOnWebApplication:當前項目是Web項目的條件下
  
  複製代碼
  
    至於如何將這些類解析成BeanDefinition並註冊進beanDefinition中的,和上一節講的過程是同樣的,再也不贅述了。
  
  debug,跳過refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory);方法。以下圖所示,最終在beanFactory的BeanDefinitionMap中找到了自定義starter中的自動裝配的類。
  
  綜合本文和上一篇博文咱們詳細的梳理了IoC容器的初始化過程,到此IoC容器的初始化過程就結束了。
  
  原創不易,轉載請註明出處。
  
  若有錯誤的地方還請留言指正。
  
  小小的碼農,大大的夢想git

相關文章
相關標籤/搜索