Spring Boot 自动装配原理

18

1. SpringBoot的两大核心

Spring Boot 框架的两大核心特性可以概括为“启动器”(Starter)和“自动配置”(Auto-configuration)。

启动器(Starter): Spring Boot 提供了预定义的依赖关系。当你在项目中引入一个 Starter POM 时,它会自动包含所有必要的 Spring 组件以及合理的默认设置。开发者不需要手动管理复杂的依赖关系,也不需要担心版本冲突的问题,减少了配置上的出错可能。比如:如果项目中添加了 spring-boot-starter-web,Spring Boot 会自动配置 Tomcat 和 Spring MVC。

自动配置(Auto-Configuration): 当添加了特定的 Starter POM 后,Spring Boot 会根据类路径上存在的 jar 包来自动配置 Bean(自动配置相关组件)(比如:SpringBoot发现类路径上存在mybatis相关的类,例如SqlSessionFactory.class,那么SpringBoot将自动配置mybatis相关的所有Bean。)。

这两个特性结合在一起,使得使用 Spring Boot 开发应用程序变得更加简单快速,减少了大量的样板代码和重复配置的工作。让程序员专注业务逻辑的开发,在环境方面耗费最少的时间

导入了spring-boot-starter-web【web启动器】会关联导入一个jar包:spring-boot-autoconfigure

2.自动装配实现原理

2.1 Spring注解

在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:

  • @SpringBootConfiguration

  • @EnableAutoConfiguration

  • @ComponentScan

2.2 Spring类导入

点进去@EnableAutoConfiguration里面有一个@Import({AutoConfigurationImportSelector.class})

main 类启动的时候,Spring Boot 会通过底层的AutoConfigurationImportSelector 类加载自动装配类。

@AutoConfigurationPackage //将main同级的包下的所有组件注册到容器中
@Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
​
    Class<?>[] exclude() default {};
​
    String[] excludeName() default {};
}

AutoConfigurationImportSelector实现了ImportSelector接口,该接口的作用是收集需要导入的配置类,配合 @Import() 将相应的类导入到 Spring 容器中。

2.3 AutoConfigurationSelector

获取注入类的方法是 selectImports(),它实际调用的是getAutoConfigurationEntry()

点进去getAutoConfigurationEntry()

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    // 检查自动配置是否启用。如果@ConditionalOnClass等条件注解使得自动配置不适用于当前环境,则返回一个空的配置条目。
    //@ConditionalOnClass注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
​
    // 获取启动类上的@EnableAutoConfiguration注解的属性,这可能包括对特定自动配置类的排除。
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
​
    // 从spring.factories中获取所有候选的自动配置类。这是通过加载META-INF/spring.factories文件中对应的条目来实现的。
    // getCandidateConfigurations方法在后面说明
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
​
    // 移除配置列表中的重复项,确保每个自动配置类只被考虑一次。
    configurations = removeDuplicates(configurations);
​
    // 根据注解属性解析出需要排除的自动配置类。
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
​
    // 检查排除的类是否存在于候选配置中,如果存在,则抛出异常。
    checkExcludedClasses(configurations, exclusions);
​
    // 从候选配置中移除排除的类。
    configurations.removeAll(exclusions);
​
    // 应用过滤器进一步筛选自动配置类。过滤器可能基于条件注解如@ConditionalOnBean等来排除特定的配置类。
    configurations = getConfigurationClassFilter().filter(configurations);
​
    // 触发自动配置导入事件,允许监听器对自动配置过程进行干预。
    fireAutoConfigurationImportEvents(configurations, exclusions);
​
    // 创建并返回一个包含最终确定的自动配置类和排除的配置类的AutoConfigurationEntry对象。
    return new AutoConfigurationEntry(configurations, exclusions);
}

补充:getCandidateConfigurations通过SpringFactoriesLoader META-INF/spring.factories加载配置类

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 1. 通过SpringFactoriesLoader从META-INF/spring.factories加载配置类
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        // getSpringFactoriesClass() 返回EnableAutoConfiguration.class,指定要加载的配置键
        getSpringFactoriesClass(), 
        // getBeanClassLoader() 提供类加载器用于读取文件
        getBeanClassLoader()
    );
    
    // 2. 校验配置列表是否为空(防止文件缺失或配置错误)
    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."
    );

总结:

@EnableAutoConfiguration是实现自动化配置的核心注解。

这个注解里面有一个@Import({AutoConfigurationImportSelector.class})

AutoConfigurationImportSelector实现了ImportSelector接口,该接口的作用是收集需要导入的配置类,配合 @Import() 将相应的类导入到 Spring 容器中。

AutoConfigutationImportSelector获取注入类的方法是 selectImports(),它实际调用的是getAutoConfigurationEntry()

getAutoConfigurationEntry()方法里面有个getCandidateConfigurations方法,通过SpringFactoriesLoader META-INF/spring.factories加载配置类

在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。

一般条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。