主要讲解SpringBoot自动化配置原理,结合系列1的内容,可以形成SpringBoot原理知识体系闭环。
SpringBoot只是个框架,前期我主要了解该框架内部的执行原理,然后掌握SpringBoot的基本使用姿势,就达到我的初步目标,后面打算结合公司具体的项目,然后再慢慢学习。
基本介绍
一般认为,SpringBoot 微框架从两个主要层面影响 Spring 社区的开发者们:
- 基于 Spring 框架的“约定优先于配置(COC)”理念以及最佳实践之路。
- 提供了针对日常企业应用研发各种场景的 spring-boot-starter 自动配置依赖模块,如此多“开箱即用”的依赖模块,使得开发各种场景的 Spring 应用更加快速和高效。
我理解,一个就是去XML配置,一个就是自动化配置。
SpringBoot 提供的这些“开箱即用”的依赖模块都约定以 spring-boot-starter- 作为命名的前缀,常用的如下:
- spring-boot-starter-logging
- spring-boot-starter-web
- spring-boot-starter-jdbc
- spring-boot-starter-aop
- spring-boot-starter-security
- spring-boot-starter-actuator
并且皆位于 org.springframework.boot 包或者命名空间下(虽然 SpringBoot 的官方参考文档中提到不建议大家使用 spring-boot-starter- 来命名自己写的类似的自动配置依赖模块,但实际上,配合不同的 groupId,这不应该是什么问题)。
那么自动化配置,在SpringBoot的执行流程中处于哪个环节呢?它又是怎么自动化加载配置的呢?其实我们在之前的文章中,就可以找到部分答案。
知识回顾
@EnableAutoConfiguration
在文章《【Spring Boot系列1】一文带你了解Spring Boot(上)》,我们介绍过@EnableAutoConfiguration注解,@EnableAutoConfiguration 是借助 @Import 的帮助,将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器,就跟一只“八爪鱼”一样:
执行流程
在文章《【Spring Boot系列1】一文带你了解Spring Boot(下)》,我们讲解了SpringApplication.run执行流程,第9步描述“最核心的一步,将之前通过 @EnableAutoConfiguration 获取的所有配置以及其他形式的 IoC 容器配置加载到已经准备完毕的 ApplicationContext。”
所以自动化配置就是在“创建好上下文”之后,在“通知应用装载上下文”之前进行的,它做了啥事情呢?其实就是将自动化配置信息完善到上下文中,而这个自动加载的过程,主要就靠@EnableAutoConfiguration。所以你也可以理解,通过该注解给Spring提供一个完善的上下文环境。
这里讲述了自动化配置的执行位置、执行方式、执行后的结果,那最后执行依赖的形式是什么呢?具体有啥用呢?大家可能还是觉得有些抽象,举个栗子:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
大白话概括一下,其实就是把这些东东加载到SpringBoot中,方便大家使用,比如jdbc、web、log、security等等,我把他们理解为“扩展功能”,感觉更贴切一些,支持“自动加载”和“开箱即用”。
自动化配置
自动化配置流程
- SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration;
- @EnableAutoConfiguration中使用了 @Import({AutoConfigurationImportSelector.class})向容器中添加了一些组件(自动配置类):
- 扫描所有jar包类路径下 META‐INF/spring.factories,把扫描到的这些文件的内容包装成properties对象,从properties中获取到 EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中,即将类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中。
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration
- 每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中,用他们来做自动配置。
盗用网上的一副图,感觉画的挺好的,就是有点大,建议大家在电脑上看:
mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。
原理分析
以HttpEncodingAutoConfiguration为例,就是以前在web.xml中配置的CharacterEncodingFilter过滤器:
//表示这是一个配置类,相当于以前编写的Spring配置文件 @Configuration //启用HttpProperties类的ConfigurationProperties功能,通过配置文件为属性注入值,并将其添加到容器中 @EnableConfigurationProperties({HttpProperties.class}) //当该应用是web应用时才生效 @ConditionalOnWebApplication(type = Type.SERVLET) //必须包含CharacterEncodingFilter类才生效 @ConditionalOnClass({CharacterEncodingFilter.class}) //如果配置文件中有spring.http.encoding选项则该配置生效,否则不生效。但是默认已经生效了 @ConditionalOnProperty(prefix = "spring.http.encoding",value = {"enabled"},matchIfMissing = true) public class HttpEncodingAutoConfiguration { private final Encoding properties; //将容器中的HttpProperties注入 public HttpEncodingAutoConfiguration(HttpProperties properties) { this.properties = properties.getEncoding(); } //将返回的filter添加到容器中,作为bean @Bean //如果容器中没有这个bean才会生效 @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE)); return filter; } }
//从配置文件中获取指定的值,然后绑定到指定的属性值 @ConfigurationProperties( prefix = "spring.http" ) public class HttpProperties { private Charset charset; private Boolean force; private Boolean forceRequest; private Boolean forceResponse; private Map<Locale, Charset> mapping; }
注意:
- 根据当前情况进行判断,决定配置类是否生产,如果不满足条件自动配置就不会生效
- 自动配置类xxAutoConfiguration的属性是从对应的xxProperties类中获取
- xxProperties类中的信息是通过配置文件注入绑定的,可以通过配置文件指定属性的值
自动配置类必须在一定的条件下才能生效,我们怎么知道哪些自动配置类生效,我们可以通过启用debug=true属性,来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效:
========================= AUTO‐CONFIGURATION REPORT ========================= Positive matches:(自动配置类启用的) ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ DispatcherServletAutoConfiguration matched: ‐ @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) ‐ @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition) Negative matches:(没有启动,没有匹配成功的自动配置类) ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ActiveMQAutoConfiguration: Did not match: ‐ @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition) AopAutoConfiguration: Did not match: ‐ @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)
简单总结一下:
- SpringBoot在启动时会加载大量的自动配置类
- 通过自动配置了向容器中添加组件
- 通过这些组件自动完成许多功能,从而简化配置
SpringBoot全流程总结
为了让大家形成知识闭环,我将之前的SpringBoot执行流程整体,通过几幅图再整体串一下:
什么?图片字体太小,看不清楚?那我放大一下。
构建SpringApplication:
启动SpringApplication:
自动化配置加载:
如果还是看不清楚,就在电脑上看吧,楼哥也没办法。
上图为SpringBoot启动结构图:
- 构建SpringApplication:进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器
- 启动SpringApplication:实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块
- 自动化配置加载:是自动化配置模块,该模块作为springboot自动配置核心,在后面的分析中会详细讨论。
前面知识的更多细节,请参考《【Spring Boot系列1】一文带你了解Spring Boot(上)》、《【Spring Boot系列1】一文带你了解Spring Boot(下)》两文。
后记
上面需要总结的知识,基本都已经总结过了,这里就写点水文吧。
后面打算花些时间,仔细研读《JAVA编程思想》和《深入理解Java虚拟机》这两本书,开启Java学习的第二个阶段--进阶阶段!到时也给自己列个详细的Plan,当然,如果中途计划有变,再做调整。