天天看點

一文學會Spring的@Configuration配置類解析

作者:Java小蟲
一文學會Spring的@Configuration配置類解析

為了給組裡的實習生妹妹講如何自定義Springboot的Starter,

我覺得需要讓她先知道Springboot的自動裝配機制,

但又感覺是不是得先讓她了解Spring的SPI機制,

最後又覺得還有必要說明一下一個@Configuration配置類在Spring中如何被解析,這又會引出對ConfigurationClassPostProcessor的講解,

完了,收不住了,到處都是知識盲區。

知識盲區腦圖如下。

一文學會Spring的@Configuration配置類解析

前言

本篇文章将詳細分析Spring中如何加載并解析由@Configuration注解修飾的配置類。

@Configuration注解是Spring中特别重要且知名的注解,大家都知道怎麼使用(如果還不知道,那你一定是沒看超詳細總結Spring的配置注解),作用就是可以向容器注冊bean,并且就像“每一個成功的男人背後都有一個優秀的女人在支援”,@Configuration注解的背後也有一個功能強大的類在支撐,那就是ConfigurationClassPostProcessor,是以本文的重點就是ConfigurationClassPostProcessor如何解析由@Configuration注解修飾的配置類。

本文的一個思維導圖如下所示。

一文學會Spring的@Configuration配置類解析

最後,請務必知道Spring的BeanDefinition是什麼,不能隻知道Spring有bean但不知道bean的前生BeanDefinition,如果真不知道,可以看一下超詳細分析Spring的BeanDefinition

Springboot版本:2.4.1

Spring版本:5.3.2

正文

一. 示例工程搭建

在進行ConfigurationClassPostProcessor源碼分析前,需要搭建示例工程來示範ConfigurationClassPostProcessor的執行邏輯。

定義如下幾個業務類。

public class MyController {}

@Service
public class MyService {}

public class MyDao {}
           

配置類如下所示。

@ComponentScan
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class,
            MyImportSelector.class})
public class MyConfig {

    @Bean
    public MyDao myDao() {
        return new MyDao();
    }

}
           

MyImportBeanDefinitionRegistrar實作了ImportBeanDefinitionRegistrar接口,定義如下。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = new RootBeanDefinition(MyController.class);
        registry.registerBeanDefinition("myController", beanDefinition);
    }

}
           

MyImportSelector實作了ImportSelector接口,定義如下。

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {
                "com.learn.beanfactory.postprocessor.further.MyFurtherConfig"
        };
    }

}
           

上面中的MyFurtherConfig也是一個配置類,定義如下。

@Configuration
public class MyFurtherConfig {

    @Bean
    public MyFurtherService myFurtherService() {
        return new MyFurtherService();
    }

}
           

MyFurtherService是一個業務類,定義如下。

public class MyFurtherService {}
           

測試類定義如下。

public class MyTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MyConfig.class);
    }

}
           

示例工程目錄結構如下所示。

一文學會Spring的@Configuration配置類解析

二. BeanFactoryPostProcessor

Spring中提供了大量的擴充點,而ConfigurationClassPostProcessor的擴充點類型叫做bean工廠後置處理器,也就是BeanFactoryPostProcessor。

先觀察一下BeanFactoryPostProcessor的類圖,如下所示。

一文學會Spring的@Configuration配置類解析

BeanFactoryPostProcessor接口允許其實作類修改系統資料庫中的BeanDefinition,這裡的系統資料庫指ConfigurableListableBeanFactory接口的實作類,通常為DefaultListableBeanFactory(如果不知道這是什麼,可以就了解為我們常說的Spring容器)。

Spring啟動并初始化容器階段,會調用到AbstractApplicationContext#refresh方法來重新整理容器,在AbstractApplicationContext#refresh方法中,會先執行invokeBeanFactoryPostProcessors() 方法來執行每一個BeanFactoryPostProcessor的邏輯,并且這一步在初始化bean之前完成。

refresh() 方法簡要表示如下。

public void refresh() throws BeansException, IllegalStateException {
        
    ......

    // 執行bean工廠後置處理器邏輯
    invokeBeanFactoryPostProcessors(beanFactory);

    ......

    // 添加bean後置處理器到容器
    registerBeanPostProcessors(beanFactory);

    ......
    
    // 初始化bean
    finishBeanFactoryInitialization(beanFactory);

    ......

}
           

既然BeanFactoryPostProcessor接口允許其實作類修改系統資料庫中的BeanDefinition,但是在Spring啟動并且初始化容器的最開始階段,Spring容器持有的系統資料庫DefaultListableBeanFactory中是沒有BeanDefinition的,那麼肯定有一個時機存在一個類完成了将BeanDefinition加載到系統資料庫中,時機就是invokeBeanFactoryPostProcessors() 方法的調用,類則是ConfigurationClassPostProcessor。

ConfigurationClassPostProcessor用于解析由@Configuration注解修飾的類(稱為配置類),其類圖如下所示。

一文學會Spring的@Configuration配置類解析

ConfigurationClassPostProcessor實作了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口繼承于BeanFactoryPostProcessor接口,是以ConfigurationClassPostProcessor有如下兩層身份。

  1. ConfigurationClassPostProcessor是一個bean工廠後置處理器;
  2. ConfigurationClassPostProcessor具備向容器注冊BeanDefinition的功能。

是以在Spring啟動并初始化容器的最開始階段,Spring會執行ConfigurationClassPostProcessor的邏輯來解析配置類,并且Spring還會通過流程控制,讓BeanDefinitionRegistryPostProcessor的執行先于非BeanDefinitionRegistryPostProcessor的bean工廠後置處理器。

小結

  1. BeanFactoryPostProcessor叫做bean工廠後置處理器,ConfigurationClassPostProcessor是由Spring提供的可以實作向容器注冊BeanDefinition功能的bean工廠後置處理器;
  2. ConfigurationClassPostProcessor的執行先于非BeanDefinitionRegistryPostProcessor的bean工廠後置處理器;
  3. ConfigurationClassPostProcessor向容器注冊BeanDefinition是通過解析配置類來實作。

三. BeanFactoryPostProcessor執行順序

前文已知ConfigurationClassPostProcessor就是一個Spring内置的BeanFactoryPostProcessor,但是Spring中有那麼多的BeanFactoryPostProcessor,包括Spring内置提供的,使用者自定義的,這些BeanFactoryPostProcessor之間的執行肯定有一個先後,本節将對BeanFactoryPostProcessor執行順序進行一個分析。

在示例工程中,建立AnnotationConfigApplicationContext容器時傳入了MyConfig配置類的Class對象,稱建立容器時傳入的配置類為初始配置類,下面再給出初始配置類MyConfig的定義,如下所示。

@ComponentScan
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class,
            MyImportSelector.class})
public class MyConfig {

    @Bean
    public MyDao myDao() {
        return new MyDao();
    }

}
           

在MyConfig的定義中,使用了如下注解。

  1. @ComponentScan注解。預設将配置類所在包及其子包下所有由@Component,@Controller,@Service,@Repository,@Configuration注解修飾的類注冊為容器中的bean;
  2. @Configuration注解。表示目前類是一個配置類,配置類也會被注冊為容器中的bean;
  3. @Import注解。通過@Import注解可以導入ImportBeanDefinitionRegistrar和ImportSelector的實作類,并通過它們的實作類來向容器注冊bean或配置類;
  4. @Bean注解。在配置類中所有由@Bean注解修飾的方法的傳回值,會被注冊為容器中的bean。

在配置類中,有兩種方式來向容器注冊bean。

  1. 通過配置類直接向容器注冊bean。例如使用@Bean注解,使用@ComponentScan注解和使用@Import(業務類.class);
  2. 通過配置類向容器注冊配置類進而間接注冊bean。被注冊的配置類也會被解析,進而被注冊的配置類所配置的bean也會被注冊到容器中,Springboot中的自動裝配就是這樣實作的。

上述讨論的通過配置類向容器注冊bean,并且可以使用多種注解和基于多種方式,能夠實作這樣的功能,實際是依賴的ConfigurationClassPostProcessor。在Spring中,new一個容器就會觸發這個容器的初始化邏輯,例如示例工程中AnnotationConfigApplicationContext的構造函數會調用到AbstractApplicationContext的refresh() 方法,在refresh() 方法中會在初始化bean之前調用到invokeBeanFactoryPostProcessors() 方法來執行bean工廠後置處理器的邏輯,invokeBeanFactoryPostProcessors() 方法實作如下所示。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 将執行bean工廠後置處理器的操作委托給PostProcessorRegistrationDelegate
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    if (!IN_NATIVE_IMAGE && beanFactory.getTempClassLoader() == null 
                && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}
           

繼續看PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors() 方法。

public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    // 記錄已經執行邏輯的内置BeanDefinitionRegistryPostProcessor的辨別以防止重複執行
    Set<String> processedBeans = new HashSet<>();

    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        // 儲存自定義的非BeanDefinitionRegistryPostProcessor的bean工廠後置處理器
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        // 儲存執行邏輯的内置BeanDefinitionRegistryPostProcessor
        // 在執行完内置BeanDefinitionRegistryPostProcessor的邏輯後
        // 還需要執行内置BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor的邏輯
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

        // 先執行自定義的BeanDefinitionRegistryPostProcessor的邏輯
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor =
                        (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                // 自定義的是非BeanDefinitionRegistryPostProcessor的bean工廠後置處理器
                // 則儲存到regularPostProcessors集合中,最後會調用這些自定義bean工廠後置處理器的邏輯
                regularPostProcessors.add(postProcessor);
            }
        }

        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

        // 首先,執行實作了PriorityOrdered接口的内置BeanDefinitionRegistryPostProcessor的邏輯
        // 由于ConfigurationClassPostProcessor實作了PriorityOrdered接口,是以其邏輯在這裡就會執行
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                // 把本批次需要執行的内置BeanDefinitionRegistryPostProcessor儲存到currentRegistryProcessors集合中
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                // 把本批次需要執行的内置BeanDefinitionRegistryPostProcessor的辨別儲存到processedBeans集合
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        // 把本批次需要執行的内置BeanDefinitionRegistryPostProcessor全部儲存到registryProcessors集合中
        registryProcessors.addAll(currentRegistryProcessors);
        // 執行本批次符合條件的内置BeanDefinitionRegistryPostProcessor的邏輯
        // ConfigurationClassPostProcessor的邏輯會在這裡被執行
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, 
                registry, beanFactory.getApplicationStartup());
        currentRegistryProcessors.clear();

        // 然後,執行實作了Ordered接口的内置BeanDefinitionRegistryPostProcessor的邏輯
        // 盡管ConfigurationClassPostProcessor也實作了Ordered接口
        // 但是其在上批次中已經執行過邏輯,是以這裡不會重複執行ConfigurationClassPostProcessor的邏輯
        // 依據就是判斷processedBeans中是否儲存有ConfigurationClassPostProcessor的辨別
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, 
                registry, beanFactory.getApplicationStartup());
        currentRegistryProcessors.clear();

        // 最後,執行剩餘的内置BeanDefinitionRegistryPostProcessor的邏輯
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(
                    BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    currentRegistryProcessors.add(beanFactory.getBean(
                            ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, 
                    registry, beanFactory.getApplicationStartup());
            currentRegistryProcessors.clear();
        }

        // 在執行完所有内置BeanDefinitionRegistryPostProcessor的邏輯後
        // 現在需要執行所有内置BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor的邏輯
        // 以及執行所有自定義的BeanFactoryPostProcessor的邏輯
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }

    else {
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }

    String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (processedBeans.contains(ppName)) {

        }
        else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            // 把實作了PriorityOrdered接口的内置BeanFactoryPostProcessor儲存到priorityOrderedPostProcessors集合中
            priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            // 把實作了Ordered接口的内置BeanFactoryPostProcessor儲存到orderedPostProcessorNames集合中
            orderedPostProcessorNames.add(ppName);
        }
        else {
            // 把其餘内置BeanFactoryPostProcessor儲存到nonOrderedPostProcessorNames集合中
            nonOrderedPostProcessorNames.add(ppName);
        }
    }

    // 首先,執行實作了PriorityOrdered接口的内置BeanFactoryPostProcessor
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

    // 然後,執行實作了Ordered接口的内置BeanFactoryPostProcessor
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
    for (String postProcessorName : orderedPostProcessorNames) {
        orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

    // 最後,執行剩餘的内置BeanFactoryPostProcessor
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
    for (String postProcessorName : nonOrderedPostProcessorNames) {
        nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

    beanFactory.clearMetadataCache();
}
           

小結

invokeBeanFactoryPostProcessors() 方法特别長,但是實作的步驟特别清晰,耐得住性子的朋友可以自行對着注釋看一下,耐不住性子的可以直接看小結。

  1. 先執行自定義的BeanDefinitionRegistryPostProcessor的邏輯;
  2. 然後執行實作了PriorityOrdered接口的内置BeanDefinitionRegistryPostProcessor的邏輯(ConfigurationClassPostProcessor的邏輯在這裡被執行);
  3. 然後執行實作了Ordered接口的内置BeanDefinitionRegistryPostProcessor的邏輯;
  4. 然後執行剩餘的内置BeanDefinitionRegistryPostProcessor的邏輯;
  5. 然後執行自定義的BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor的邏輯;
  6. 然後執行内置BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor的邏輯;
  7. 然後執行實作了PriorityOrdered接口的内置BeanFactoryPostProcessor的邏輯;
  8. 然後執行實作了Ordered接口的内置BeanFactoryPostProcessor的邏輯;
  9. 最後執行剩餘的内置BeanFactoryPostProcessor的邏輯。

四. ConfigurationClassPostProcessor執行分析

1. 入口分析

在PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors() 方法中,會調用到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry() 方法,該方法的注釋如下所示。

Derive further bean definitions from the configuration classes in the registry.

直譯過來就是:從系統資料庫中的配置類派生進一步的bean定義。這裡的系統資料庫指的就是容器持有的DefaultListableBeanFactory,系統資料庫中的配置類指的就是建立Spring容器時傳入的配置類即初始配置類,派生進一步的bean定義指的就是将初始配置類直接或間接向容器注冊的bean解析為BeanDefinition并儲存到DefaultListableBeanFactory的beanDefinitionMap中。

ConfigurationClassPostProcessor解析配置類的邏輯在processConfigBeanDefinitions() 方法中,該方法比較長,是以這裡先給出其實作流程圖。

一文學會Spring的@Configuration配置類解析

概括一下就是。

  1. 先将初始配置類的BeanDefinition從系統資料庫(這裡指DefaultListableBeanFactory,後續如果無特殊說明,系統資料庫預設指DefaultListableBeanFactory)的beanDefinitionMap中擷取出來;
  2. 建立ConfigurationClassParser,解析初始配置類的BeanDefinition,即解析@PropertySource,@ComponentScan,@Import,@ImportResource,@Bean等注解并生成ConfigurationClass,最後緩存在ConfigurationClassParser的configurationClasses中;
  3. 建立ConfigurationClassBeanDefinitionReader,解析所有ConfigurationClass,基于ConfigurationClass建立BeanDefinition并緩存到系統資料庫的beanDefinitionMap中。

下面開始分析源碼。

ConfigurationClassPostProcessor#processConfigBeanDefinitions方法源碼如下。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    // 從系統資料庫中把初始配置類對應的BeanDefinition擷取出來
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // 如果未擷取到初始配置類對應的BeanDefinition,則直接傳回
    if (configCandidates.isEmpty()) {
        return;
    }

    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
                    AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }

    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

    // 建立ConfigurationClassParser以解析初始配置類及其引出的其它配置類
    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());
    do {
        StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
        // ConfigurationClassParser開始執行解析
        parser.parse(candidates);
        parser.validate();

        // 将ConfigurationClassParser解析得到的ConfigurationClass拿出來
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // 建立ConfigurationClassBeanDefinitionReader,以基于ConfigurationClass建立BeanDefinition
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // 開始建立BeanDefinition并注冊到系統資料庫中
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

        candidates.clear();
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                            !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                        candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());

    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}
           

在processConfigBeanDefinitions() 方法中,ConfigurationClassPostProcessor将解析初始配置類以得到ConfigurationClass的操作委托給了ConfigurationClassParser,将基于ConfigurationClass建立BeanDefinition并注冊到系統資料庫的操作委托給了ConfigurationClassBeanDefinitionReader,是以下面會對這兩個步驟進行分析。

首先是ConfigurationClassParser解析初始配置類,其parse() 方法如下所示。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                // 初始配置類對應的BeanDefinition為AnnotatedGenericBeanDefinition
                // AnnotatedGenericBeanDefinition實作了AnnotatedBeanDefinition接口
                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());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    
    // 延遲處理DeferredImportSelector
    this.deferredImportSelectorHandler.process();
}
           

由于初始配置類對應的BeanDefinition為AnnotatedGenericBeanDefinition,而AnnotatedGenericBeanDefinition實作了AnnotatedBeanDefinition接口,是以繼續看parse(AnnotationMetadata metadata, String beanName) 方法,如下所示。

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

繼續跟進ConfigurationClassParser#processConfigurationClass方法,如下所示。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) 
                throws IOException {
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                existingClass.mergeImportedBy(configClass);
            }
            return;
        }
        else {
            this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
        // 實際開始處理配置類
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);

    // 将解析得到的ConfigurationClass緩存起來
    this.configurationClasses.put(configClass, configClass);
}
           

對于上面的processConfigurationClass() 方法,有兩點需要注意。

  1. 在解析配置類時,通過配置類引入的bean或配置類,都會在解析之後得到ConfigurationClass,并不是隻有配置類才會得到ConfigurationClass。例如示例工程中的MyService,其是在解析初始配置類MyConfig的@ComponentScan注解時被引入的bean,但是也會得到一個MyService的ConfigurationClass并緩存到configurationClasses中;
  2. 上面的processConfigurationClass() 方法是會被遞歸調用的方法。隻要在解析配置類時通過配置類引入了bean或者配置類,那麼都會為引入的bean或者配置類生成ConfigurationClass然後遞歸的調用回上述processConfigurationClass() 方法。通常,如果是bean對應的ConfigurationClass,在processConfigurationClass() 方法中不會有實際的解析邏輯發生,如果是配置類對應的ConfigurationClass,在processConfigurationClass() 方法中就會有實際的解析邏輯發生。

知道了上述的注意點後,下面以解析示例工程中的初始配置類MyConfig為例,繼續深入分析。

ConfigurationClassParser的doProcessConfigurationClass() 方法如下所示。

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        processMemberClasses(configClass, sourceClass, filter);
    }

    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // 解析@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) {
            // 使用ComponentScanAnnotationParser來解析@ComponentScan注解資訊
            // 解析得到的bean會直接被建立為BeanDefinition并注冊到系統資料庫中
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // 為解析得到的bean建立ConfigurationClass,然後遞歸調用到processConfigurationClass()方法進行解析
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    // 在這裡會基于bean建立ConfigurationClass,然後遞歸調用到processConfigurationClass()方法進行解析
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // 解析@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    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);
        }
    }

    // 解析@Bean注解
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    processInterfaces(configClass, sourceClass);

    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            return sourceClass.getSuperClass();
        }
    }

    return null;
}
           

ConfigurationClassParser的doProcessConfigurationClass() 方法中會依次完成對@ComponentScan注解,@Import注解和@Bean注解的解析。

2. 解析@ComponentScan注解

解析@ComponentScan注解的步驟概括如下。

  1. 将@ComponentScan注解資訊擷取出來;
  2. 調用ComponentScanAnnotationParser基于@ComponentScan注解元資訊擷取到需要注冊到容器中的bean的資訊,并為這些bean生成BeanDefinition并注冊到系統資料庫中;
  3. 基于每個bean對應的BeanDefinition生成ConfigurationClass對象,然後遞歸調用processConfigurationClass() 方法解析bean對應的ConfigurationClass對象。

3. 解析@Import注解

解析@Import注解的邏輯在ConfigurationClassParser#processImports方法中,如下所示。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
        boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
        return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            // importCandidates是通過@Import注解導入的類資訊
            // 或者是通過ImportSelector導入的類資訊
            for (SourceClass candidate : importCandidates) {
                if (candidate.isAssignable(ImportSelector.class)) {
                    // @Import注解導入的類實作了ImportSelector接口
                    // 對應示例工程中的MyImportSelector類
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = ParserStrategyUtils.instantiateClass(
                            candidateClass, ImportSelector.class,
                            this.environment, this.resourceLoader, this.registry);
                    Predicate<String> selectorFilter = selector.getExclusionFilter();
                    if (selectorFilter != null) {
                        exclusionFilter = exclusionFilter.or(selectorFilter);
                    }
                    if (selector instanceof DeferredImportSelector) {
                        // 如果實作了DeferredImportSelector接口,則需要延遲處理
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        // 調用ImportSelector的selectImports()方法擷取ImportSelector導入的類的全限定名
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        // 為ImportSelector導入的類生成SourceClass
                        Collection<SourceClass> importSourceClasses = asSourceClasses(
                                    importClassNames, exclusionFilter);
                        // 遞歸調用processImports()方法處理ImportSelector導入的類
                        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                    }
                }
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // @Import注解導入的類實作了ImportBeanDefinitionRegistrar接口
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                    this.environment, this.resourceLoader, this.registry);
                    // 将ImportBeanDefinitionRegistrar緩存到ConfigurationClass的importBeanDefinitionRegistrars字段中
                    // 後續在解析ConfigurationClass為BeanDefinition的過程中
                    // 會調用到ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法
                    // 向系統資料庫直接注冊BeanDefinition
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                    // @Import注解導入的類即不是ImportSelector也不是ImportBeanDefinitionRegistrar
                    // 則為Import注解導入的類建立ConfigurationClass并遞歸調用processConfigurationClass()方法進行解析
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        }
        finally {
            this.importStack.pop();
        }
    }
}
           

processImports() 方法在處理@Import注解時,将@Import注解導入的類分成了三種類型進行處理。

  1. 實作了ImportSelector接口的類

如果實作了ImportSelector接口的類還同時實作了DeferredImportSelector接口,則将其添加到DeferredImportSelectorHandler中以實作延遲處理DeferredImportSelector。

否則在processImports() 方法中會立即調用ImportSelector的selectImports() 方法擷取ImportSelector導入的類資訊,然後再遞歸調用processImports() 方法來解析ImportSelector導入的類。

在示例工程中,MyImportSelector實作了ImportSelector接口,同時MyImportSelector實作的selectImports() 方法會傳回MyFurtherConfig配置類的全限定名,是以會基于MyFurtherConfig的類資訊建立SourceClass并遞歸調用processImports() 方法進行解析,這裡也是通過配置類引入配置類的一個示例;

  1. 實作了ImportBeanDefinitionRegistrar接口的類

由于ImportBeanDefinitionRegistrar的registerBeanDefinitions() 方法可以直接向系統資料庫注冊BeanDefinition,是以在processImports() 方法中僅會将ImportBeanDefinitionRegistrar添加到ConfigurationClass的importBeanDefinitionRegistrars字段中,後續在解析ConfigurationClass為BeanDefinition的過程中會調用到ImportBeanDefinitionRegistrar的registerBeanDefinitions() 方法向系統資料庫直接注冊BeanDefinition;

  1. 即沒有實作ImportSelector接口也沒有實作ImportBeanDefinitionRegistrar的類

processImports() 方法中會為這種類型的類建立ConfigurationClass并遞歸調用processConfigurationClass() 方法進行解析。

假如通過@Import注解直接導入一個bean,或者示例工程中通過@Import注解導入了MyImportSelector同時又通過MyImportSelector導入了MyFurtherConfig,都會為這些bean或配置類建立ConfigurationClass然後遞歸的調用processConfigurationClass() 方法進行解析。

4. 解析@Bean注解

解析@Bean注解的步驟概括如下。

  1. 将目前配置類中所有由@Bean注解修飾的方法擷取出來,并封裝成BeanMethod對象;
  2. 将BeanMethod對象添加到配置類對應的ConfigurationClass的beanMethods字段中。

解析@Bean注解就是将配置類中的由@Bean注解修飾的方法和配置類對應的ConfigurationClass對象綁定,後續解析ConfigurationClass為BeanDefinition的時候會使用到這些方法。

5. DeferredImportSelector的延遲處理

DeferredImportSelector的類圖,如下所示。

一文學會Spring的@Configuration配置類解析

已知在解析@Import注解時,如果通過@Import注解導入了DeferredImportSelector,那麼會将DeferredImportSelector先緩存到DeferredImportSelectorHandler類中來實作延遲處理DeferredImportSelector的效果,那麼是在哪裡處理的DeferredImportSelector呢。實際是在ConfigurationClassParser#processConfigurationClass中。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                // 初始配置類對應的BeanDefinition為AnnotatedGenericBeanDefinition
                // AnnotatedGenericBeanDefinition實作了AnnotatedBeanDefinition接口
                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());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    
    // 延遲處理DeferredImportSelector
    this.deferredImportSelectorHandler.process();
}
           

對應的流程圖如下。

一文學會Spring的@Configuration配置類解析

6. 解析ConfigurationClass

已知在ConfigurationClassPostProcessor的processConfigBeanDefinitions() 方法中,會先解析配置類并得到配置類和bean對應的ConfigurationClass,然後會再使用ConfigurationClassBeanDefinitionReader來将ConfigurationClass解析為BeanDefinition并注冊到系統資料庫中。

ConfigurationClassBeanDefinitionReader解析ConfigurationClass的入口是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions方法,如下所示。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
           

在loadBeanDefinitions() 方法中周遊每一個ConfigurationClass并調用了loadBeanDefinitionsForConfigurationClass() 方法,繼續看loadBeanDefinitionsForConfigurationClass() 方法,如下所示。

private void loadBeanDefinitionsForConfigurationClass(
        ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }

    if (configClass.isImported()) {
        // 基于ConfigurationClass自身建立BeanDefinition并緩存到系統資料庫中
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        // 基于ConfigurationClass中由@Bean注解修飾的方法建立BeanDefinition并緩存到系統資料庫中
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    // 在這裡執行ConfigurationClass緩存的ImportBeanDefinitionRegistrar的邏輯
    // 即調用ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法
    // 直接向系統資料庫注冊BeanDefinition
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
           

至此,@Configuration配置類向Spring容器注冊的所有bean對應的BeanDefinition就已經全部被加載了。

總結

@Configuration注解是一個很簡單但是又很重要的注解,支撐@Configuration注解運作的類是ConfigurationClassPostProcessor,本篇文章主要是對ConfigurationClassPostProcessor的工作原理進行了分析。

ConfigurationClassPostProcessor是bean工廠後置處理器,同時也是具備注冊BeanDefinition功能(因為實作了BeanDefinitionRegistryPostProcessor接口)的bean工廠後置處理器,是以ConfigurationClassPostProcessor邏輯的執行會先于普通bean工廠後置處理器。

ConfigurationClassPostProcessor向系統資料庫注冊BeanDefinition的步驟分為兩個大的步驟。

  1. 使用ConfigurationClassParser将配置類自身,以及配置類引入的bean和配置類均解析為ConfigurationClass;
  2. 使用ConfigurationClassBeanDefinitionReader将ConfigurationClass解析為BeanDefinition并注冊到系統資料庫中。

整體的時序圖如下所示。

一文學會Spring的@Configuration配置類解析

最後,還想提一下,看如下一個Springboot啟動類。

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
           

由于@SpringBootApplication是一個包含了@Configuration注解的複合注解,是以在Springboot應用中,我們的啟動類就是一個配置類,也就是初始配置類,關于Springboot美好的一切都是由這個初始配置類開始的。

如果覺得本篇文章對你有幫助,求求你點個贊,加個轉發最後再點個關注吧。創作不易,感謝支援!

連結:https://juejin.cn/post/7212240532796391483

繼續閱讀