© 本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。转载请保留原文链接及作者
不翼而飞的xml配置
曾经在学习Spring框架的时候,需要繁琐的xml配置或者注解,稍不注意还很容易出错,码农需要花费很多的时间来进行xml配置。直到有一天,SpringBoot出现了,犹如天使一般,码农再也不用进行繁琐的xml配置了,这一切都是来自于SpringBoot的魔法——自动配置。
Springboot遵循“约定优于配置”的原则,使用注解对一些常规的配置项做默认配置,减少或不使用xml配置,让你的项目快速运行起来。Springboot还为大量的开发常用框架封装了starter,如今引入框架只要引入一个starter,你就可以使用这个框架,只需少量的配置甚至是不需要任何配置。
SpringBoot自动配置原理
1. @SpringBootApplication
SpringBoot启动的时候加载主配置类(@SpringBootApplication),开启了自动配置功能 @EnableAutoConfiguration。
@SpringBootApplication是一个派生注解,里面包含了三个注解@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。
@SpringBootApplication
@EnableSwagger2
public class HzmokoApplication {
public static void main(String[] args) {
SpringApplication.run(HzmokoApplication.class, args);
}
- @SpringBootConfiguration:我们点进去以后可以发现底层是Configuration注解,说白了就是支持JavaConfig的方式来进行配置(使用Configuration配置类等同于XML文件)。
- @EnableAutoConfiguration:开启自动配置功能(后文详解)
- @ComponentScan:这个注解,学过Spring的同学应该对它不会陌生,就是扫描注解,默认是扫描当前类下的package。将@Controller/@Service/@Component/@Repository等注解加载到IOC容器中。
2. @EnableAutoConfiguration
我们知道SpringBoot可以帮我们减少很多的配置,也肯定听过“约定大于配置”这么一句话,那SpringBoot是怎么做的呢?其实靠的就是@EnableAutoConfiguration
注解。
简单来说,这个注解可以帮助我们自动载入应用程序所需要的所有默认配置。
我们点进去看一下,发现有两个比较重要的注解:
-
@AutoConfigurationPackage
:自动配置包 -
@Import
:给IOC容器导入组件
2.1 @AutoConfigurationPackage
有人将这个@AutoConfigurationPackage
注解解释成自动配置包,我们也看看@AutoConfigurationPackage
里边有什么:
我们可以发现,依靠的还是@Import
注解,再点进去查看,我们发现重要的就是以下的代码:
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
在默认的情况下就是将:主配置类(@SpringBootApplication
)的所在包及其子包里边的组件扫描到Spring容器中。
- 看完这句话,会不会觉得,这不就是ComponentScan的功能吗?这俩不就重复了吗?
我开始也有这个疑问,直到我看到文档的这句话:
it will be used when scanning for code @Entity classes. It is generally recommended that you place EnableAutoConfiguration (if you're not using @SpringBootApplication) in a root package so that all sub-packages and classes can be searched.
比如说,你用了Spring Data JPA,可能会在实体类上写@Entity
注解。这个@Entity
注解由@AutoConfigurationPackage
扫描并加载,而我们平时开发用的@Controller/@Service/@Component/@Repository
这些注解是由ComponentScan
来扫描并加载的。
- 简单理解:这二者扫描的对象是不一样的。
2.2 @Import(EnableAutoConfigurationImportSelector.class)
我们来看以下EnableAutoConfigurationImportSelector的源码,它继承了AutoConfigurationImportSelector
通过源码分析:
AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:
spring-boot-autoconfigure jar包结构图.png
spring.factories文件结构.png
这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
自动配置生效
每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:
- @ConditionalOnBean:当容器里有指定的bean的条件下。
- @ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
- @ConditionalOnClass:当类路径下有指定类的条件下。
- @ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
- @ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何生效,比如:server.port=8081,是如何生效的(当然不配置也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat)。
在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性,而它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。
在这个类上,我们看到了一个非常熟悉的注解:@ConfigurationProperties,它的作用就是从配置文件中绑定属性到对应的bean上,而@EnableConfigurationProperties负责导入这个已经绑定了属性的bean到spring容器中(见上面截图)。那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。
至此,我们大致可以了解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。
而诸多的XxxxAutoConfiguration自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。
可能到目前为止还是有所疑惑,但面试的时候,其实远远不需要回答的这么具体,你只需要这样回答:
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。
通过一张图标来理解一下这一流程:
网友评论