我们在使用 Spring Boot 的过程中,往往都是在pom.xml里加了一系列的依赖,然后启支一个包含main方法的Application,一切就OK啦。给你我的感觉,就像是自己要动手做个菜,自己不再需要准备每一部分的原材料,直接购买包装好的一份菜的原料,下锅即可。

东风网站制作公司哪家好,找创新互联公司!从网页设计、网站建设、微信开发、APP开发、响应式网站开发等网站项目制作,到程序开发,运营维护。创新互联公司于2013年创立到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联公司。
那我们详细看下,这份「包装好」的原料中,到底做了些什么。
添加Starter依赖
这里添加的依赖,除了我们之前在Maven中熟悉的之外,还有一些都是长这个样子:
名为xxx-starter,比如
org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.2 
具体这些starter是怎么起作用的呢,他们什么时候开始工作的?
一切都要从入口处说起。我们以上面的starter为例,看到这个mybatis的starter,其对应的pom中,包含这些依赖
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-jdbc org.mybatis.spring.boot mybatis-spring-boot-autoconfigure org.mybatis mybatis org.mybatis mybatis-spring 
我们看到,相当于我们添加了一个Starter的依赖,其背后会引入许多其定义的其他依赖,通过 Maven 的传递依赖,这些都会被自动添加了进来。
自动配置
相比传统的依赖,我们看到其中包含这样一个:mybatis-spring-boot-autoconfigure,这也是每个Starter的秘密所在:「AutoConfigure」
它会在实现时,考虑应用中的其他部分因素,「推断」你所需要的 Spring 配置。
在Spring Boot中,我们***的感受是配置仿佛都被做好了,直接使用即可,这就是
spring-boot-autoconfigure. 每个starter都有一个名为spring.factories
的文件,存放在META-INF目录下,其中的内容类似下面这个样子:
- # Auto Configure
 - org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 - org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
 
所有需要自动配置的Class,都需要配置成key是EnableAutoConfiguration的。
我们来看类的内部
- @Configuration
 - @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
 - @ConditionalOnBean({DataSource.class})
 - @EnableConfigurationProperties({MybatisProperties.class})
 - @AutoConfigureAfter({DataSourceAutoConfiguration.class})
 - public class MybatisAutoConfiguration {
 
Class 之上, 有不少注解来标识,有几点需要关注的:
其中,@ConditionalOnClass 标识 SqlSessionFactory类存在时,执行该配置, @ConditionalOnBean标识DataSource Bean在 Spring Context时,执行配置。
这些spring.factories是怎么被识别的呢? 这就得夸下 Spring 的FactoriesLoader了。
看下官方文档说明
启动的时候,根据ClassLoader中的jar,扫描所有 spring.factories,将其中符合条件的过滤出来,执行对应的配置。重点可以关注下
- protected List
 getAutoConfigurationImportFilters() { - return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
 - this.beanClassLoader);
 - }
 
- AutoConfigurationMetadata autoConfigurationMetadata) {
 - long startTime = System.nanoTime();
 - String[] candidates = StringUtils.toStringArray(configurations);
 - boolean[] skip = new boolean[candidates.length];
 - boolean skipped = false;
 - for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
 - invokeAwareMethods(filter);
 - boolean[] match = filter.match(candidates, autoConfigurationMetadata);
 - for (int i = 0; i < match.length; i++) {
 - if (!match[i]) {
 - skip[i] = true;
 - skipped = true;
 - }
 - }
 - }
 - if (!skipped) {
 - return configurations;
 - }
 - List
 result = new ArrayList<>(candidates.length); - for (int i = 0; i < candidates.length; i++) {
 - if (!skip[i]) {
 - result.add(candidates[i]);
 - }
 - }
 - return new ArrayList<>(result);
 - }
 - public String[] selectImports(AnnotationMetadata annotationMetadata) {
 - if (!isEnabled(annotationMetadata)) {
 - return NO_IMPORTS;
 - }
 - AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
 - .loadMetadata(this.beanClassLoader);
 - AnnotationAttributes attributes = getAttributes(annotationMetadata);
 - List
 configurations = getCandidateConfigurations(annotationMetadata, - attributes);
 - configurations = removeDuplicates(configurations);
 - Set
 exclusions = getExclusions(annotationMetadata, attributes); - checkExcludedClasses(configurations, exclusions);
 - configurations.removeAll(exclusions);
 - configurations = filter(configurations, autoConfigurationMetadata);
 - fireAutoConfigurationImportEvents(configurations, exclusions);
 - return StringUtils.toStringArray(configurations);
 - }
 
经过这里的执行之后, filter方法把符合条件的过滤出来了。
创建自定义Starter
经过上面两步,我们大概知道 Starter的工作原理。有时候,我们需要对外提供一些工具组件时,也想以 Starter 的形式提供出来,供别人使用。步骤也还算清晰,照葫芦画瓢。
此时,就不需要再将 Spring Boot 做为 Parent依赖,在单独的依赖中增加
org.springframework.boot spring-boot-autoconfigure 2.0.6.RELEASE org.springframework.boot spring-boot-starter 2.0.6.RELEASE 
AutoConfiguration类也简单,照上面的创建一个
- @Configuration
 - @ConditionalOnClass(HelloService.class)
 - public class HelloServiceAutoConfiguration {
 
然后,增加文件
- org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.demo.HelloServiceAutoConfiguration
 
在需要这个服务的地方,直接引入依赖就OK啦。
【本文为专栏作者“侯树成”的原创稿件,转载请通过作者微信公众号『Tomcat那些事儿』获取授权】