菜单

Juning
发布于 2020-06-05 / 910 阅读
0
0

Spring Boot自动配置原理

近期想系统的学习一下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注解中可以看出一些联系。
图片.png
@SpringBootApplication注解是一个组合注解:

  • @SpringBootConfiguration表示这是一个Spring Boot配置类
  • @EnableAutoConfiguration表示开启自动配置
  • @ComponentScan组件扫描器,扫描当前类(带有此注解的类)同级目录下的所有文件

其中重点就是@EnableAutoConfiguration注解,@EnableAutoConfiguration注解也是一个组合注解,它的主要逻辑由@Import注解引入的AutoConfigurationImportSelector.class提供:
图片.png
进入AutoConfigurationImportSelector类可以看到一个selectImports方法:
图片.png
selectImports方法会调用getAutoConfigurationEntry方法拿到所返回的自动配置项

图片.png
getAutoConfigurationEntry方法的配置项目是由getCandidateConfigurations方法通过SpringFactoriesLoader.loadFactoryNames方法扫描所有拥有META-INF/spring.factories文件的jar:
图片.png
图片.png
图片.png
spring-boot-autoconfigure-2.3.0.RELEASE.jar里面就有一个spring.factories文件
图片.png
spring.factories文件是一组一组的key=value,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表:
图片.png
xxxxAutoConfiguration就是各种Spring Boot用到的配置类。

再看看Spring Boot的启动类:
图片.png
里面就一个main方法,main方法里面也只有一行代码:

SpringApplication.run(DemoApplication.class, args);

我们一步一步的往下跟进:
图片.png
图片.png
图片.png
图片.png
到这里会看到一个设置初始化(setInitializers)和设置监听器(setListeners)的步骤,跟进去看看:
图片.png
在这里我们喜闻乐见的看到了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配置类为例,解释一下全局配置文件中的属性如何生效:

image.png

  • @ConditionalOnClass:如果ServletRequest.class存在,当前配置生效
  • @ConditionalOnWebApplication:如果当前项目是一个web项目并且基于servlet时生效

当上面的两个注解全部生效后@EnableConfigurationProperties开启自动配置属性,它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。

image.png

在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注解与全局配置文件中对应的属性进行绑定的。

着整个过程大致的原理图是这样的:
howspringbootautoconfigureworks.png

归根到底就是:

  • XxxxProperties类的含义是:封装配置文件中相关属性
  • XxxxAutoConfiguration类的含义是:自动配置类,目的是给容器中添加组件

而其他的主方法启动,则是为了加载这些五花八门的XxxxAutoConfiguration类。


评论