Spring Boot源码解析 - 自动装配原理

|------------------------------------------------------------------------------------------|
| ### 引言 > Spring Boot自动装配是Spring Boot框架的一个关键特性,它的目标是让开发者能够快速构建Spring应用程序,减少繁琐的配置工作。 |

一、注解解析

@SpringApplication

从启动类@SpringApplication注解入手,@SpringBootApplication是一个组合注解,它是Spring Boot框架中常用的一个主要注解之一。它结合了多个注解,简化了Spring Boot应用程序的配置。

java 复制代码
@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

@SpringBootApplication 包含以下三个核心注解:

  1. @SpringBootConfiguration:这是Spring Boot框架中的一个元注解,用于标识该类为Spring Boot的配置类。配置类是用来定义Bean和配置应用程序的类。
  2. @EnableAutoConfiguration:这个注解告诉Spring Boot根据类路径下的依赖和配置自动配置Spring应用程序。它利用了Spring Boot的自动配置特性,根据条件化配置的原理,自动配置所需的Bean、组件和属性。
  3. @ComponentScan:这个注解用于指定Spring Boot扫描组件的基础包。Spring Boot会扫描这个包及其子包,查找带有注解(例如@Controller@Service@Repository@Component等)的类,并将它们注册为Spring管理的Bean
java 复制代码
@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 {
    ...省略
}

EnableAutoConfiguration 是自动装配的核心,下面我们对该注解展开分析。

@EnableAutoConfiguration
java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...省略
}

@EnableAutoConfiguration的源码中,可以看到它使用@Import注解引入了AutoConfigurationImportSelector类。当Spring bean后置处理器扫描解析启动类下所有包创建beanDefinition时,AutoConfigurationImportSelector会触发自动装配的加载过程。

@Import扩展点

@Import是一个用于导入其他配置类的扩展点。通过使用@Import注解,我们可以在一个配置类中引入其他的配置类,从而将它们的Bean定义和配置合并到当前配置中。

@Import导入配置类分为两种:

  • 实现ImportSelector接口的类,执行selectImports()返回一个字符串数组,其中包含要导入的配置类的全限定名。
  • 实现ImportBeanDefinitionRegistrar接口的类,执行registerBeanDefinition()可以动态地注册Bean定义,从而实现更复杂的配置。

通过上述可知AutoConfigurationImportSelector 实现ImportSelector接口。该自动装配核心类逻辑在接下的分析中展开说

二、spring boot 自动装配原理分析

初始化SpringApplication
java 复制代码
@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        //第一步
        SpringApplication.run(MyApp.class, args);
    }
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        //第二步
        return new SpringApplication(primarySources).run(args);
    }

//第三步
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //资源加载器
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //设置启动引导类
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //web应用类型:
        // 1. NONE  非web应用程序
        // 2. SERVLET web应用程序
        // 3. REACTIVE 响应式web引用程序
        //通过web应用类路径选择web应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //Spring SPI机制获取key为 ApplicationContextInitializer的类,并将它们作为应用程序上下文初始化器(ApplicationContextInitializer)注册
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //Spring SPI机制获取key为 ApplicationListener,并将它们作为应用程序事件监听器(ApplicationListener)注册
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

这块关注第一次调用Spring SPI机制的setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

Spring SPI机制
java 复制代码
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        //Spring SPI机制加载 META-INF/spring.factories 文件以key,value形式的自动装配类
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

通过 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 方法,从 META-INF/spring.factories 文件中加载指定接口类型的实现类的名称集合,并将它们保存在一个 Set 中。

  • META-INF/spring.factories: 通常是用于自动装配的配置文件,其包含了需要自动装配的类的全限定名。

一些八股文认为在AutoConfigurationImportSelector使用Spring SPI加载META-INF/spring.factories获取自动装配类 ,其实不然,Spring SPI只加载一次META-INF/spring.factories,以key-value的形式进行本地缓存,后续触发自动装配时,从本地缓存中获取自动装配类集合

Spring Boot启动的核心逻辑
java 复制代码
public ConfigurableApplicationContext run(String... args) {
   .
   .省略
   .
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      //初始化并配置应用程序的环境,包括加载配置文件、解析命令行参数、设置系统属性等。
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
       //打印Banner
      Banner printedBanner = printBanner(environment);
      //根据web类型创建ApplicationContext
      context = createApplicationContext();
       //SpringBootExceptionReporter用于报告Spring Boot应用程序启动过程中的异常
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      //准备ApplicationContext,包括设置环境、配置监听器、初始化Bean等操作。
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      //这里会完成整个Spring IoC容器的初始化,包括解析Bean的定义,创建Bean实例,以及处理依赖注入等。
      refreshContext(context);
      .
      .省略
      .
   return context;
}

上述代码中,除了createApplicationContext()refreshContext(context),其他部分与自动装配无关。在后续的文章中,会进一步介绍这部分内容。

createApplicationContext()做了什么?
java 复制代码
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                //根据webApplicationType枚举类型,创建应用
                switch (this.webApplicationType) {
                case SERVLET: //SERVLET web应用程序
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE: //REACTIVE 响应式web引用程序
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default: //NONE  非web应用程序
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

public AnnotationConfigServletWebServerApplicationContext() {
        //用于读取基于注解的Bean定义的类
        this.reader = new AnnotatedBeanDefinitionReader(this);
        //用于扫描类路径中的组件,并将其注册为Spring管理的Bean定义。
        this.scanner = new ClassPathBeanDefinitionScanner(this);
}

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        Assert.notNull(environment, "Environment must not be null");
        this.registry = registry;
        this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    //注册BeanFactory后置处理器
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

//注册BeanFactory后置处理器
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
            BeanDefinitionRegistry registry, @Nullable Object source) {
        .
        .省略
        .
        Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

        if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            //自动装配重点:创建BeanFactory后置处理器 ConfigurationClassPostProcessor,用于扫描解析启动类当前包以及子包下的配置类
            RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
            def.setSource(source);
            //注册 ConfigurationClassPostProcessor
            beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

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

        // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
        if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            //创建BeanFactory后置处理器 CommonAnnotationBeanPostProcessor,处理依赖注入@Resource、WebServiceRef标注的字段以及方法
            RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
            def.setSource(source);
            //注册CommonAnnotationBeanPostProcessor
            beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
        }
        .
        .省略
        .

        return beanDefs;
    }

上述这段代码整体来说非常简单,根据 web应用类型创建ApplicationContext, 例如web应用类型为 SERVLET,创建AnnotationConfigServletWebServerApplicationContext。通过AnnotationConfigServletWebServerApplicationContext构造方法创建 AnnotatedBeanDefinitionReader以及ClassPathBeanDefinitionScanner,我们需要注意的是AnnotatedBeanDefinitionReader涉及BeanFactory后置处理器注册的内容,可以看到这一步注册了ConfigurationClassPostProcessor

  • AnnotatedBeanDefinitionReader: 用于读取基于注解的Bean定义的类

  • ClassPathBeanDefinitionScanner: 用于扫描类路径中的组件

  • ConfigurationClassPostProcessor : 用于扫描解析启动类当前包以及子包下的配置类

refreshContext(context)做了什么?
java 复制代码
@Override
    public void refresh() throws BeansException, IllegalStateException {
        ...省略

            /**
             * 共注册了2个后置处理器
             * ApplicationContextAwareProcessor,用于执行xxxAware接口中的方法
             * ApplicationListenerDetector,保证监听器被添加到容器中
             */
            prepareBeanFactory(beanFactory);
        try {

            //为容器的某些子类指定特殊的BeanPost事件处理器
            postProcessBeanFactory(beanFactory);

            //触发BeanFactory后置处理器
            //定位注册加载BeanDefinition, 能让我们对容器中扫描出来的BeanDefinition做出修改以达到扩展的目的
            invokeBeanFactoryPostProcessors(beanFactory);

            //说白了就是beanFactory.addBeanPostProcessor(postProcessor);添加bean后置处理器
            registerBeanPostProcessors(beanFactory);

            初始化容器事件传播器,添加事件传播器到IOC容器
            initApplicationEventMulticaster();

            //启动容器
            onRefresh();

            //为事件传播器注册事件监听器.
            registerListeners();

            //将解析的BeanDefinition注册到IOC容器中
            finishBeanFactoryInitialization(beanFactory);

            //初始化容器的生命周期事件处理器,并发布容器的生命周期事
            finishRefresh();
        }
        ...省略
    }

refreshContext方法中,完成了整个Spring IoC容器的初始化,包括解析Bean的定义、创建Bean实例以及处理依赖注入等。我们主要关注invokeBeanFactoryPostProcessors(beanFactory)对配置类进行定位解析注册BeanDefinition

invokeBeanFactoryPostProcessors(beanFactory)做了什么?
java 复制代码
    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        //遍历内部所有BeanDefinitionRegistry后置处理器 (继承 BeanFactoryPostProcessor),
        //调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 解析配置类成benaDefinition并注册
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

        // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
        // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
        if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }
    }

    //第二步: 

invokeBeanFactoryPostProcessors方法中,会触发所有BeanFactory后置处理器的执行,其中ConfigurationClassPostProcessor后置处理器负责扫描解析启动类当前包以及子包下的配置类,并将其注册为Spring管理的BeanDefinition,接下来我们就看看它的源码

ConfigurationClassPostProcessor源码分析
java 复制代码
    //调用 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 
        //获取传入的 BeanDefinitionRegistry 对象 registry 的哈希码,作为标识
        int registryId = System.identityHashCode(registry);
        //通过检查 registriesPostProcessed 集合和 factoriesPostProcessed 集合,防止了在同一个 BeanDefinitionRegistry 上重复执行逻辑
        if (this.registriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
        }
        if (this.factoriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + registry);
        }
        this.registriesPostProcessed.add(registryId);

        //第三步
        processConfigBeanDefinitions(registry);
    }

    //处理一些自定义的BeanDefinition
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {

        //存放加了注解@Configuration @Component @ComponentScan @Import @ImportResource的类
        List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
        String[] candidateNames = registry.getBeanDefinitionNames();

        for (String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            //判断是否已经处理
            if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                    ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
                }
            } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                //存放加了注解@Configuration @Component @ComponentScan @Import @ImportResource的类
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }

        ...省略

        // 创建配置类解析器,构造方法会创建ComponentScanAnnotationParser扫描器
        // 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());
        do {
            //1.解析
            parser.parse(candidates);
            //验证
            parser.validate();

            // 已经解析过的配置类
            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            // 移除已经解析过的配置类,防止重复加载了配置类中的bd
            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());
            }
            // 2. 将所有ConfigurationClass解析成BeanDefinition进行 @Condition... 匹配,然后注册到beanDefinition容器中
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);

            candidates.clear();
            // 如果大于,说明容器中新增了一些BeanDefinition,那么需要重新判断新增的bd是否是配置类,如果是配置类,需要再次解析
            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());

        // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
        // 注册ImportRegistry到容器中
        // 当通过@Import注解导入一个全配置类A(被@Configuration注解修饰的类),A可以实现ImportAware接口
        // 通过这个Aware可以感知到是哪个类导入的
        if (sbr != null) {
            if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
            }
        }

        if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
            // Clear cache in externally provided MetadataReaderFactory; this is a no-op
            // for a shared cache since it'll be cleared by the ApplicationContext.
            ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
        }
    }

//解析
public void parse(Set<BeanDefinitionHolder> configCandidates) {
   this.deferredImportSelectors = new LinkedList<>();

   // 在 new SpringApplication时候,会将 启动类 生成为AnnotatedBeanDefinition,然后注入到beanDefinition容器中
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
         if (bd instanceof 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);
      }
   }

   //处理启动类的自动装配注解上@Import,对于 实现ImportSelector接口的执行selectImport
   processDeferredImportSelectors();
}

private void processDeferredImportSelectors() {
        List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        if (deferredImports == null) {
            return;
        }

        Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
        for (DeferredImportSelectorHolder deferredImport : deferredImports) {
            ConfigurationClass configClass = deferredImport.getConfigurationClass();
            try {
                // 获取启动类上的所有@Import的实现ImportSelector类,执行selectImports
                // 自动装配就在此处触发,获取类处境
                String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
                 // 处理selectImports返回的可导入自动装配类
                //1. 实现imporselect接口,则创建为实例调用selectImprot方法,然后递归调用处理import
                //2. 实现importRegistry接口,则缓存到importBeanDefinitionRegistrars集合
                //3. 非上述接口,则当做配置类处理。解析@PropertySources、@ComponentScans、@Import、@Bean
                processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
            } catch (BeanDefinitionStoreException ex) {
                throw ex;
            } catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                                configClass.getMetadata().getClassName() + "]", ex);
            }
        }
    }

上述代码主要流程:

  1. 配置类解析器进行解析

    • 注解方式解析
      • 解析启动类@ComponentScans注解信息获取basePackagesbasePackageClasses ,没有则将启动类包路径
      • 创建ClassPathBeanDefinitionScanner对当前包以及子包下所有后缀.class文件进行扫描解析
        @Component@Controller@Service@Repository
        以及@Configura配置类的@PropertySources@Import@Bean生成ConfigurationClass
      • ConfigurationClassexcludeFilters进行排除过滤、Conditional进行条件匹配
    • 处理启动类上的@Import扩展点
  2. 将所有ConfigurationClass解析成BeanDefinition@ConditiOnXXX 进行条件匹配,最后注册到BeanDefinition容器中

@bean 等价于 factory-method

@bean 所在的类 等价于 factory-bean

自动装配核心逻辑 - AutoConfigurationImportSelector
java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        //加载组件自动配置类元信息spring-autoconfigure-metadata.properties 
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //获取key为EnableAutoConfiuration对应的value集合
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        //自动装配类去重
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //根据maven导入的启动器,过滤出需要导入的自动装配类
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }

上面这段代码主要做了四件事

  1. 加载spring-autoconfigure-metadata.properties文件得到组件自动装配类元信息
  2. Spring SPI 本地缓存中获取keyEnableAutoConfiuration对应的自动装配类集合
  3. 使用LinkedHashSet对自动装配类做去重操作
  4. 将自动配置类与spring-autoconfigure-metadata.properties进行条件匹配过滤出需要导入的自动装配类 (例如 @ConditionalOnClass )
  5. 返回自动装配类数组

通过本文前部分的注解分析已知AutoConfigurationImportSelector实现ImportSelector接口。当ConfigurationClassPostProcessor后置处理器处理@Import时,就会触发自动装配核心类AutoConfigurationImportSelector#selectImprot()获取Spring SPI 本地缓存keyEnableAutoConfiuration的自动装配类集合,然后去重过滤返回需要导入的自动装配类数组。

三、小结

简单的来说,Spring Boot的自动装配原理主要依赖于Spring SPI机制+@Import扩展点+ ConfigurationClassPostProcessor实现。ConfigurationClassPostProcessor后置处理器会在扫描解析启动类当前包以及子包下的配置类,并将其注册为Spring管理的BeanDefinition,从而实现自动装配。而AutoConfigurationImportSelector作为自动装配核心类,在处理@Import逻辑中触发自动装配的过程,最终获取需要导入的自动装配类集合,实现Spring Boot的自动装配功能。

相关推荐
Amarantine、沐风倩✨2 分钟前
设计一个监控摄像头物联网IOT(webRTC、音视频、文件存储)
java·物联网·音视频·webrtc·html5·视频编解码·七牛云存储
路在脚下@1 小时前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
啦啦右一1 小时前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
森屿Serien1 小时前
Spring Boot常用注解
java·spring boot·后端
苹果醋32 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader2 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭3 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪3 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0073 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生3 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl