SpringBoot 配置類解析


本文首發於 vivo互聯網技術 微信公眾號
鏈接:https://mp.weixin.qq.com/s/NvPO5-FWLiOlrsOf4wLaJA
作者:Li Wanghong

SpringBoot作為Java領域非常流行的開源框架,集成了大量常用的第三方庫配置,Spring Boot應用中這些第三方庫幾乎可以是零配置的開箱即用,大部分的 Spring Boot 應用都只需要非常少量的配置代碼,開發者能夠更加專注於業務邏輯。SpringBoot上手快,但是如果你的項目中業務場景需要一些特殊定制,甚至對源碼進行定制化,那這時候了解原理就變成必需的了,只有充分了解源碼,知道框架底層的工作原理,才能對源碼中原有的機制進行修改 / 擴展等等。本文介紹了SpringBoot如何解析配置類、如何集成第三方配置。

一、基本概念介紹

在SpringBoot中推薦基於Java Config的方式來代替傳統的XML方式去引入Bean,本文就是分析SpringBoot如何解析這些配置類,為容器中注入我們自定義的以及SpringBoot為我們提供的Bean。SpringBoot版本基於2.1.7.RELEASE。

// 通常一個SpringBoot工程會含有這樣一個主配置類,它位於我們項目的根包下,通過啟動這個main方法就可以啟動我們的項目
// 下面我們先分析@SpringBootApplication注解有哪些作用,在第二節中分析run方法,在run方法中會進行配置類的解析
@SpringBootApplication
public class SpringbootApplication {
    public static void main(String[] args) {
       SpringApplication.run(SpringbootApplication.class, args);
    }
}
 
// 點擊@SpringBootApplication進去發現它其實是由三個核心注解構成的,下面分別講解這三個注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
                      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

1、@SpringBootConfiguration注解

// 點進去發現它其實就是一個@Configuration注解,SpringBoot解析到就會知道這是一個配置類,會給容器中引入一些bean
// 一個被@Configuration標注的類,相當於一個applicationContext.xml文件
// @Configuration點進去發現其實就是一個@Component注解
@Configuration
public @interface SpringBootConfiguration {

}
2、@EnableAutoConfiguration注解
// 結合下面@AutoConfigurationPackage注解,發現@EnableAutoConfiguration注解就是通過@Import注解給容器中引入了兩個bean,
// 分別是AutoConfigurationImportSelector和AutoConfigurationPackages.Registrar,通過這兩個類可以給容器中引入更多的類
// 下面先介紹下@Import注解的使用
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
@Import注解

在原生SpringFramework中,裝配組件有下面三種方式

  1. 使用@Component注解,Spring2.5+
  2. 使用配置類@Configuration與@Bean,Spring3.0+
  3. 使用模塊裝配@EnableXXX與@Import,Spring3.1+

如果要注冊較多的Bean,通過1) 2) 兩種方式不太方便,可以通過Spring提供的模塊裝配功能,通過給配置類標注@Enable注解,再在注解上標注@Import注解,即可完成組件裝配的效果,下面通過一個例子講解@EnableXXX和@Import的使用。

// 步驟1) 
// 創建幾個動物的實體類,如Cat、Bird、Chicken、Dog、Duck、Pig、Sheep、Snake、Tiger
// 接下來會通過@Import的各種用法將這些bean注入到容器中
@Data
public class Cat {
    private String name;
}
// 步驟2) 在主配置類上加上我們自定義的@EnableAnimal注解,含義就是通過@EnableAnimal會給容器中導入上述的那些動物Bean
// @EnableAnimal就可以類比我們上面說的@EnableAutoConfiguration注解,兩者功能類似
@SpringBootApplication
@EnableAnimal
public class SpringbootApplication {
    public static void main(String[] args) {
       SpringApplication.run(SpringbootApplication.class, args);
    }
}
// 步驟3) 
// @EnableAnimal如下所示,這是我們自定義的注解,這個注解的核心是是在上面聲明了@Import注解
// @Import注解可以傳入四種類型,普通類、配置類、ImportSelector的實現類、ImportBeanDefinitionRegistar的實現類
// 下面分別講解@Import注解導入四種類型的用法
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({Dog.class, AnimalRegisterConfiguration.class, AnimalImportSelector.class, 
                                                       AnimalImportBeanDefinitionRegistar.class})
public @interface EnableAnimal {
}
// 1) 直接@Import({Dog.class}),則容器啟動完成之后容器中就有Dog這個bean,注意Dog這個類我們並沒有通過@Component方式注入
// 2) @Import ({AnimalRegisterConfiguration.class}),通過這種Import配置類形式也可以導入

@Configuration
public class AnimalRegisterConfiguration {

    @Bean
    public Cat cat(){
        return new Cat("cat");
    }
}
// 3) @Import({AnimalImportSelector.class}),通過返回一個Spring數組的方式,數組中可以方便的指定多個Bean
// 我們上面說的AutoConfigurationImportSelector就是這種方式,SpringBoot給容器中導入MybatisAutoConfiguration
// 等自動配置類就是通過這種方式導入的,第二節配置類解析會講這個
public class AnimalImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {Tiger.class.getName()};
    }
}
// 4) @Import({AnimalImportBeanDefinitionRegistar.class}),通過手動編碼方式向registry注入Bean
// 我們上面說的AutoConfigurationPackages.Registrar就是這種方式

public class AnimalImportBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("snake", new RootBeanDefinition(Snake.class));
    }
}
3、@ComponentScan注解

這個注解就是相當於XML中的<context:component-scan>,它會從定義的掃描包路徑(默認是SpringBoot主配置所在的包及其子包)掃描標識了@Controller、@Service、@Repository、@Component注解的類到Spring容器中。

// 我們可以看到這個@ComponentScan注解上顯示指定了兩個Filter過濾條件,它是SpringBoot提供的一種擴展機制,能讓我們
// 向IOC容器中注冊一些自定義的組件過濾器,以便在包掃描的過程中過濾一些Bean
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
                      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
// 這是TypeExcludeFilter的方法,可以看到它是獲取容器中所有的TypeExcludeFilter,並遍歷它們去過濾一些不想要的Bean
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {
  Collection<typeexcludefilter> delegates
                    = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
  for (TypeExcludeFilter delegate : delegates) {
      if (delegate.match(metadataReader, metadataReaderFactory)) {
    return true;
      }
  }
    }
    return false;
}
// 我們可以自定義一個TypeExcludeFilter,它的功能就是過濾掉User這個bean,不讓它注入到容器中
@Component
public class MyTypeExcludeFilter extends TypeExcludeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return User.class.getName().equals(metadataReader.getClassMetadata().getClassName());
    }
}

@Component
public class User {
    private String name;
}
// 可以看到AutoConfigurationExcludeFilter就是將自動配置類給過濾掉了,因為自動配置類xxxAutoConfiguration是通過
// 上面說的AutoConfigurationImportSelector方式導入的,沒必要導入兩次
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
    }
}

4、@Conditional注解

我們說SpringBoot約定大於配置,它通過一些xxxAutoConfiguration給容器中導入了一些組件,如果你需要但是沒有配置視圖解析器,則SpringBoot就會提供其默認配置的視圖解析器,這樣就簡化了配置。那么如果自己定義了一個視圖解析器,那么到底SpringBoot會往容器中注入哪個呢? 查看下面默認注入的視圖解析器代碼,發現其上面有一個@ConditionalOnMissingBean注解,意思就是若容器中沒有這個則容器會給你注入一個這樣的視圖解析器,若容器中有就不注入了。

// @ConditionalOnMissingBean是通過@Conditional注解和Condition接口的實現類(OnBeanCondition.class)來實現這個效果的
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

我們也可以自己實現一個自定義的條件注解。

// 自定義一個Condition接口的實現類MyCondition,通過其matches方法來判斷是否符合指定條件
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 獲取注解MyConditionAnotation注解上指定value對應的值,如果沒有這個值,則不符合條件
        String[] value = (String[]) metadata.getAnnotationAttributes(MyConditionAnotation.class.getName()).get("value");
        for (String property : value){
            if(StringUtils.isEmpty(context.getEnvironment().getProperty(property))){
                return false;
            }
        }
        return true;
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(MyCondition.class)
public @interface MyConditionAnotation {
    String[] value() default {};
}
// 通過@Component給容器中注入一個A類型Bean,條件是@MyConditionAnotation注解指定的key1、key2對應的值有配置
@Component
@MyConditionAnotation({"key1", "key2"})
public class A {
}

5、SpringFactoriesLoader

類似Java的SPI、Dubbo的SPI機制,SpringBoot也提供了一種機制,它通過讀取META-INF/spring.factories文件(這些文件可能存在於類路徑中的多個jar包中)來加載一些預先配置的類,而這個核心機制來源於SpringFactoriesLoader。spring.factories文件必須采用 properties 格式,其中key是接口或抽象類的全限定名,而value是用逗號分隔的實現類的全限定類名列表。

// 我們先來看下上面講解@Import注解講到的AutoConfigurationImportSelector,我們說通過它就可以導入SpringBoot提供那些
// 自動配置類. 下面是Import注解講到的AutoConfigurationImportSelector的selectImport方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
     // 忽略一些其他代碼
     // 這里的含義就是通過SpringFactoriesLoader去加載META-INF/spring.factories中配置的那些xxxAutoConfiguration
     // 並放入String數組返回
     AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                                                       annotationMetadata);
     return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

// 忽略中間過程調用,后面會分析,接下來就走到這里通過SpringFactoriesLoader去加載自動配置類
protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
     // 具體調用看下面分析
     List<string> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                                    getBeanClassLoader());
     return configurations;
}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
     return EnableAutoConfiguration.class;
}
public static List<string> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    // org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String factoryClassName = factoryClass.getName();
    // 獲得一個Map,map形如<key,List<String>>形式,后面getOrDefault是獲取
    // org.springframework.boot.autoconfigure.EnableAutoConfiguration對應的實現類集合
    // 注意這里面不僅僅是加載自動配置類,也會加載監聽器、初始化器那些容器配置的實現類,這里統一一次加載之后放到緩存中
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

// 帶緩存功能,從各個jar包的META-INF/spring.factories文件中加載實現類,一個key可能包含多個實現
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        // 緩存中有直接返回
        return result;
    }

    try {
        // FACTORIES_RESOURCE_LOCATION值是META-INF/spring.factories
        Enumeration<url> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                                       ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                // 獲取key
                String factoryClassName = ((String) entry.getKey()).trim();
                // 將value逗號分隔,獲得各個具體的實現類
                for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    // 放入result中
                    result.add(factoryClassName, factoryName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        // 返回的result見下圖
        return result;
    }
}
可以看到返回了很多自動配置類,有AopAutoConfiguration、RabbitAutoConfiguration,也有MybatisAutoConfiguraion等(圖中沒有截全)

6、BeanFactoryPostProcessor

  1. BeanFactoryPostProcessor,BeanFactory后置處理器,是針對於BeanFactory的擴展點,即Spring會在BeanFactory初始化之后,beanDefinition都已經loaded,但是bean還未創建前進行調用,可以修改或增加BeanDefinition。
  2. BeanDefinitionRegistryPostProcessor,是BeanFactoryPostProcessor的子接口,是針對於BeanFactory的擴展點,即Spring會在調用BeanFactoryPostProcessor之前調用它。我們下面要重點分析的
  3. ConfigurationClassPostProcessor就是該接口的實現類,SpringBoot就是通過它去解析配置類,封裝成一個個BeanDefinition注入到容器中。
  4. BeanPostProcessor,是針對Bean的擴展點,即Spring會在Bean初始化前后調用方法對Bean進行處理,AOP、依賴注入就是通過BeanPostProcessor實現的。

下面是自定義的一個BeanFactoryPostProcessor和BeanPostProcessor,發現通過BeanFactoryPostProcessor可以往容器中增加新的Bean或者修改原有的Bean定義,通過BeanPostProcessor可以修改已經創建好的Bean的屬性值。

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

        // 往容器中新增BeanDefinition
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(Chicken.class);
        registry.registerBeanDefinition("beanFactoryPostProcessor-Chicken", beanDefinition);

        // 修改容器中原有的BeanDefinition
        BeanDefinition snake = registry.getBeanDefinition("snake");
        snake.setLazyInit(true);
    }
}
@Component
public class CatBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Cat){
            Cat cat = (Cat) bean;
            cat.setName("changeNameCat");
        }
        return bean;
    }
}

二、SpringBoot啟動流程概述 

第一節是SpringBoot解析自動配置類會用到的一些知識點,下面我們來看SpringBoot解析配置類的具體過程。上圖是SpringBoot啟動流程圖,其中在refreshContext的第五步會調用容器的BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法。其中有一個是ConfigurationClassPostProcessor,它是在創建ConfigurableApplicationContext時設置到容器中的,如下所示。

// 圖中說的創建ConfigurableApplicationContext,默認創建的是普通的Servlet Web容器,就是下面這個
// 通過反射創建會走到其默認的構造函數
public AnnotationConfigServletWebServerApplicationContext() {
    // 這里面進去會走到下面代碼
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

// 走到這里
registerAnnotationConfigProcessors(registry, null);

// 走到這里
// 向容器中注入一個ConfigurationClassPostProcessor,它是BeanFactoryPostProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}

// 向容器中注入一個AutowiredAnnotationBeanPostProcessor,它是BeanPostProcessor,用於解決依賴注入的
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    def.setSource(source);
     beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

三、配置類解析

上面說到在refreshContex中的第五步時,會調用容器中的BeanFactoryPostProcessor

的postProcessBeanDefinitionRegistry方法。其中有一個是ConfigurationClassPostProcessor,這是我們解析自動配置類的入口,下面分析其postProcessBeanDefinitionRegistry方法。

1、配置類解析流程概述

@Override  
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {  
    // 刪掉一些非關鍵代碼
    processConfigBeanDefinitions(registry);  
}

下面的processConfigBeanDefinitions方法就是對應上圖中的步驟1、2、3、4、5,其中步驟4和步驟5比較長,單獨拆出來分析。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {  
    List<beandefinitionholder> configCandidates = new ArrayList<>(); 
    // 獲取容器中已注冊的bean名字,見下圖,注意,這里容器中這些BeanDefinition都是容器初始化過程中容器添加進去的
    // 不是我們業務代碼的beanDefinition,這段代碼其實是連貫的,為了注釋圖片方便才分開
    String[] candidateNames = registry.getBeanDefinitionNames();  

    for (String beanName : candidateNames) {  
  // 獲取BeanDefinition
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);  
  // 判斷這個BeanDefinition的configurationClass屬性是不是full或者lite,如果是認為已經處理過了,第一次時默認為空,
  // 走下面分支
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||  
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {  
            // 打印日志記錄下
        } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
      // 1) 下面先分析下這個checkConfigurationClassCandidate方法,這邊看方法名也可以猜到是檢測該類是不是配置類
      // 是配置類的意思就是它會給容器中引入bean,這個方法判斷主要就是看這個類的元信息中有沒有@Configuration注解
      // 有沒有@Component注解、有沒有@ComponentScan、@Import、@ImportResource注解,有沒有@Bean方法
      // 構造一個BeanDefinitionHolder,放入configCandidates中
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));  
        }  
    }  
  
    // Return immediately if no @Configuration classes were found  
    // 上圖中容易默認已經引入了7個BeanDefinition,經過上面檢測發現默認就一個符合條件的配置類,即我們的主配置類
    // 這里面configCandidates就一個,就是SpringBootApplication
    if (configCandidates.isEmpty()) {  
        return;  
    }  
  
    // Sort by previously determined @Order value, if applicable 
    // 排序
    configCandidates.sort((bd1, bd2) -> {  
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());  
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());  
        return Integer.compare(i1, i2);  
    });  
  
    // 刪掉部分代碼
  
    // Parse each @Configuration class  
    // 配置類解析工具
    ConfigurationClassParser parser = new ConfigurationClassParser(  
                                this.metadataReaderFactory, this.problemReporter, this.environment,  
                                this.resourceLoader, this.componentScanBeanNameGenerator, registry);  
    // 待處理集合
    Set<beandefinitionholder> candidates = new LinkedHashSet<>(configCandidates);  
    // 已處理集合
    Set<configurationclass> alreadyParsed = new HashSet<>(configCandidates.size());  
    // 循環處理直到candidates.isEmpty()
    do {  
  // 這邊開始解析,對應步驟4
        parser.parse(candidates);  
        parser.validate();  
        // 取出第四步解析得到的一些configurationClasses集合
        Set<configurationclass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());  
        configClasses.removeAll(alreadyParsed);  
  
        // 刪除一部分代碼
  // 這邊也會去加載BeanDefinition,對應圖中步驟五
        this.reader.loadBeanDefinitions(configClasses);  
        alreadyParsed.addAll(configClasses);  
   }  
   while (!candidates.isEmpty());  
}

2、檢測是否是配置類

在配置類解析流程圖中,第二步,會獲取容器中已經注冊的BeanDefinition,放入candidateNames中,然后依次遍歷這些BeanDefinition,判斷它有沒有被處理過,如果處理過就不管,否則通過checkConfigurationClassCandidate方法去判斷它是不是配置類,判斷方法如下。通過閱讀這段代碼,發現如果一個類上面有@Configuration注解、或者有@Component、@ComponentScan、@Import、@ImportResource注解、或者有@Bean標注的方法,則認為它是一個配置類。默認情況下,走到這里時最終只有一個candidateName符合,它是我們的主配置類,也就是SpringbootApplication這個Bean。

ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory);

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, 
                                                       MetadataReaderFactory metadataReaderFactory) {  
  
    String className = beanDef.getBeanClassName();  
    // 獲取下類名,如果類名為空或者該類為工廠類
    if (className == null || beanDef.getFactoryMethodName() != null) {  
        return false;  
    }  
  
    // 獲取類的元數據信息
    AnnotationMetadata metadata; 
    // 上圖的7個candidateNames中只有一個springbootApplication是AnnotatedBeanDefinition,其余全返回false
    if (beanDef instanceof AnnotatedBeanDefinition &&
                  className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {  
        // Can reuse the pre-parsed metadata from the given BeanDefinition... 
  // springbootApplication走到這里
        metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();  
    }  
    else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {  
        // Check already loaded Class if present...  
        // since we possibly can't even load the class file for this Class.  
  Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();  
        metadata = new StandardAnnotationMetadata(beanClass, true);  
    }  
    else {  
        try {  
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);  
      // 讀取類的元數據信息,這里面包括注解等信息
            metadata = metadataReader.getAnnotationMetadata();  
        }  
        catch (IOException ex) {  
            return false;  
        }  
    }  
  
    // metadata.isAnnotated(Configuration.class.getName()),這個就是判斷類上面有沒有@Configuration注解
    if (isFullConfigurationCandidate(metadata)) {  
  // 如果true的話設置下這個屬性,那么就標記為處理過了
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);  
    } else if (isLiteConfigurationCandidate(metadata)) {  
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);  
    }  
    else {  
  // 其余6個返回false
        return false;  
    }  
  
    // It's a full or lite configuration candidate... Let's determine the order value, if any.  
    Integer order = getOrder(metadata);  
    if (order != null) { 
       // 獲取下類上的@Order信息
       beanDef.setAttribute(ORDER_ATTRIBUTE, order);  
    }  
    return true;  
}
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {  
    // Do not consider an interface or an annotation...  
    if (metadata.isInterface()) {  
        return false;  
    }  
  
    // Any of the typical annotations found?  
    for (String indicator : candidateIndicators) {  
  // 判斷下類上面有沒有這幾個注解
        if (metadata.isAnnotated(indicator)) {  
           return true;  
        }  
    }  
  
    // Finally, let's look for @Bean methods...  
    try {  
        // 判斷有沒有@Bean的方法
        return metadata.hasAnnotatedMethods(Bean.class.getName());  
    }  
    return false;  
  }  
}

private static final Set<string> candidateIndicators = new HashSet<>();  
  
static {  
    candidateIndicators.add(Component.class.getName());  
    candidateIndicators.add(ComponentScan.class.getName());  
    candidateIndicators.add(Import.class.getName());  
    candidateIndicators.add(ImportResource.class.getName());  
}

3、步驟四解析

public void parse(Set<beandefinitionholder> configCandidates) {  
    // 刪除部分代碼,實際執行時這里的configCandidates就一個springBootApplication代表的主配置類
    for (BeanDefinitionHolder holder : configCandidates) {
        // 獲取BeanDefinition
        BeanDefinition bd = holder.getBeanDefinition();  
        // 我們的SpringBootApplication會走到這邊,下面先分析這邊
        parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); 
    }  
    // (**)處這邊也要留意下,這邊會處理DeferredImportSelector,我們前面說的AutoConfigurationImportSelector就是在這邊處理
    // 給容器中導入xxxAutoConfiguration
    this.deferredImportSelectorHandler.process();  
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {  
     processConfigurationClass(new ConfigurationClass(metadata, beanName));  
}

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {  
     // 根據當前類上面的@Conditional注解標注的條件判斷是否要解析這個配置類
     if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {  
        return;  
     }  
     // 以configClass作為key去獲取,第一次來肯定是獲取不到的,走下面邏輯
     ConfigurationClass existingClass = this.configurationClasses.get(configClass);  
     if (existingClass != null) {  
         if (configClass.isImported()) {  
             if (existingClass.isImported()) {  
                 existingClass.mergeImportedBy(configClass);  
             }  
             // Otherwise ignore new imported config class; existing non-imported class overrides it.  
             return;  
         }  
         else {  
             // Explicit bean definition found, probably replacing an import.  
             // Let's remove the old one and go with the new one.  this.configurationClasses.remove(configClass);  
             this.knownSuperclasses.values().removeIf(configClass::equals);  
          }  
      }  
  
      // Recursively process the configuration class and its superclass hierarchy.
      // 這一步其實沒有做啥,重點還是看下一步驟
      SourceClass sourceClass = asSourceClass(configClass);  
      do {  
    // 這里是重點,里面具體分為8大步驟,單獨拿一小節分析
    // b) doProcessConfigurationClass
          sourceClass = doProcessConfigurationClass(configClass, sourceClass);  
      }  
      while (sourceClass != null);  
     // 放入configurationClasses中
      this.configurationClasses.put(configClass, configClass);  
}
// 上面的asSourceClass最終其實就是封裝了一個SourceClass對象
public SourceClass(Object source) {  
    this.source = source;  
    if (source instanceof Class) {  
        this.metadata = new StandardAnnotationMetadata((Class<?>) source, true);  
    }  
    else {  
        this.metadata = ((MetadataReader) source).getAnnotationMetadata();  
    }  
}

下面這個doProcessConfigurationClass具體分為8個小步驟去解析,對應步驟四種的A-H步驟

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  
      throws IOException {  
    // 判斷這個類上面有沒有@Component注解 
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {  
        // Recursively process any member (nested) classes first  
  // 如果有的話,遍歷其內部類,然后也是調用doProcessConfigurationClass遞歸處理
        processMemberClasses(configClass, sourceClass);  
    }  
  
    // Process any @PropertySource annotations
    // 處理PropertySource注解,之前講解屬性配置也分析過,就是將該注解對應的屬性文件加載到Environment中
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(  
                                               sourceClass.getMetadata(), PropertySources.class,  
                                               org.springframework.context.annotation.PropertySource.class)) {  
        if (this.environment instanceof ConfigurableEnvironment) {  
            processPropertySource(propertySource);  
        }  
    }  
  
    // Process any @ComponentScan annotations 
    // 處理@ComponentScan注解,將其指定的包下的bean注冊到框架中
    Set<annotationattributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(  
                                         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);  
    if (!componentScans.isEmpty() &&
             !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {  
        for (AnnotationAttributes componentScan : componentScans) {  
            // The config class is annotated with @ComponentScan -> perform the scan immediately  
            Set<beandefinitionholder> scannedBeanDefinitions =  
                         this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());  
            // Check the set of scanned definitions for any further config classes and parse recursively if needed  
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {  
                 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();  
                 if (bdCand == null) {  
                     bdCand = holder.getBeanDefinition();  
                 }  
                 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {  
                     parse(bdCand.getBeanClassName(), holder.getBeanName());  
                 }  
            }  
        }  
    }  
  
    // Process any @Import annotations  
    // 處理Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), true);  
  
    // Process any @ImportResource annotations  
    // 處理@ImportResource注解,可以通過它來指定xml文件,BeanFactory就會讀取這個xml文件將bean注冊進去
    AnnotationAttributes importResource =  
                           AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);  
    if (importResource != null) {  
        String[] resources = importResource.getStringArray("locations");  
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");  
        for (String resource : resources) {  
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);  
            configClass.addImportedResource(resolvedResource, readerClass);  
        }  
    }  
  
    // Process individual @Bean methods  
    // 處理我們的類中使用@Bean注解的方法,添加到configClass的beanMethod中
    Set<methodmetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);  
    for (MethodMetadata methodMetadata : beanMethods) {  
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));  
    }  
  
    // Process default methods on interfaces  
    // 處理接口的默認方法,遍歷這個類的接口,判斷有沒有使用@Bean注解的非抽象方法,添加到configClass的beanMethod中
    processInterfaces(configClass, sourceClass);  
  
    // Process superclass, if any  
    // 遞歸處理父類,這邊返回父類上層方法會遞歸處理
    if (sourceClass.getMetadata().hasSuperClass()) {  
       // 判斷父類不為null且不在knownSuperclasses中且不以Java開頭
       String superclass = sourceClass.getMetadata().getSuperClassName();  
       if (superclass != null && !superclass.startsWith("java") &&
                                              !this.knownSuperclasses.containsKey(superclass)) {  
           this.knownSuperclasses.put(superclass, configClass);  
           // Superclass found, return its annotation metadata and recurse  
           return sourceClass.getSuperClass();  
       }  
    }  
  
    // No superclass -> processing is complete  
    return null;  
}

(1)處理內部類

if (configClass.getMetadata().isAnnotated(Component.class.getName())) {  
    // Recursively process any member (nested) classes first  
    processMemberClasses(configClass, sourceClass);  
}


 // Register member (nested) classes that happen to be configuration classes themselves.
 private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {  
     Collection<SourceClass> memberClasses = sourceClass.getMemberClasses(); 
     // 判斷是否有內部類,沒有的話直接不處理
     if (!memberClasses.isEmpty()) {  
         List<SourceClass> candidates = new ArrayList<>(memberClasses.size());  
         for (SourceClass memberClass : memberClasses) {  
       // 判斷是否是配置類,判斷也很簡單,之前分析過,判斷類上面有沒有@Configuration注解、@Import、@ImportResource
       // @Component、@ComponentScan以及@Bean標注的方法
             if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&  
                     !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {  
      // 加入到candidates中然后排個序
                  candidates.add(memberClass);  
             }  
         }  
         OrderComparator.sort(candidates);  
         for (SourceClass candidate : candidates) {     
       // 防止A引入防止A引入B,B引入A
             if (this.importStack.contains(configClass)) {  
                this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));  
             }  
             else {  
                this.importStack.push(configClass);  
                try {  
        // 放入棧中並遍歷處理這些配置類,也是遞歸處理,調用之前的doProcessConfigurationClass處理這個配置類
                    processConfigurationClass(candidate.asConfigClass(configClass));  
                }  
                finally {  
                    this.importStack.pop();  
                }  
             }  
         }  
    }  
}

(2)處理@PropertySource注解

 

@SpringBootApplication  
@PropertySource({"demo.properties"})  
public class Springboot2Application {

 

for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(  
                                                sourceClass.getMetadata(), PropertySources.class,  
                                                org.springframework.context.annotation.PropertySource.class)) {  
    if (this.environment instanceof ConfigurableEnvironment) {  
  // 這邊就不進去看了,主要是讀取@PropertySource注解指定的文件,將其封裝成一個屬性集放入到環境中
        processPropertySource(propertySource);  
    }  
}

(3)處理@ComponentScan注解

Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(  
                                       sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);  
if (!componentScans.isEmpty() &&  
          !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {  
    for (AnnotationAttributes componentScan : componentScans) {  
        // The config class is annotated with @ComponentScan -> perform the scan immediately  
  // 下面先分析這個parse方法
        Set<BeanDefinitionHolder> scannedBeanDefinitions =  
                     this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());  
        // Check the set of scanned definitions for any further config classes and parse recursively if needed  
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {  
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();  
            if (bdCand == null) {  
                bdCand = holder.getBeanDefinition();  
            }  
      // 如果是配置類,再遞歸處理
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {  
                parse(bdCand.getBeanClassName(), holder.getBeanName());  
            }  
        }  
    }  
}
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {  
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,  
                           componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);  
  
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");  
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); 
    // 設置一個bean名字生成器,默認就是使用org.springframework.beans.factory.support.BeanNameGenerator
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :  
                                                                      BeanUtils.instantiateClass(generatorClass));  
  
    // 就是默認的
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");  
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {  
        scanner.setScopedProxyMode(scopedProxyMode);  
    }  
    else {  
        Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");  
  // 理解是元數據解析器
        scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));  
    }  
  
    // 設置下掃描的資源模式,是**/*.class
    scanner.setResourcePattern(componentScan.getString("resourcePattern"));  
  
    // 添加IncludeFilter和ExcludeFilter
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {  
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {  
            scanner.addIncludeFilter(typeFilter);  
        }  
    }  
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {  
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {  
            scanner.addExcludeFilter(typeFilter);  
        }  
    }  
    // 設置是否懶加載
    boolean lazyInit = componentScan.getBoolean("lazyInit");  
    if (lazyInit) {  
        scanner.getBeanDefinitionDefaults().setLazyInit(true);  
    }  
  
    // 解析掃描的包路徑加入到basePackages中
    Set<String> basePackages = new LinkedHashSet<>();  
    String[] basePackagesArray = componentScan.getStringArray("basePackages");  
    for (String pkg : basePackagesArray) {  
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),  
                                                 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);  
        Collections.addAll(basePackages, tokenized);  
    }  
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {  
  // 解析basePackageClasses所在的包並加入到basePackages
        basePackages.add(ClassUtils.getPackageName(clazz));  
    }  
     // 如果是空的,將聲明該注解所在的類的包加入到basePackages
    if (basePackages.isEmpty()) {  
  // 通常我們的主配置類是沒有聲明包掃描的路徑的,所以這里會將主配置類所在的包加到這里面
        basePackages.add(ClassUtils.getPackageName(declaringClass));  
    }  
  
    // 添加一個ExcludeFilter,跳過聲明該注解的類
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {  
       @Override  
       protected boolean matchClassName(String className) {  
           return declaringClass.equals(className);  
       }  
    });  
    return scanner.doScan(StringUtils.toStringArray(basePackages));  
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {  
     Assert.notEmpty(basePackages, "At least one base package must be specified");  
     Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();  
     // 遍歷所有的包路徑
     for (String basePackage : basePackages) {  
   // 獲取該包下面所有符合條件的BeanDefinition,然后遍歷處理,下面會分析
         Set<BeanDefinition> candidates = findCandidateComponents(basePackage);  
         for (BeanDefinition candidate : candidates) {  
             ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);  
             candidate.setScope(scopeMetadata.getScopeName());  
       // 通過beanNameGenerator生成beanName
             String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
       // 這兩個if判斷邏輯比較簡單,就是設置一些Lazy、DependsOn屬性
             if (candidate instanceof AbstractBeanDefinition) {  
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);  
             }  
             if (candidate instanceof AnnotatedBeanDefinition) {  
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);  
             }  
       // 這邊是檢查下有沒有之前定義過這個BeanDefinition
             if (checkCandidate(beanName, candidate)) {  
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);  
                definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, 
                                                      definitionHolder, this.registry);  
                // 將該beanDefinition加入到集合中,並注冊到容器中
                beanDefinitions.add(definitionHolder);  
                registerBeanDefinition(definitionHolder, this.registry);  
             }  
        }  
    }  
    return beanDefinitions;  
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {  
     Set<BeanDefinition> candidates = new LinkedHashSet<>();  
     try {  
         // 掃描指定包路徑及其子包下面的class文件,將其封裝成Resource對象
   // classpath*:com/lwh/springboot/**/*.class
   String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
                                          resolveBasePackage(basePackage) + '/' + this.resourcePattern;  
         Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);  
   
         for (Resource resource : resources) {  
             if (resource.isReadable()) {  
                try {  
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);  
                    if (isCandidateComponent(metadataReader)) {  
                         ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);  
                         sbd.setResource(resource);  
                         sbd.setSource(resource);  
                         if (isCandidateComponent(sbd)) {  
                             candidates.add(sbd);  
                         }  
                    }  
                }  
            }  
         }
    }
    return candidates;  
}
// 通過之前設置的幾個filter進行過濾
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {  
    for (TypeFilter tf : this.excludeFilters) {  
        if (tf.match(metadataReader, getMetadataReaderFactory())) {  
            return false;  
        }  
    }  
    for (TypeFilter tf : this.includeFilters) {  
        if (tf.match(metadataReader, getMetadataReaderFactory())) {  
            return isConditionMatch(metadataReader);  
        }  
    }  
    return false;  
}

(4)處理@Import注解

// Process any @Import annotations  
// getImports方法就是去遞歸掃描configClass上面所有的注解,將@Import注解標注的值放入importCandidates中,見下圖
processImports(configClass, sourceClass, getImports(sourceClass), true);
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,  
                                     Collection<SourceClass> importCandidates, boolean checkForCircularImports) {  
  
    if (importCandidates.isEmpty()) {  
        return;  
    }  
  
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {  
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));  
    }  
    else {  
        this.importStack.push(configClass);  
        try {  
            for (SourceClass candidate : importCandidates) {  
    // 依次遍歷判斷類型
    // 其中有一個是這個類型,@Import(AutoConfigurationImportSelector.class)
    // 這個就是自動配置原理,導入xxxAutoConfiguration這些類
                if (candidate.isAssignable(ImportSelector.class)) {  
                    // Candidate class is an ImportSelector -> delegate to it to determine imports  
                    Class<?> candidateClass = candidate.loadClass();  
        // 實例化並調用xxxAware的方法並注入相關屬性
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);  
                    ParserStrategyUtils.invokeAwareMethods(  
                                            selector, this.environment, this.resourceLoader, this.registry); 
        // 它是DeferredImportSelector類型的
                    if (selector instanceof DeferredImportSelector) {  
                  // deferredImportSelectors = new ArrayList<>()
      // 這邊會將兩個參數封裝下加入到deferredImportSelectors中,后面處理
      // 加入到deferredImportSelectors中后,具體的處理是this.deferredImportSelectorHandler.process();
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);  
                    }  
                    else {  
      // 不是的話獲取@Import導入的類名數組
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());  
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); 
      // 然后再遞歸處理
                        processImports(configClass, currentSourceClass, importSourceClasses, false);  
                    }  
                }  
    // @Import(AutoConfigurationPackages.Registrar.class),我們的主配置類上面的注解就是這個類型
    // 這個是用於導入主配置類所在包及其子包下的BeanDefinition
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {  
                     // Candidate class is an ImportBeanDefinitionRegistrar ->  
                     // delegate to it to register additional bean definitions  
                     Class<?> candidateClass = candidate.loadClass();  
                     ImportBeanDefinitionRegistrar registrar =  
                                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);  
                     ParserStrategyUtils.invokeAwareMethods(  
                                               registrar, this.environment, this.resourceLoader, this.registry);  
         // 這邊就是將這兩個參數作為key,value放入了一個map中
               // this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
                     configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());  
                }  
                else {  
                     // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->  
                     // process it as an @Configuration class  
               this.importStack.registerImport(  
                                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());  
         // 當做一個普通類處理,判斷是不是配置類,遞歸處理
                     processConfigurationClass(candidate.asConfigClass(configClass));  
                }  
         }  
      }  
      finally {  
         this.importStack.pop();  
      }  
   }  
}

(5)處理@ImportSource注解

// 這種就是Spring中常用的通過XML形式注入的方式
@SpringBootApplication  
@ImportResource("test.xml")  
public class Springboot2Application {
// Process any @ImportResource annotations
// 可以使用@ImportResource注解指定xml文件,導入BeanDefinition
AnnotationAttributes importResource =  
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);  
if (importResource != null) {  
     String[] resources = importResource.getStringArray("locations");  
     Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");  
     for (String resource : resources) {  
   // 就是test.xml
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);  
         configClass.addImportedResource(resolvedResource, readerClass);  
     }  
}

public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {  
     // 這邊就是放入到了map中,這邊是先統一存放起來,在步驟五的4)在真正進行導入BeanDefinition
     this.importedResources.put(importedResource, readerClass);  
}

private final Map<String, Class<? extends BeanDefinitionReader>> importedResources = new LinkedHashMap<>();

(6)處理@Bean標注的方法

// Process individual @Bean methods  
// 獲取當前類中的Bean方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);  
for (MethodMetadata methodMetadata : beanMethods) {  
     // 這邊也是加入到set中,見下面代碼,也是在步驟五的3)中進行處理
     configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));  
}

private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();

(7)處理默認方法

// 默認方法舉例,主配置類實現這個接口就可以
public interface ConfigurationInterface {

    @Bean
    default Pig pig(){
        return new Pig();
    }
}
 // Process default methods on interfaces  
 processInterfaces(configClass, sourceClass);

 // Register default methods on interfaces implemented by the configuration class.
 // 這邊也是遞歸處理其父接口,判斷父接口中默認方法是不是@Bean方法
 private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {  
     for (SourceClass ifc : sourceClass.getInterfaces()) {  
         Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);  
         for (MethodMetadata methodMetadata : beanMethods) {  
             if (!methodMetadata.isAbstract()) {  
                 // 也是在步驟五的3)中進行處理
                 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));  
             }  
         }  
         processInterfaces(configClass, ifc);  
     }  
}

(8)遞歸處理父類

// Process superclass, if any  
if (sourceClass.getMetadata().hasSuperClass()) {  
    String superclass = sourceClass.getMetadata().getSuperClassName();  
    if (superclass != null && !superclass.startsWith("java") &&  
                              !this.knownSuperclasses.containsKey(superclass)) {  
        this.knownSuperclasses.put(superclass, configClass);  
        // Superclass found, return its annotation metadata and recurse  
        return sourceClass.getSuperClass();  
    }  
}
講到這邊步驟四中的大部分方法已經分析完了,但是還有一個重要步驟沒有分析,就是導入xxxAutoConfiguration這些自動配置類部分。下面分析:
// 在解析@Import注解時,我們的AutoConfigurationImportSelector就是DeferredImportSelector類型的,它的意思是延遲導
// 入. 為啥要延遲導入? 因為AutoConfigurationImportSelector是給容器中導入一些默認的組件,如果容器中已經有這種類型
// 的組件了,那么就不再導入. 因此,它是故意先等SpringBoot容器去解析那些用戶自定義的Bean,最后發現沒有才來導入這
// 些默認的組件
if (selector instanceof DeferredImportSelector) {  
   // deferredImportSelectors = new ArrayList<>()
   // 這邊會將兩個參數封裝下加入到deferredImportSelectors中,后面處理
   // 加入到deferredImportSelectors中后,具體的處理是this.deferredImportSelectorHandler.process();
   this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);  
}
// 步驟四就是從這里開始分析的
public void parse(Set<beandefinitionholder> configCandidates) {  
    // 刪除部分代碼,實際執行時這里的configCandidates就一個springBootApplication代表的主配置類
    for (BeanDefinitionHolder holder : configCandidates) {
        // 獲取BeanDefinition
        BeanDefinition bd = holder.getBeanDefinition();  
        // 我們的SpringBootApplication會走到這邊,下面先分析這邊
        parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); 
    }  
  
    // 給容器中導入xxxAutoConfiguration,我們現在分析這邊
    this.deferredImportSelectorHandler.process();  
}
// 上面方法會走到這里
public void processGroupImports() {  
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {  
   // 下面先分析這個getImports方法
         grouping.getImports().forEach(entry -> {  
             ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());  
             try {  
                 processImports(configurationClass, asSourceClass(configurationClass),  
                 asSourceClasses(entry.getImportClassName()), false);  
             }  
         });  
    }  
}

public Iterable<group.entry> getImports() {  
    // 這邊的deferredImports是通過grouping.add(deferredImport)添加進去的
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {  
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),  
                                                                       deferredImport.getImportSelector());  
    }  
    return this.group.selectImports();  
}

public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { 
     // 下面先分析下這個getAutoConfigurationEntry方法
     AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)  
                     .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);  
     // 加入到這個autoConfigurationEntries中
     this.autoConfigurationEntries.add(autoConfigurationEntry);  
     for (String importClassName : autoConfigurationEntry.getConfigurations()) {  
   // 接着依次遍歷放入到這個map中,Map<string, annotationmetadata> entries = new LinkedHashMap<>()
         this.entries.putIfAbsent(importClassName, annotationMetadata);  
     }  
}
// 這邊就到了AutoConfigurationImportSelector類的方法中
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,  
                                                           AnnotationMetadata annotationMetadata) {  
    // 判斷是否支持自動配置
    if (!isEnabled(annotationMetadata)) {  
        return EMPTY_ENTRY;  
    }  
    // 這個attributes屬性就是上圖中顯示的,用來過濾自動配置類的
    AnnotationAttributes attributes = getAttributes(annotationMetadata);  
    // 下面分析這個方法,這個就是加載容器中的自動配置類
    List<string> configurations = getCandidateConfigurations(annotationMetadata, attributes);  
    // 去除重復的,方法就是放入set再放入list中
    configurations = removeDuplicates(configurations);  
    // 去除掉應該被排除的
    Set<string> exclusions = getExclusions(annotationMetadata, attributes);  
    checkExcludedClasses(configurations, exclusions);  
    configurations.removeAll(exclusions); 
  
    // 通過filter過濾,下面分析,過濾完發現只有22個了
    configurations = filter(configurations, autoConfigurationMetadata);  
    // 發布一個事件,好像沒有做啥關鍵的
    fireAutoConfigurationImportEvents(configurations, exclusions);  
    // 將configurations封裝成AutoConfigurationEntry返回
    return new AutoConfigurationEntry(configurations, exclusions);  
}
protected boolean isEnabled(AnnotationMetadata metadata) {  
    if (getClass() == AutoConfigurationImportSelector.class) {  
  // 判斷有沒有配置這個屬性,沒有的話默認為true,
  // String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
        return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);  
    }  
    return true;  
}
 
protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 
    // getSpringFactoriesLoaderFactoryClass()返回EnableAutoConfiguration.class,那么這邊就是獲取容器中所有的字段配置類
    List<string> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),  
                                                                                getBeanClassLoader());  
    return configurations;  
}
protected List<autoconfigurationimportfilter> getAutoConfigurationImportFilters() {  
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);  
}

 

4、步驟五解析

// 上面分析的是配置類解析的步驟四
parser.parse(candidates);

// 下面來看第五步,步驟四中解析得到的配置類會放在configurationClasses中
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
this.reader.loadBeanDefinitions(configClasses);
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
  loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
private void loadBeanDefinitionsForConfigurationClass(
  ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
     // 判斷是否應該跳過
     if (trackedConditionEvaluator.shouldSkip(configClass)) {
  return;
     }

     // 向容器中注入這個配置類所代表的的BeanDefinition
     if (configClass.isImported()) {
  registerBeanDefinitionForImportedConfigurationClass(configClass);
     }
     // 遍歷這個配置類的所有的Bean方法,注入這些@Bean標注的方法要引入的BeanDefinition
     for (BeanMethod beanMethod : configClass.getBeanMethods()) {
  loadBeanDefinitionsForBeanMethod(beanMethod);
     }

     // 處理該配置類上的@ImportResource指定的配置文件,就是Spring中常用的XML配置方式
     // 這邊會通過解析該XML文件給容器中注入BeanDefinition,SpringBoot中不推薦這種方式,具體就不往下分析了
     loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
   
     // 處理步驟四中D解析@Import注解時獲得的importBeanDefinitionRegistrars,調用其registerBeanDefinitions
     // 方法給容器中注入BeanDefinition,這在前面講解@Import方式時已經講過了
     loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
     registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry));
}

四、總結

至此本文對SpringBoot的配置類解析就基本講完了,簡單回顧一下:

  1. 第一部分介紹了SpringBoot解析配置類時會用到的一些類和注解,講解了它們的使用及原理。
  2. 第二部分介紹了SpringBoot啟動流程,引出了SpringBoot在哪一步解析配置類。
  3. 第三部分介紹了SpringBoot解析配置類的8大步驟。

更多內容敬請關注vivo 互聯網技術微信公眾號

注:轉載文章請先與微信號:Labs2020聯系。


免責聲明!

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



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