如何开发本身的Spring Boot Starter

mp.weixin.qq.com/s/Eg_z26Hnc…


咱们在使用 Spring Boot 的过程当中,每每都是在pom.xml里加了一系列的依赖,而后启支一个包含main方法的Application,一切就OK啦。给你个人感受,就像是本身要动手作个菜,本身再也不须要准备每一部分的原材料,直接购买包装好的一份菜的原料,下锅便可
那咱们详细看下,这份「包装好」的原料中,到底作了些什么。web

添加Starter依赖

这里添加的依赖,除了咱们以前在Maven中熟悉的以外,还有一些都是长这个样子:
名为xxx-starter,好比spring

<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>复制代码

具体这些starter是怎么起做用的呢,他们何时开始工做的?bash

一切都要从入口处提及。咱们以上面的starter为例,看到这个mybatis的starter,其对应的pom中,包含这些依赖mybatis

<dependencies>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter</artifactId>    </dependency>    <dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-jdbc</artifactId>    </dependency>    <dependency>      <groupId>org.mybatis.spring.boot</groupId>      <artifactId>mybatis-spring-boot-autoconfigure</artifactId>    </dependency>    <dependency>      <groupId>org.mybatis</groupId>      <artifactId>mybatis</artifactId>    </dependency>    <dependency>      <groupId>org.mybatis</groupId>      <artifactId>mybatis-spring</artifactId>    </dependency>  </dependencies>复制代码

咱们看到,至关于咱们添加了一个Starter的依赖,其背后会引入许多其定义的其余依赖,经过 Maven 的传递依赖,这些都会被自动添加了进来。spring-boot

自动配置

相比传统的依赖,咱们看到其中包含这样一个:mybatis-spring-boot-autoconfigure,这也是每一个Starter的秘密所在:「AutoConfigure」
它会在实现时,考虑应用中的其余部分因素,「推断」你所须要的 Spring 配置。工具

在Spring Boot中,咱们最大的感觉是配置仿佛都被作好了,直接使用便可,这就是
spring-boot-autoconfigure. 每一个starter都有一个名为spring.factories
的文件,存放在META-INF目录下,其中的内容相似下面这个样子:ui

# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration复制代码

全部须要自动配置的Class,都须要配置成key是EnableAutoConfiguration的。
咱们来看类的内部this

@Configuration@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})@ConditionalOnBean({DataSource.class})@EnableConfigurationProperties({MybatisProperties.class})@AutoConfigureAfter({DataSourceAutoConfiguration.class})public class MybatisAutoConfiguration {复制代码

Class 之上, 有很多注解来标识,有几点须要关注的:spa

  • 其中有标准的 Spring 配置注解 @Configurationcode

  • 几个@ConditionalXX

  • 标识执行顺序的@AutoConfigureAfter

其中,@ConditionalOnClass 标识 SqlSessionFactory类存在时,执行该配置, @ConditionalOnBean标识DataSource Bean在 Spring Context时,执行配置。

这些spring.factories是怎么被识别的呢? 这就得夸下 Spring 的FactoriesLoader了。
看下官方文档说明

Auto-configuration classes are regular Spring {@link Configuration} beans. They are located using the {@link SpringFactoriesLoader} mechanism (keyed against this class).
Generally auto-configuration beans are {@link Conditional @Conditional} beans (most
often using {@link ConditionalOnClass @ConditionalOnClass} and
{@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).

启动的时候,根据ClassLoader中的jar,扫描全部 spring.factories,将其中符合条件的过滤出来,执行对应的配置。重点能够关注下

AutoConfigurationImportFilter类,

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,                this.beanClassLoader);    }复制代码
private List<String> filter(List<String> configurations,            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<String> 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<String> configurations = getCandidateConfigurations(annotationMetadata,                attributes);        configurations = removeDuplicates(configurations);        Set<String> 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 的形式提供出来,供别人使用。步骤也还算清晰,照葫芦画瓢。

  • 先建立本身的模块

  • 增长须要用到的依赖

  • 建立对应的 AutoConfiguration

  • 建立META-INF/spring.factories 文件

此时,就不须要再将 Spring Boot 作为 Parent依赖,在单独的依赖中增长

<dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-autoconfigure</artifactId>            <version>2.0.6.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter</artifactId>            <version>2.0.6.RELEASE</version>        </dependency>复制代码

AutoConfiguration类也简单,照上面的建立一个

@Configuration@ConditionalOnClass(HelloService.class)public class HelloServiceAutoConfiguration {复制代码

而后,增长文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.demo.HelloServiceAutoConfiguration复制代码

在须要这个服务的地方,直接引入依赖就OK啦。

相关文章
相关标签/搜索