如何开发自己的 Spring Boot Starter

我们在使用 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 Starter

在Spring Boot中,我们最大的感受是配置仿佛都被做好了,直接使用即可,这就是

spring-boot-autoconfigure .   每个starter都有一个名为
spring.factories

的文件,存放在META-INF目录下,其中的内容类似下面这个样子:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=/
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

所有需要自动配置的Class,都需要配置成keyEnableAutoConfiguration 的。

我们来看类的内部

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

Class 之上, 有不少注解来标识,有几点需要关注的:

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

  • 几个 @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啦。

相关阅读:

Spring 的自定义Schema是如何解析生效的

Tomcat 是怎样处理 SpringBoot应用的?

关注『  Tomcat那些事儿    ,发现更多精彩文章!了解各种常见问题背后的原理与答案。深入源码,分析细节,内容原创,欢迎关注。

如何开发自己的 Spring Boot Starter

                        转发是最大的支持 ,谢谢

更多精彩内容:

一台机器上安装多个Tomcat 的原理(回复001)

监控Tomcat中的各种数据 (回复002)

启动Tomcat的安全机制(回复003)

乱码问题的原理及解决方式(回复007)

Tomcat 日志工作原理及配置(回复011)

web.xml 解析实现(回复 012)

线程池的原理( 回复 014)

Tomcat 的集群搭建原理与实现 (回复 015)

类加载器的原理 (回复 016)

类找不到等问题 (回复 017)

代码的热替换实现(回复 018)

Tomcat 进程自动退出问题 (回复 019)

为什么总是返回404? (回复 020)

PS: 对于一些 Tomcat常见问 ,在公众号的【 常见问题 】菜单中,有需要的朋友欢迎关注查看。

原文 

https://mp.weixin.qq.com/s/Eg_z26HncGhJRUSSIVPvcA

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 如何开发自己的 Spring Boot Starter

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址