目录
Spring Boot 自动配置流程图示
1、自动配置读取原理源码解析
从 @SpringBootApplication 注解开始,Spring Boot 应用把该注解标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot 需要运行这个类的 main() 方法来启动 SpringBoot 应用;
@SpringBootApplication 注解由以下注解组成
关于Java元注解,可以查看这篇文章《Java注解解析和使用》
(1)@SpringBootConfiguration
@SpringBootConfiguration 标注在某个类上,表示这是一个Spring Boot的配置类;@SpringBootConfiguration 注解详情如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 标记为配置类
public @interface SpringBootConfiguration {
}
(2)@ComponentScan
@ComponentScan 包扫描注解,相当于 spring.xml 配置中的 <context:component-scan base-package="com.xxx.xxx" />,如果没有指定 basepackage,会自动扫描当前注解配置类所在的包和所在包下的所有子包。// 所以被@SpringBootApplication 标注的类总是放置在项目的最外层包
(3)@EnableAutoConfiguration
@EnableAutoConfiguration 自动配置注解,自动配置的核心注解,详情如下
@AutoConfigurationPackage 注解,负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。然后使用 @Import 注解将其注入到 ioc 容器中。随后,Spring 可以在容器中拿到该路径。
@Import 注解只能用在类上 ,通过快速导入的方式实现把实例加入 spring 的 IOC 容器中
SpringBoot 通过 @Import 注解导入了一个 AutoConfigurationImportSelector.class 的类
@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector.class 实现了 DeferredImportSelector 接口(DeferredImportSelector 是 ImportSelector 的一种), 在 ImportSelector 接口中 ImportSelector#selectImports() 方法会返回一个字符串数组(实际上就是 SpringBean 的完整限定名),Spring 底层会根据这些完整限定名,将类注册成 Bean 放到 IOC容器中
DeferredImportSelector#selectImports() 方法的执行逻辑:如果实现类实现了 getImportGroup() 方法(返回值不为空且实现了DeferredImportSelector.Group),那么就调用 Group 类中的selectImports() 方法,否则调用实现类的 selectImports() 方法。
SpringBoot 中 AutoConfigurationImportSelector 实现了 getImportGroup() 方法,返回了一个AutoConfigurationGroup.class
在 AutoConfigurationGroup.class 中,会先调用 process() 方法获取自动配置类,然后调用 selectImports() 方法把获取到的自动配置类封装成 Iterable<Entry> 返回
在 AutoConfigurationGroup#process 中,process#getAutoConfigurationEntry会获取到所有的自动配置类
process#getAutoConfigurationEntry 中会获取所有的候选配置,然后通过一系列筛选才返回最终的自动配置类
getAutoConfigurationEntry#getCandidateConfigurations 真正的去获取配置类,那么它是怎么获取这些配置文件的呢?
使用 getCandidateConfigurations#loadFactoryNames 加载配置文件
loadFactoryNames 中的详细执行逻辑如下,它会去加载 META-INF/spring.factories ,获取这个文件中的所有内容,然后只返回与 factoryClassName 匹配的所有 List<String> ;
至此,SpringBoot 自动配置的读取流程就已经完成了。
附:
每一个 SpringBoot 应用,都会引入 spring-boot-autoconfigure.jar ,jar包中 META-INF/ 目录下包含一个 spring.factories 文件。spring.factories 文件中数据以 Key=Value 形式存在,多个 Value 时使用 "," 隔开,该文件中定义了关于初始化,监听器等信息。其中真正使自动配置类生效的 key 是 org.springframework.boot.autoconfigure.EnableAutoConfiguration ,如下所示:
为什么会是 org.springframework.boot.autoconfigure.EnableAutoConfiguration ?
还记得使用 getCandidateConfigurations#loadFactoryNames 加载配置文件这个方法吗?
其中有一个参数是通过 getSpringFactoriesLoaderFactoryClass() 获取的,这个方法的会返回一个EnableAutoConfiguration
这个 EnableAutoConfiguration 就是 org.springframework.boot.autoconfigure.EnableAutoConfiguration
然后 loadFactoryNames 会根据这个 EnableAutoConfiguration 的名字做为筛选条件,把所有的 xxxAutoConfiguration 封装成一个 List<String> 集合
每一个这样的 xxxAutoConfiguration 类都是容器中的一个组件,xxxAutoConfiguration 类中的Bean 会加入到 IOC 容器中,从而实现自动配置。
注:@EnableAutoConfiguration 注解,通过 @SpringBootApplication 被间接的标记在了 SpringBoot 的启动类上。在 SpringApplication.run(...) 内部就会执行 selectImports() 方法,从而找到所有JavaConfig 自动配置类的全限定名对应的class,然后将所有自动配置类加载到 Spring 容器中。
2、Springboot 自动配置类的使用
以 HttpEncodingAutoConfiguration 自动配置文件为例
在 spring-boot-autoconfigure.jar 的 spring.factories 文件中,有这样一行配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
// 省略...
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
// 省略...
HttpEncodingAutoConfiguration.class 文件的内容如下
其中 HttpProperties.class 里边的属性就是我们通过 application.properties 可以配置的属性
@ConfigurationProperties(prefix = "spring.http") // 绑定外部配置文件的命名属性
假如,我们在 application.properties 中配置如下属性
那么 SpringBoot 启动时,会自动加载到这个属性的值
至此,完成一个自动配置的属性注入。