SpringBoot自動配置原理


1.什么是自動配置

個人理解SpringBoot的自動配置就是在系統啟動的過程中自動掃描加載starter和自定義的配置類和配置文件中的bean,並且能根據當前環境和條件動態加載bean,達到開箱即用的目的。

2.從注解反向看自動配置

說到自動配置,很多帖子會直接從啟動類的main函數說起,從@SpringBootApplication這個入手,進而找到加載Bean的入口,一般情況下是可以這樣看的。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

SpringBootApplication 這個注解是個復合注解,

@SpringBootConfiguration 這個注解的作用和@Configuration的作用是一致的,聲明當前類為一個配置類。

@ComponentScan 這個注解的作用是聲明Bean掃描的相關信息,掃描路徑和排除,包含關系

@EnableAutoConfiguration 字面意思是開啟自動配置。它本身也是一個復合注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@AutoConfigurationPackage注解其實是向容器導入了一個Bean,
@Import(AutoConfigurationImportSelector.class)向容器導入了AutoConfigurationImportSelector 這個Bean,這個Bean的作用是加載自動配置類
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
//加載自動配置類 AutoConfigurationEntry autoConfigurationEntry
= getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
//獲取候選的配置類名稱,其實是加載包路徑下的META-INF/spring.factories 中的類路徑 List
<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//排重 configurations
= removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions);
//移除要排除的配置類 configurations.removeAll(exclusions);
//篩選滿足條件的配置類,這個地方其實是根據Condition過濾到滿足條件的配置類 configurations
= filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }

到此為止,我們僅僅能看到加載了哪些類,但是每個類中又有很多Bean,每個Bean上又加了很多Condition注解,那這些Bean是如何加載的?這個AutoConfigurationImportSelector是什么時候被觸發的?如果單從注解反向看的話是很難回答的?

3.從啟動流程正向看自動配置

要想真正了解自動配置原理,還是要從啟動流程中下手,啟動過程中有很重要的一步是

refreshContext(context);
它調用的是AbstractApplicationContext的 refresh方法, 啟動有一步是執行BeanFactory的后置處理器
invokeBeanFactoryPostProcessors(beanFactory);
這里面有一段執行 BeanDefinitionRegistryPostProcessor 的代碼,這個是和bean注冊相關的
    // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the bean factory post-processors apply to them!
            // Separate between BeanDefinitionRegistryPostProcessors that implement
            // PriorityOrdered, Ordered, and the rest.
            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.

// 這個地方拿到的是org.springframework.context.annotation.internalConfigurationAnnotationProcessor 這個類, String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors);
        //currentRegistryProcessors中存放的是ConfigurationClassPostProcessor,這個類是自動配置的核心 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear();
invoke 方法調用的org.springframework.context.annotation.ConfigurationClassPostProcessor的processConfigBeanDefinitions方法
String[] candidateNames = registry.getBeanDefinitionNames();

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

從候選bean中選取有@Configuration的類,候選的有下面幾個,其中只有我們自己的主啟動類包含了這個注解

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
conditionTestApplication(主啟動類)
org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory

接下來執行parser,這里面循環調用,將所有@Configuration的配置類都處理了一遍,將使用@Bean注解的方法都提取出來了

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 {
//記載配置類 parser.parse(candidates); parser.validate();         //存放配置類及Bean信息 Set
<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); }
//根據條件注解,加載Bean
this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
parser.parse(candidates); 方法中調用了 this.deferredImportSelectorHandler.process(),而 AutoConfigurationImportSelector 是 DeferredImportSelector的實現類
public void process() {
            List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
            this.deferredImportSelectors = null;
            try {
                if (deferredImports != null) {
                    DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                    deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                    deferredImports.forEach(handler::register);
//進行導入 handler.processGroupImports(); } }
finally { this.deferredImportSelectors = new ArrayList<>(); } }
public void processGroupImports() {
            for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
                grouping.getImports().forEach(entry -> {
                    ConfigurationClass configurationClass = this.configurationClasses.get(
                            entry.getMetadata());
                    try {
                        processImports(configurationClass, asSourceClass(configurationClass),
                                asSourceClasses(entry.getImportClassName()), false);
                    }
                    catch (BeanDefinitionStoreException ex) {
                        throw ex;
                    }
                    catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to process import candidates for configuration class [" +
                                        configurationClass.getMetadata().getClassName() + "]", ex);
                    }
                });
            }
        }
grouping.getImports()的實現是
public Iterable<Group.Entry> getImports() {
            for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
                this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                        deferredImport.getImportSelector());
            }
            return this.group.selectImports();
        }
group.process的調用了AutoConfigurationImportSelector.process 方法,這樣就和前面的從注解看實現的步驟對上了,加載配置類的入口就找到了,並且經過一層過濾
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));
// 這個地方就是執行的導入配置類 AutoConfigurationEntry autoConfigurationEntry
= ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }

再回到processGroupImports() 這個方法很復雜,作用是循環調用所有配置類,將自動配置類中的有 @ComponentScan @Import  @ImportResource 這些注解的類都找到,如果沒有注解作為配置類使用,並且將結果存在在configClass中,這個類中存放的是所有配置類及配置類中產生Bean方法的信息,后面將通過這些信息確定要最終加載哪些Bean。

this.reader.loadBeanDefinitions(configClasses);
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()) {
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
// 將configerClass 轉成BeanDefinition並注冊到BeanFactory
for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }

 ConditionEvaluator.shouldSkip 的作用就是根據判斷Condition注解能否生效

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
        if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
            return false;
        }

        if (phase == null) {
            if (metadata instanceof AnnotationMetadata &&
                    ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
                return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
            }
            return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
        }

        List<Condition> conditions = new ArrayList<>();
        for (String[] conditionClasses : getConditionClasses(metadata)) {
            for (String conditionClass : conditionClasses) {
                Condition condition = getCondition(conditionClass, this.context.getClassLoader());
                conditions.add(condition);
            }
        }

        AnnotationAwareOrderComparator.sort(conditions);

        for (Condition condition : conditions) {
            ConfigurationPhase requiredPhase = null;
            if (condition instanceof ConfigurationCondition) {
                requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
            }
            if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
                return true;
            }
        }

        return false;
    }
這樣整個自動配置的鏈路就串起來了,加載自動配類,使用條件注解判斷哪些Bean需要加載到容器中都可以找到實現。


免責聲明!

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



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