• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

如何开发自己的Spring Boot Starter

技术杂谈 勤劳的小蚂蚁 2个月前 (02-13) 77次浏览 已收录 0个评论 扫描二维码

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

添加Starter依赖

这里添加的依赖,除了我们之前在Maven中熟悉的之外,还有一些都是长这个样子:
名为xxx-starter,比如
<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是怎么起作用的呢,他们什么时候开始工作的?
一切都要从入口处说起。我们以上面的starter为例,看到这个mybatis的starter,其对应的pom中,包含这些依赖
<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 的传递依赖,这些都会被自动添加了进来。

自动配置

相比传统的依赖,我们看到其中包含这样一个: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.classSqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration {
Class 之上, 有不少注解来标识,有几点需要关注的:
  • 其中有标准的 Spring 配置注解 @Configuration
  • 几个@ConditionalXX
  • 标识执行顺序的@AutoConfigureAfter
其中,@ConditionalOnClass 标识 SqlSessionFactory类存在时,执行该配置, @ConditionalOnBean标识DataSourceBean在 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啦。

丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:如何开发自己的Spring Boot Starter
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

您必须 登录 才能发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00