为了账号安全,请及时绑定邮箱和手机立即绑定

如何开发自己的Spring Boot Starter

标签:
SpringBoot

我们在使用 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 配置。

webp

在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 之上, 有不少注解来标识,有几点需要关注的:

其中有标准的 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啦。



作者:Java高级技术
链接:https://www.jianshu.com/p/1a7c15187f8a


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
50
获赞与收藏
175

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消