近期想系统的学习一下SpringBoot,但是最基本的使用很简单,简单到没有笔记值得做的地步。
但是仔细想想,嗯!为什么最基本的使用很简单?这就是Spring Boot的精髓——Spring Boot自动配置原理
Spring Boot的配置文件
都知道Spring Boot得益于“习惯优于配置”的理念,没有繁琐的配置,而刚刚学习Spring Boot的时候我们就知道Spring Boot有一个全局配置文件:application.properties、application.yaml或application.yml。
application.yaml和application.yml是一个东西,至于yaml是啥、怎么使用,这个后期再找个时间写写。
Spring Boot项目的各种配置都可以在这个文件重进行配置,比如最常见的server.port=8080、spring.application.name=demo等等。
当然这只是我们用到的很少一部分,那么具体有多少配置?在官方文档中可以查到:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties
官网中那么多的配置在Spring Boot的项目中又是怎么生效的呢?这就是Spring Boot的精髓——Spring Boot自动配置原理
原理解析
在Spring Boot项目中关于自动配置的源码在spring-boot-autoconfigure-2.3.0.RELEASE.jar
中,虽然官方文档中没有详细说明细自动配置的原理,但是在Spring Boot启动类中的@SpringBootApplication
注解中可以看出一些联系。
@SpringBootApplication
注解是一个组合注解:
@SpringBootConfiguration
表示这是一个Spring Boot配置类@EnableAutoConfiguration
表示开启自动配置@ComponentScan
组件扫描器,扫描当前类(带有此注解的类)同级目录下的所有文件
其中重点就是@EnableAutoConfiguration
注解,@EnableAutoConfiguration
注解也是一个组合注解,它的主要逻辑由@Import
注解引入的AutoConfigurationImportSelector.class
提供:
进入AutoConfigurationImportSelector
类可以看到一个selectImports
方法:
selectImports
方法会调用getAutoConfigurationEntry
方法拿到所返回的自动配置项
而getAutoConfigurationEntry
方法的配置项目是由getCandidateConfigurations
方法通过SpringFactoriesLoader.loadFactoryNames
方法扫描所有拥有META-INF/spring.factories
文件的jar:
spring-boot-autoconfigure-2.3.0.RELEASE.jar
里面就有一个spring.factories
文件
spring.factories
文件是一组一组的key=value,其中一个key是EnableAutoConfiguration
类的全类名,而它的value是一个xxxxAutoConfiguration
的类名的列表:
xxxxAutoConfiguration
就是各种Spring Boot用到的配置类。
再看看Spring Boot的启动类:
里面就一个main方法,main方法里面也只有一行代码:
SpringApplication.run(DemoApplication.class, args);
我们一步一步的往下跟进:
到这里会看到一个设置初始化(setInitializers
)和设置监听器(setListeners
)的步骤,跟进去看看:
在这里我们喜闻乐见的看到了SpringFactoriesLoader.loadFactoryNames
方法,而它也会去扫描所有拥有META-INF/spring.factories
文件的jar
也就是说@EnableAutoConfiguration
注解通过@SpringBootApplication
注解间接性的标记在Spring Boot的启动类上,在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类初始化加载到Spring容器中。
自动配置生效
在spring.factories文件中那么多XxxxAutoConfiguration自动配置类其实都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:
- @ConditionalOnBean:当容器里有指定的bean的条件下。
- @ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
- @ConditionalOnClass:当类路径下有指定类的条件下。
- @ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
- @ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
以org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何生效:
- @ConditionalOnClass:如果ServletRequest.class存在,当前配置生效
- @ConditionalOnWebApplication:如果当前项目是一个web项目并且基于servlet时生效
当上面的两个注解全部生效后@EnableConfigurationProperties开启自动配置属性,它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。
在ServerProperties类中喜闻乐见的看到了@ConfigurationProperties注解,这表示它会将类中存在的配置去和配置文件中的server.***进行匹配绑定(就算配置文件没有指定也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat),而@EnableConfigurationProperties负责导入这个已经绑定了属性的bean到spring容器中;那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。
到这里,大致可以了解,在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。
而诸多的XxxxAutoConfiguration自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。
如果有人问到你spring boot自动配置的原理的时候你也不用像上面那样回答的那么具体:
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。
着整个过程大致的原理图是这样的:
归根到底就是:
- XxxxProperties类的含义是:封装配置文件中相关属性
- XxxxAutoConfiguration类的含义是:自动配置类,目的是给容器中添加组件
而其他的主方法启动,则是为了加载这些五花八门的XxxxAutoConfiguration类。