一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
场景引入
你能说说SpringBoot的自动配置原理吗?
你能说说SpringBoot的自动运行的原理?
这是3月份两个远程面试电话里遇到的2个提问,都指向一个问题:简述SpringBoot如何实现到通过引入一个xxx-starter 实现了模块功能的引入。举例,像下面这样子引入一个knif4j模块
<!--接口文档-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<!--<version>2.0.7</version>-->
</dependency>
再补充下配置信息,就可以实现到一个接口文档。
knife4j:
cloud:
## 参考注册API中的accessKey
accessKey: J***********************************************nVTdkRKL1A2RGptOQ
## 项目编号
code: APITest
## Knife4jCloud的对外域名地址
server: http://127.0.0.1:8090
## 当前服务是否是HTTPS的,默认可以不配置,并且该参数默认为false
ssl: false
## 参考注册API中的client属性,该参数可以不配置,只有在域名的情况下需要进行配置
client: dzz_api
@Bean("测试平台")
public Docket createRestApi() {
Predicate<RequestHandler> selector1 = RequestHandlerSelectors.basePackage("com.d4tech.jifen.controller");
return new Docket(DocumentationType.SWAGGER_2)
.groupName("测试平台")
.apiInfo(apiInfo())
.select()
.apis(Predicates.or(selector1))
.paths(PathSelectors.any())
.build()
.securityContexts(securityContext())
.securitySchemes(securitySchemes())
;
}
打脸回答
第一次回答:AOP和IOC的相关理解。给打脸,说这是Spring的两个基础特性,并不是SpringBoot自动运行的原理。
回到问题
回答这个问题,我们需要先从SpringBootApplication启动类上面的注解开始说起。(最近在复习SpringCloud,以SpringCloudApplication说起)
@SpringBootApplication
SpringBoot自动配置的核心注解是@SpringBootApplication,该注解是springboot的启动类注解,它是一个复合注解,里面包含
@SpringBootConfiguration
@EnableAutoConfiguration
@SpringBootConfiguration
该注解上有一个 @Configuration注解,表示这个spring boot启动类是一个配置类,最终要被注入到spring容器中。
@EnableAutoConfiguration
表示开启自动配置,它也是一个复合注解,里面包含:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage
该注解上有一个@Import(AutoConfigurationPackages.Registrar.class)注解,其中 Registrar 类的作用是将启动类所在包下的所有子包的组件扫描注入到spring容器中。 这也是为什么在spring boot中,我们通常将controller、pojo、service、dao等包放在启动类同级目录下的原因。
@Import(AutoConfigurationImportSelector.class)
其中AutoConfigurationImportSelector类中有一个getCandidateConfigurations()方法,该方法通过SpringFactoriesLoader.loadFactoryNames()方法查找位于META-INF/spring.factories文件中的所有自动配置类,并加载这些类。
spring.factories文件是以key-value键值对的形式存储文件里,其中有一个key=EnableAutoConfiguration,它对应的value值就是一个个以AutoConfiguration结尾来命名的 xxxAutoConfiguration 自动配置类。
所以spring boot在整个的启动过程中,其实就是在类路径的META-INF/spring.factories 文件中找到EnableAutoConfiguration对应的所有的自动配置类,然后将所有自动配置类加载到spring容器中。
所有自动配置类都是以AutoConfiguration结尾来命名的,而诸多的xxxAutoConfiguration 其实就是Spring容器的JavaConfig形式,它的作用就是为Spring容器导入bean。
步骤二:自动配置生效
前面加载的所有自动配置类并不是都生效的,每一个xxxAutoConfiguration自动配置类都是在某些特定的条件之下才会生效。这些条件限制是通过@ConditionOnxxx注解实现的。
常见的@@ConditionOnxxx注解有以下几种:
@ConditionalOnBean:当容器里存在指定bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如
@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
@ConditionOnxxx注解用来指定自动配置类在哪些条件下会生效。我们要使用哪些类,就直接在spring boot项目中的pom.xml文件中导入相应的启动器即可,这样spring boot就会利用@ConditionOnxxx注解使我们需要的自动配置类生效,将该类的bean注入到spring容器中,这样就完成了整个自动配置的过程。
总结 spring boot中,每一个xxxAutoConfiguration 自动配置类,其实就是一个spring容器的JavaConfig形式,它的作用就是为spring容器注入相应的bean。而在注入bean的过程中,所有需要的属性值则是通过xxxProperties的bean来获得的。
拓展:xxxProperties类 或者问:在spring boot中,全局配置文件application.yaml或application.properties中自定义的属性如何产生作用?
在每一个xxxAutoConfiguration 类上都有一个@EnableConfigurationProperties(xxxProperties.class)注解,该注解表示开启配置属性,它的参数是一个xxxProperties类。
而xxxProperties类上又有一个@ConfigurationProperties(prefix = “xxx”)注解,该注解的作用就是从配置文件中绑定属性到对应的bean上,该注解的参数prefix 关键字定义的属性就是我们可以在全局配置文件application.yaml中自定义的属性。
举个栗子:假设在application.properties文件中,定义server.port=8081。
首先会找到根据prefix="server"找到对应的ServerProperties类,可以看到该类中定义了一个port属性,所以我们才可以在application.properties文件中自定义server.port属性。 接着@ConfigurationProperties起作用,将我们自定义的属性绑定到对应的bean上
然后@EnableConfigurationProperties注解将已经绑定新属性的bean注入到spring 容器中,这样开发人员自定义的属性就能起作用了。
总结
在全局配置的属性,如server.port=8081等,通过@ConfigurationProperties注解的prefix关键字将自定义的属性绑定到对应的xxxProperties实体类的bean上,然后通过@EnableConfigurationProperties注解将这个绑定了自定义属性的bean注入到spring容器中。