引言

最近有个读者在面试,面试中被问到了这样一个问题“看你我的项目中用到了springboot,你说下springboot的主动配置是怎么实现的?”这应该是一个springboot外面最最常见的一个面试题了。上面咱们就来带着这个问题一起解剖下springBoot的主动配置原理吧。

SpringMvc和SpringBoot比照

首先咱们回顾下原来搭建一个springmvchello-wordweb我的项目(xml配置的)咱们是不是要在pom中导入各种依赖,而后各个依赖有可能还会存在版本抵触须要各种排除。当你历尽含辛茹苦的把依赖解决了,而后还须要编写web.xml、springmvc.xml配置文件等。咱们只想写个hello-word我的项目而已,确把一大把的工夫都花在了配置文件和jar包的依赖下面。大大的影响了咱们开发的效率,以及加大了web开发的难度。为了简化这简单的配置、以及各个版本的抵触依赖关系,springBoot就应运而生。咱们当初通过idea创立一个springboot我的项目只有分分钟就解决了,你不须要关怀各种配置(根本实现零配置)。让你真正的实现了开箱即用。SpringBoot帮你节约了大量的工夫去陪女朋友,不对程序员怎么会有女朋友呢?(没有的话也是能够new一个的)它的呈现不仅能够让你把更多的工夫都花在你的业务逻辑开发上,而且还大大的升高了web开发的门槛。所以SpringBoot还是比拟善解人衣的,错啦错啦是善解人意,晓得开发人员的痛点在哪。

SpringBoot主动配置加载

既然Springboot只管这么好用,然而作为一个使用者,咱们还是比拟好奇它是怎么帮咱们实现开箱即用的。Spring Boot有一个全局配置文件:application.properties或application.yml。在这个全局文件外面能够配置各种各样的参数比方你想改个端口啦server.port 或者想调整下日志的级别啦统统都能够配置。更多其余能够配置的属性能够参照官网。https://docs.spring.io/spring...
![
](https://img-blog.csdnimg.cn/2...
这么多属性,这些属性在我的项目是怎么起作用的呢?SpringBoot我的项目看下来啥配置也没有,配置”(application.properties或application.yml除外),既 然从配置下面找不到突破口,那么咱们就只能从启动类下面找入口了。启动类也就一个赤裸裸的一个main办法,类下面仅有一个注SpringBootApplication
这个注解是Spring Boot我的项目必不可少的注解。那么主动配置原理肯定和这个注解有着千头万绪的分割!咱们上面来一起看看这个注解吧。
@SpringBootApplication注解

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {

这里最下面四个注解的话没啥好说的,基本上本人实现过自定义注解的话,都晓得别离是什么意思。

  • @SpringBootConfiguration继承自@Configuration,二者性能也统一,标注以后类是配置类。
  • @ComponentScan用于类或接口上次要是指定扫描门路,跟Xml外面的<context:component-scan base-package="" />配置一样。springboot如果不写这个扫描门路的话,默认就是启动类的门路。
  • @EnableAutoConfiguration
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {

这个注解咱们重点看下AutoConfigurationImportSelector这个类getCandidateConfigurations
这个办法外面通过SpringFactoriesLoader.loadFactoryNames()扫描所有具备META-INF/spring.factoriesjar包( spring.factories 咱们能够了解成 Spring Boot 本人的 SPI 机制)。
spring-boot-autoconfigure-x.x.x.x.jar里就有一个spring.factories文件。spring.factories文件由一组一组的Key = value的模式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个以AutoConfiguration结尾的类名的列表,有redis、mq等这些类名以逗号分隔。


咱们在回到getAutoConfigurationEntry这个办法当执行完getCandidateConfigurations这个办法的时候咱们能够看到此时总共加载了127个主动配置类。

这些类难道都要加载进去吗?springboot还是没有那么傻的,它提倡的话是按需加载。

  • 它会去掉反复的类
  • 过滤掉咱们配置了exclude注解的类上面配置就会过滤掉RestTemplateAutoConfiguration这个类

  • 通过下面的解决,剩下的这写主动配置的类如果要起作用的话,是须要满足肯定的条件的。这些条件的满足的话spring boot是通过条件注解来实现的。
@ConditionalOnBean:当容器里有指定Bean的条件下
@ConditionalOnClass:当类门路下有指定的类的条件下
@ConditionalOnExpression:基于SpEL表达式为true的时候作为判断条件才去实例化
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的地位
@ConditionalOnMissingBean:当容器里没有指定Bean的状况下
@ConditionalOnMissingClass:当容器里没有指定类的状况下
@ConditionalOnWebApplication:以后我的项目时Web我的项目的条件下
@ConditionalOnNotWebApplication:以后我的项目不是Web我的项目的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类门路是否有指定的值
@ConditionalOnOnSingleCandidate:当指定Bean在容器中只有一个,或者有多个然而指定首选的Bean

这些注解都组合了@Conditional注解,只是应用了不同的条件组合最初为true时才会去实例化须要实例化的类,否则疏忽过滤掉。咱们在回到代码能够看到通过了条件判断过滤后咱们剩下符合条件的主动配置类只剩23个了。其余的都是因为不满足条件注解而被过滤了。

如果咱们想晓得哪些主动配置类被过滤了,是因为什么起因被过滤了,以及加载了哪些类等。spring boot都为咱们记录了日志。还是十分贴心的。咱们能够调整下咱们日志的级别改为debug。而后咱们就能看到以下日志了

这里就截取了局部日志。总共别离有上面四局部日志:

  • Positive matches@Conditional条件为真,配置类被Spring容器加载。
  • Negative matches: @Conditional条件为假,配置类未被Spring容器加载。
  • Exclusions: 咱们明确了不须要加载的类。比方在下面启动类配置的RestTemplateAutoConfiguration
  • Unconditional classes: 主动配置类不蕴含任何类级别的条件,也就是说,类始终会被主动加载。

主动配置失效

咱们以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何失效,比方:server.port=88,是如何失效的(当然不配置也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat)。

// 标记为配置类@Configuration(proxyBeanMethods = false)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)// 如果有ServletRequest.class 才会失效@ConditionalOnClass(ServletRequest.class)@ConditionalOnWebApplication(type = Type.SERVLET)// 把@ConfigurationProperties注解的类注入为Spring容器的Bean。@EnableConfigurationProperties(ServerProperties.class)@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })public class ServletWebServerFactoryAutoConfiguration {

咱们能够发现EnableConfigurationProperties注解外面配置的ServerProperties.class

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)public class ServerProperties {    /**     * Server HTTP port.     */    private Integer port;

在这个类上有一个注解:@ConfigurationProperties,它的作用就是从配置文件中绑定属性到对应的bean上(也就是把咱们application.properties对应的server.port映射到ServerProperties 类中的port属性)而@EnableConfigurationProperties这个注解就是把曾经绑定了属性的beanServerProperties)注入到spring容器中(相当于@Component注解一样)。
所有在配置文件中能配置的属性都是在xxxxPropertites类中封装着,配置文件能配置什么就能够参照某个性能对应的这个属性类。
到当初为止应该能答复文章结尾的那个问题了,面试的时候应该不须要答复的这么具体能够参考下以下答案:

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有主动配置类,并对其进行加载,而这些主动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig模式的Spring容器配置类,它能通过以Properties结尾命名的类中获得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

在网上找了一张图,基本上把主动拆卸的流程给说分明了。

总结

  • SpringBoot启动会加载大量的主动配置类(通过“SPI”的形式),而后会依据条件注解保留一些须要的类。
  • 咱们新引入一个组件,能够先看看springBoot是否曾经有默认的提供。
  • SpringBoot根本实现了“零配置“,并且开箱即用。

完结

  • 因为本人满腹经纶,难免会有纰漏,如果你发现了谬误的中央,还望留言给我指出来,我会对其加以修改。
  • 如果你感觉文章还不错,你的转发、分享、赞叹、点赞、留言就是对我最大的激励。
  • 感谢您的浏览,非常欢送并感谢您的关注。


站在伟人的肩膀上摘苹果:
https://docs.spring.io/spring...
https://blog.csdn.net/u014745...
https://afoo.me/posts/2015-07...