# 1. 什么是SpringBoot的自动配置

SpringBoot自动配置,英文是Auto-Configuration:

  • 是指基于你引入的依赖jar包,对SpringBoot应用进行自动配置
  • 它为SpringBoot框架的"开箱即用"提供了基础支撑

# 2.自动配置和自动装配的区别

自动配置是:Auto-Configuration针对是Spring中的配置类,自动装配Autowire针对的是Spring的依赖注入。

# 3.术语“配置类”--Configuration class

  • 广义的配置类:被注解@Component直接或间接修饰的某个类,就是我们常说的Spring组件,其中包含了@Configuration类
  • 狭义的"配置类":特指被注解@Configuration所修饰的类,又称为@Configuration类

# 4.SpringBoot简化版本启动流程

image.png

image.png

# SpringBoot加载配置类的流程

image.png

image.png

# 注解@ComponectScan

@ComponentScan,是来自Spring框架的一个注解

  • 它的作用是对制定的package进行扫描,找到其中符合条件的类,默认是搜索被@Component修饰的配置类
  • 通过属性backPackages或basePackageClasses,来制定要进行扫描的package,来制定要进行扫描的package
  • 如果未制定package,则默认扫描当前@ComponectScan所修饰的类所在的package

# @Import,是来自Spring框架的一个注解:

  • 它的作用是提供了一种显示地从其它地方加载配置类的方式,这样可以避免使用性能较差的组件扫描(ComponectScan)
  • 支持导入:
    • 普通类(这里的"普通",是相对于随后的两个接口而言的);
    • 接口ImportSelecter的实现(选择器类)类
      • 可以通过@Import导入一个接口ImportSelector的实现类。接口ImportSelector中有一个selectImports方法,它的返回值是一个字符串数组,数组中的每个元素分别代表一个将被导入的配置类的全限定名。
    • 接口ImportBeanDefinitionRegistrar的实现(注册器)
      • 可以通过@Import导入一个接口ImportBeanDefinitionRegistrar的实现类。通过它,我们可以手动将多个BeanDefinition注册到Ioc容器中,从而实现个性化的定制。利用该特性我们可以给IOC容器动态地导入多个BeanDefinition

# SpringBoot使用那种方式来加载SpringBoot的自动配置类

  • 首先排除注解@ComponentScan。因为使用它很不方便,开发人员需要记住每个第三方jar包中的package名称,然后把它们写到应用程序中。
  • 在排除掉@Import中的接口ImportBeanDefinitionRegistrar的实现,BeanDefinition注册器的设计目标是对@Bean方法的一个补充,从名字就可以看出,它针对的是BeanDefinition层面的。
  • 综上可以看出接口ImportSelecter的实现(选择器类)类是非常适合做Spirngboot的自动配置的
  • 得出一个结论,Springboot使用@Import导入一个ImportSelector从而实现自动配置的功能

# 从源码来分析

我们都知道SpringBoot启动核心就是@SpringbootApplication,而SpringBootApplication中重要的注解就是下面这三个。 @EnableAutoConfiguration注解的含义就是开启自动配置的功能。 image.png 得到@SpringBoot的结构图

image.png 从图中我们得知

  • 首先@SpringBootApplication修饰的类,也会被@Configuration间接修饰,即原配置类。
  • 其次SpringBoot框架会对原配置类所在的package进行组件扫描(@ComponentScan)--现在大家知道为什么启动类所在的位置了吧
  • 最终SpringBoot框架最终会导入AutoCOnfigurationImportSelector.class的选择器来实现自动配置。

# 如何实现类AutoConfigurationImportSelector

  • SpringFactories机制
  • Java SPI机制的延伸和扩展
  • Spring框架的基础机制,在Spring以及SpringBoot源码中到处可见
  • 可以基于它来实现SpringBoot的自动配置功能
  • 它的核心逻辑是从classpath中读取到所有Jar包中的配置文件META-IF/spirng.factories然后根据指定的key从配置文件中解析出对应的value的值。
  • 通过类SpringFactoriesLoader,返回一个类名的集合,可以根据实习需求对这些类名进行下一步的处理。

# 类AutoConfigurationImportSelector的关键源码

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
    // SpringBoot的自动配置的入口方法
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
    // 1.获取annotationMetadata的注解@EnableAutoConfiguration的属性
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        // 从资源文件spring.factories中获取EnableAutoConfiguration对应的所有的类。
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        configurations = this.removeDuplicates(configurations);
        // 3.通过在注解@EnableAutoConfiguration设置exclude的相关属性,可以排除制定的自动配置类
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        //4 根据@conditional来判断是否需要排除某些自动配置类。
        configurations = this.getConfigurationClassFilter().filter(configurations);
        // 5 触发AutoConfiguration导入的相关事件。
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

主要看一下getCandidateConfigurations

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    //通过SpringFactories机制,从配置文件Spring.factories中找出所有的自动配置类。
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

从配置文件获取自动配置类时,使用的key是类EnableAutoConfiguraton的全限定名

image.png @Conditional是来自Spring框架的一个注解

  • 它的作用是实现:只有在特定条件满足时,才会向IOC容器注册指定的组件。
  • 我们可以将@Conditonal理解为某种IF语句

image.png 总结一下自动配置的过程

image.png

# @ComponentScan和@EnableAutoConfiguration的区别

  • 相同点:两者都可以将带有@Component@Service等注解的对象加入到ioc容器中。

  • 不同点:

    • 两者虽然都能将带有注解的对象放入ioc容器中,但是它们扫描的范围是不一样的。@ComponentScan扫描的范围默认是它所在的包以及子包中所有带有注解的对象,@EnableAutoConfiguration扫描的范围默认是它所在类。
    • 它们作用的对象不一样,@EnableAutoConfiguration除了扫描本类带有的注解外,还会借助@Import的支持,收集和注册依赖包中相关的bean定义,将这些bean注入到ioc容器中,在springboot中注入的bean有两部分组成,一部分是自己在代码中写的标注有@Controller,@service,@Respority等注解的业务bean,这一部分bean就由@ComponentScan将它们加入到ioc容器中,还有一部分是springboot自带的相关bean,可以将这部分bean看成是工具bean,这部分bean就是由@EnableAutoConfiguration负责加入到容器中。
    • @EnableAutoConfiguration可以单独启动springboot项目,而@ComponentScan是不
  • 结论:Component注解可以将我们自定义bean加载到我们的ioc容器,而我们的系统级别的组件加载则由我们EnableAutoConfiguration注解来进行!