1、概述

  • SpringBoot 自动配置的是一些Java 配置类;也就是说Spring Boot自动装配的对象是Spring Bean。
  • SpringBoot自动配置的类取决于我们在应用的Class Path下添加的JAR文件依赖。
  • 自动装配的类可以打包到外部的JAR文件中,也能够被SpringBoot装载;
  • 自动装配也能被关联到“starter"中,这些”starter“提供自动装配的类及关联的依赖;
  • spring-boot-autoconfigure是Spring Boot核心模块(JAR),其中提供了大量的内建自动装配类,它们统一存放在org.springframework.boot.autoconfigure包或子包下;
  • 自动装配的类均配置在META-INF/spring.factories 文件中;META-INF/spring.factories属于Java Properties格式文件;
  • 激活自动装配的注解 @EnableAutoConfiguration 的全类名充当自动配置类的Key,而自动装配类为Value。


# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

2、@SpringBootApplication

1)注解语义

@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注解是一个组合注解,内部包含三个注解:

  1. @SpringBootConfiguration(就是一个@Configuration配置类);
  2. @ComponentScan(basePackages="")(扫描指定包下的@Component组件,并指定扫描时排除指定类)
  • 是一种综合性技术手段;它重新深度整合Spring注解编程模型,@Enable模块驱动及条件装配等Spring framework原生特性;
  • @ComponentScan中排除了AutoConfigurationExcludeFilter类(即:永远排除其他同时标注@Configuration和@EnableAutoConfiguration的类);
  1. @EnableAutoConfiguration(负责激活SpringBoot自动装配机制)

即:@SpringBootApplication 注解等同于 @Configuration + @EnableAutoConfiguration + @ComponScan 注解。因此 @SpringBootApplication 注解也能激活自动装配。(只要将注解@EnableAutoConfiguration 标注在一个@Configuration类上,即可实现自动装配)而且它能减少多注解所带来的配置成本;比如@ComponentScanbasePackages属性被@SpringBootApplicationscanBasePackages属性做别名。

2)@SpringBootApplication属性别名

@AliasFor注解用于桥接其他注解的属性,其能够将一个或多个注解的属性“别名”在某个注解中。

public @interface SpringBootApplication {
    ....
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

@SpringBootApplication的scanBasePackages属性将 @ComponentScan扫描的位置指向我们设置的位置。

3)@SpringBootApplication 标注非引导类

可以将@SpringBootApplication注解加在任一类ClassA上(注意scanBasePackages属性的值),然后在引导类的main()方法中run(ClassA.class, args)

  • @SpringBootApplication并非限定标注于引导类。
  • @SpringBootApplication 和 @EnableAutoConfiguration 均能激活自动装配的特性,但对于被标注类的Bean类型则存在差异。

@SpringBootApplication“继承”了@Configuration 拥有CGLIB提升特性:

  • 正常@Bean的声明方式为“轻量模式”(Lite)
  • 在@Configuration下声明的@Bean则属于”完全模式“(Full),会执行CGLIB提升的操作。即:@Configuration类有CGLIB提升。

关于CGLIb提升:

  • 使用 @Configuration 的配置类,下面的声明@bean的方法,就只会被调一次,也就是初始化的时候,哪怕是下面的方法直接互相引用,返回的对象依旧是第一次调用时创建的;
  • 而如果不加@Configuration,那么下面的方法如果有相互调用,那么返回的对象的构造方法就会被调多次;

3、@EnableAutoConfiguration(自动配置的过程)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}

@EnableAutoConfiguration 注解由两部分组成,分别为:@AutoConfigurationPackage(用来向容器中注入一个组件BasePackages来保存一些包路径)、@Import(AutoConfigurationImportSelector.class)(负责加载各jar包中spring.factories文件中配置的自动装配类)。

1)@AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    ....
}

@AutoConfigurationPackage 注解通过 @Import 注解导入了 AutoConfigurationPackages.Registrar 类。@Import注解会直接向Spring容器中注入指定的组件。

AutoConfigurationPackages.Registrar类:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
	}

	@Override
	public Set<Object> determineImports(AnnotationMetadata metadata) {
		return Collections.singleton(new PackageImports(metadata));
	}
}

AutoConfigurationPackages.Registrar类的registerBeanDefinitions()方法中:

  • 入参metadata包含启动类信息;例如:com.saint.StartApplication;
  • new PackageImports(metadata).getPackageNames()表示包加载/扫描路径,即启动类所在的目录,例如:com.saint
    这就是为什么我们需要将启动类(主配置类)放在项目的最外层目录中的原因之一(另一个原因是@ComponentScan默认会把启动类所在包作为扫描路径)。

因此,@AutoConfigurationPackage注解的作用是向容器中注入组件BasePackages来保存一些包路径。(JPA中在没有配置扫描路径时就利用该路径来扫描自己的实体类)

注意:如果我们依赖jar包中配置类所在包路径在当前程序扫描的路径中,是可以不用在spring.factories文件中写EnableAutoConfiguration = 某某类的。

2)@Import(AutoConfigurationImportSelector.class)

通过@Import注入

AutoConfigurationImportSelector 类,而它实现了DeferredImportSelector接口,

selectImports()方法会去加载

META-INF/spring.factories 文件,所有自动装配类都配置在其中。

1、AutoConfigurationImportSelector 读取自动装配类的流程
  1. 在其selectImports(AnnotationMetadata)方法中调用自己的getAutoConfigurationEntry(AnnotationMetadata)方法拿到所有自动配置的节点;这里分为六步;
  2. 第一步,利用Spring Framework工厂机制的加载器SpringFactoriesLoader,通过SpringFactoriesLoader#loadFactoryNames(Class, ClassLoader)方法读取所有META-INF/spring.factories资源中@EnableAutoConfiguration所关联的自动装配类集合。
  3. 第二步,利用Set不可重复性对自动装配Class集合进行去重,因为自动装配组件存在重复定义的情况;
  4. 第三步,读取当前配置类所标注的@EnableAutoConfiguration注解的属性exclude和excludeName,并与spring.autoconfigure.exclude配置属性的值合并为自动装配类排除集合
  5. 第四步,校验自动装配类排除集合的合法性、并排除掉自动装配类排除集合中的所有类(不需要自动装配的Class)。
  6. 第五步,再次过滤候选自动装配Class集合中不符合条件装配的Class成员;
  7. 最后一步,触发自动装配的导入事件AutoConfigurationImportEvent,发布事件到到Listener(Listener中会绑定BeanFactory,将通过过滤的自动装配候选类、已经排除的自动装配类信息记录到条件评估报告器ConditionEvaluationReport中)。
    我们在application.yml文件中指定debug: true来查看自动装配情况时,就是ConditionEvaluationReport输出的。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    // 加载自动装配的元信息
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 1. 获取@EnableAutoConfiguration标注类的元信息
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 2. 返回自动装配类的候选类名集合
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 3. 移除重复对象,因为 自动装配组件存在重复定义的情况
    configurations = removeDuplicates(configurations);
    // 4. 自动装配组件的排除名单
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // 5.1. 检查自动装配Class排除集合的合法性
    checkExcludedClasses(configurations, exclusions);
    // 5.2 排除掉不需要自动装配的Class
    configurations.removeAll(exclusions);
    // 6. 进一步过滤
    configurations = getConfigurationClassFilter().filter(configurations);
    // 7. 触发自动装配的导入事件,事件包括候选的装配组件类名单和排除名单。
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

这里着重看一下 getCandidateConfigurations() 是如何获取到所有的自动装配类的:

利用SpringFactoriesLoader#loadFactoryNames(Class, ClassLoader)方法,

SpringFactoriesLoader是Spring Framework工厂机制的加载器;loadFactoryNames()原理如下:

  1. 搜索指定ClassLoader下所有的META-INF/spring.fatories资源内容;
  2. 将搜索到的资源内容作为Properties文件读取,合并为一个Key为接口的全类名、Value为实现类全类名 列表的Map,作为方法的返回值;
  3. 最后从上一步返回的Map中查找并返回方法指定类型对应的实现类全类名列表
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                                                                         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;
}
/**
 * SpringFactoriesLoader#loadFactoryNames()
 */
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    // 先从缓存中获取
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        Enumeration<URL> urls = (classLoader != null ?
                                 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            /**
             * 查找所有我们依赖的jar包,并找到对应有META-INF/spring.factories⽂件,然后获取⽂件中的内容
             * 
             * 第一次循环:file:/.../org/springframework/spring-beans/5.2.12.RELEASE/spring-beans-5.2.12.RELEASE.jar!/META-INF/spring.factories
             * 第二次循环:file:/.../org/springframework/boot/spring-boot/2.3.7.RELEASE/spring-boot-2.3.7.RELEASE.jar!/META-INF/spring.factories
             * 第三次循环:file:/../org/springframework/boot/spring-boot-autoconfigure/2.3.7.RELEASE/spring-boot-autoconfigure-2.3.7.RELEASE.jar!/META-INF/spring.factories
             */
            URL url = urls.nextElement();
            // 获取资源
            UrlResource resource = new UrlResource(url);
            // 获取资源的内容
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}
  • 第一次扫描:
  • springboot 项目配置不自动连接mongodb springboot自动配置了什么_spring

  • 第二次扫描:
  • springboot 项目配置不自动连接mongodb springboot自动配置了什么_spring boot_02


  • springboot 项目配置不自动连接mongodb springboot自动配置了什么_List_03

  • 第三次扫描:
  • springboot 项目配置不自动连接mongodb springboot自动配置了什么_List_04

SpringBoot扫描到META-INF/spring.factories⽂件之后,META-INF/spring.factories文件中的内容很多,例如spring-boot-autoconfigure-2.3.7.RELEASE.jar!/META-INF/spring.factories中key为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的Value很多,我们如何知道哪个需要自动加载、哪个不需要?

2、自动装配按需加载的原理

例如:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration

@Configuration(proxyBeanMethods = false)
// 没有引入RabbitTemplate和Channel就不会初始化RabbitAutoConfiguration到IOC容器
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
    ....
}
  • 其中@ConditionalOnClass({ RabbitTemplate.class, Channel.class })便体现了SpringBoot自动配置的按需加载,即:只有RabbitTemplate.class和Channel.class都存在时,才会自动配置RabbitAutoConfiguration。
  • 相关的注解还有很多,比如:@ConditionalOnMissingClass、@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnProperty 等。
3、容错兼容

如果用户创建了一个名称不正确(不符合SpringBoot要求)的类型的Bean,SpringBoot会对其进行自动容错兼容。

比如自动装载Servlet时,org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration中上传文件的处理器MultipartResolver,如果用户创建了一个名称不为multipartResolver的MultipartResolver类,SpringBoot会自动将其名称转换为标准名称multipartResolver:

@Bean
// 作用在当前方法上,要求IOC中必须存在MultipartResolver类型的Bean
@ConditionalOnBean(MultipartResolver.class)
// 要求IOC容器中不存在名称为multipartResolver的bean
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
    // Detect if the user has created a MultipartResolver but named it incorrectly
    // 调⽤者为IOC,resolver就是从IOC中获取到的类型为MultipartResolver的bean(是MultipartResolver类型的但是名称不是“multipartResolver”)
    return resolver; // 保存到IOC中的bean的名称是“multipartResolver”
}
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
4、 用户配置优先(也可以说是外部配置项修改组件行为)

自定义配置优先级高于系统的默认配置。比如WebMvcAutoConfiguration类针对View进行解析的InternalResourceViewResolver的prefix和suffix设置。

@Bean
// 如果容器中没有@ConditionalOnMissingBean名称为“defaultViewResolver”的InternalResourceViewResolver类型的Bean,则去创建(对于IOC容器而言,如果你没有创建,我帮你去创建;如果你创建了,那我就以你为主)
public InternalResourceViewResolver defaultViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix(this.mvcProperties.getView().getPrefix());
    resolver.setSuffix(this.mvcProperties.getView().getSuffix());
    return resolver;
}

我们可以自定义配置:

spring.mvc.view.prefix = "template"
spring.mvc.view.suffix="ftl"
5、 查看自动配置情况

yaml文件中配置如下内容:

debug=true

控制台输出:

springboot 项目配置不自动连接mongodb springboot自动配置了什么_spring boot_05

6、自动装配事件
  • 首先获取所有的自动装配事件监听器AutoConfigurationImportListener
  • AutoConfigurationImportListener有别于传统的Spring ApplicationListener的实现。
  • ApplicationListener与Spring应用上下文ConfigurableApplicationContext紧密关联,监听Spring ApplicationContext。
  • 而AutoConfigurationImportListener继承java EventListener接口,仅用于监听AutoConfigurationImportEvent,不过其实例同样可以被SpringFactoriedLoader加载,也就是说SpringBoot框架层面为开发人员提供了扩展的途径,我们可以自定义AutoConfigurationImportListener的实现类;
  • 执行所有的自动装配事件。
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    // 1. 获取所有的自动装配事件监听器AutoConfigurationImportListener
    List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
        AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
        for (AutoConfigurationImportListener listener : listeners) {
            invokeAwareMethods(listener);
            // 2. 执行自动装配事件
            listener.onAutoConfigurationImportEvent(event);
        }
    }
}

自定义AutoConfigurationImportListener:

/**
 * 自定义监听器(监听autoImport)
 */
public class CustomAutoConfigurationImportListener implements AutoConfigurationImportListener {
    @Override
    public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
        // 获取当前ClassLoader
        ClassLoader classLoader = event.getClass().getClassLoader();
        // 候选的自动装配Class名单
        List<String> candidates = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader);
        // 实际的自动装配Class名单
        List<String> configurations = event.getCandidateConfigurations();
        // 排除的自动装配Class名单
        Set<String> exclusions = event.getExclusions();
        // 输出各自数量
        System.out.printf("自动装配Class名单 - 候选数量: %d, 实际数量: %d, 排除数量: %s \n",
                candidates.size(), configurations.size(), exclusions.size());
        // 输出实际和排除的自动装配Class名单
        System.out.println("实际的自动装配Class名单:");
        event.getCandidateConfigurations().forEach(System.out::println);
        System.out.println("排除的自动装配Class名单:");
        event.getExclusions().forEach(System.out::println);
    }
}

META-INF/spring.factories中添加如下信息

org.springframework.boot.autoconfigure.AutoConfigurationImportListener =\
com.saint.spring.autoconfigureImportlistener.CustomAutoConfigurationImportListener
@EnableAutoConfiguration(exclude = SpringApplicationAdminJmxAutoConfiguration.class)
public class EnableAutoConfigurationBootstrap {

    public static void main(String[] args) {
        new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)
                .web(WebApplicationType.NONE) // 非Web应用
                .run(args) // 运行
                .close(); // 关闭当前上下文
    }
}

从数量上来看,META-INF/spring.factories资源配置的自动装配Class名单要远多于实际装配的,这是因为很多都被过滤掉了。

4、自动装配顺序

自动装配类的@Order很大,也就是优先级极低,要等所有@Configuration class加载完,它才会加载。SpringBoot官网提供了两种方式对自动装配组件组件进行排序:

  • 绝对自动装配顺序 --> @AutoConfigureOrder
  • 与Spring Framework @Order注解的语义相同;
  • 相对自动装配顺序 --> @AutoConfigureBefore 和 @AutoConfigureAfter(推荐使用)。

一般不建议对自动装配组件的顺序进行排序,因为这要求开发人员要了解所有的自动装配组件的顺序,显然不太现实。

我们可以了解大体的排序规则:

  1. 如果自动装配Class集合中未包含@AutoConfigureOrder等顺序注解,则他们按照字母顺序依次加载。
  2. 如果存在,当AutoConfigurationClassesAutoConfigurationClass建立映射关系后,具体的@AutoConfigureOrder排序规则由AutoConfigurationClass#getOrder()方法决定:
  • 首先读取自动装配类的@AutoConfigureOrder的配置值,如果不存在,则使用默认值(很小);
  • 接着对@AutoConfigureBefore 和@AutoConfigureAfter进行排序。
  1. 因为兼容性问题,建议使用@AutoConfigureBefore或@AutoConfigureAfter的name()属性方法。

自动装配Class:

  • META-INF/spring-autoconfigure-metadata.properties是自动装配Class预处理元信息配置的资源。
  • 当该资源文件存在自动装配Class的注解元信息配置时,自动装配Class无须ClassLoader加载,即可得到所需的元信息,减少了运行时的计算消耗。

如果自动装配Class集合中未包含@AutoConfigureOrder等顺序注解,则它们是按照字母顺序依次加载的

5、如何使自动装配失效

SpringBoot提供了两种方式:

  1. 代码配置方式
  • 配置类型安全的属性方法:@EnableAutoConfiguration.exclude()
  • 配置排除类名的属性方法:@EnableAutoConfiguration.excludeName()
  1. 外部化配置方式
  • 配置属性:spring.autoconfigure.exclude

6、自动装配入口

自动装配入口的代码执行流程图:

springboot 项目配置不自动连接mongodb springboot自动配置了什么_spring boot_06


1、概述

  • SpringBoot 自动配置的是一些Java 配置类;也就是说Spring Boot自动装配的对象是Spring Bean。
  • SpringBoot自动配置的类取决于我们在应用的Class Path下添加的JAR文件依赖。
  • 自动装配的类可以打包到外部的JAR文件中,也能够被SpringBoot装载;
  • 自动装配也能被关联到“starter"中,这些”starter“提供自动装配的类及关联的依赖;
  • spring-boot-autoconfigure是Spring Boot核心模块(JAR),其中提供了大量的内建自动装配类,它们统一存放在org.springframework.boot.autoconfigure包或子包下;
  • 自动装配的类均配置在META-INF/spring.factories 文件中;META-INF/spring.factories属于Java Properties格式文件;
  • 激活自动装配的注解 @EnableAutoConfiguration 的全类名充当自动配置类的Key,而自动装配类为Value。