专栏首页阿杜的世界Spring Boot:定制自己的starter

Spring Boot:定制自己的starter

在学习Spring Boot的过程中,接触最多的就是starter。可以认为starter是一种服务——使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要具体的配置信息,由Spring Boot自动通过classpath路径下的类发现需要的Bean,并织入bean。举个例子,spring-boot-starter-jdbc这个starter的存在,使得我们只需要在BookPubApplication下用@Autowired引入DataSource的bean就可以,Spring Boot会自动创建DataSource的实例。

这里我们会用一个不太规范的starter展示Spring Boot的自动配置的运行原理。Spring Boot的自动配置、Command-line Runner一文中曾利用StartupRunner类在程序运行启动后首先查询数据库中书的数目,现在换个需求:在系统启动后打印各个实体的数量

How Do

  • 新建一个模块db-count-starter,然后修改db-count-starter模块下的pom文件,增加对应的库。
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <!-- version继承父模块的-->
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-commons</artifactId>
        <version>1.9.3.RELEASE</version>
    </dependency></dependencies>
  • 新建包结构com/test/bookpubstarter/dbcount,然后新建DbCountRunner类,实现CommandLineRunner接口,在run方法中输出每个实体的数量。
package com.test.bookpubstarter.dbcount;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;

public class DbCountRunner implements CommandLineRunner {
    protected final Logger logger = LoggerFactory.getLogger(DbCountRunner.class);
    private Collection<CrudRepository> repositories;

    public DbCountRunner(Collection<CrudRepository> repositories) {
        this.repositories = repositories;
    }
    @Override
    public void run(String... strings) throws Exception {
        repositories.forEach(crudRepository -> {
            logger.info(String.format("%s has %s entries",
                    getRepositoryName(crudRepository.getClass()),
                    crudRepository.count()));
        });
    }

    private static String getRepositoryName(Class crudRepositoryClass) {
        for (Class repositoryInterface : crudRepositoryClass.getInterfaces()) {
            if (repositoryInterface.getName().startsWith("com.test.bookpub.repository")) {
                return repositoryInterface.getSimpleName();
            }
        }
        return "UnknownRepository";
    }
}
  • 增加自动配置文件DbCountAutoConfiguration
package com.test.bookpubstarter.dbcount;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;

@Configuration
public class DbCountAutoConfiguration {
    @Bean
    public DbCountRunner dbCountRunner(Collection<CrudRepository> repositories) {
        return new DbCountRunner(repositories);
    }
}
  • 在src/main/resources目录下新建META-INF文件夹,然后新建spring.factories文件,这个文件用于告诉Spring Boot去找指定的自动配置文件,因此它的内容是
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.test.bookpubstarter.dbcount.DbCountAutoConfiguration
  • 在之前的程序基础上,在顶层pom文件中增加starter的依赖
<dependency>
   <groupId>com.test</groupId>
   <artifactId>db-count-starter</artifactId>
   <version>0.0.1-SNAPSHOT</version>
</dependency>
  • 把StartupRunner相关的注释掉,然后在main函数上右键Run BookPubApplication.main(...),可以看出我们编写的starter被主程序使用了。

自己的starter简单演示.png

分析

正规的starter是一个独立的工程,然后在maven中新仓库注册发布,其他开发人员就可以使用你的starter了。

常见的starter会包括下面几个方面的内容:

  1. 自动配置文件,根据classpath是否存在指定的类来决定是否要执行该功能的自动配置。
  2. spring.factories,非常重要,指导Spring Boot找到指定的自动配置文件。
  3. endpoint:可以理解为一个admin,包含对服务的描述、界面、交互(业务信息的查询)
  4. health indicator:该starter提供的服务的健康指标

在应用程序启动过程中,Spring Boot使用SpringFactoriesLoader类加载器查找org.springframework.boot.autoconfigure.EnableAutoConfiguration关键字对应的Java配置文件。Spring Boot会遍历在各个jar包种META-INF目录下的spring.factories文件,构建成一个配置文件链表。除了EnableAutoConfiguration关键字对应的配置文件,还有其他类型的配置文件:

  • org.springframework.context.ApplicationContextInitializer
  • org.springframework.context.ApplicationListener
  • org.springframework.boot.SpringApplicationRunListener
  • org.springframework.boot.env.PropertySourceLoader
  • org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider
  • org.springframework.test.contex.TestExecutionListener

Spring Boot的starter在编译时不需要依赖Spring Boot的库。这个例子中依赖spring boot并不是因为自动配置要用spring boot,而仅仅是因为需要实现CommandLineRunner接口。

两个需要注意的点

  1. @ConditionalOnMissingBean的作用是:只有对应的ban在系统中都没有被创建,它修饰的初始化代码块才会执行,用户自己手动创建的bean优先
  2. Spring Boot starter如何找到自动配置文件(xxxxAutoConfiguration之类的文件)?
  • spring.factories:由Spring Boot触发探测classpath目录下的类,进行自动配置;
  • @Enable:有时需要由starter的用户触发*查找自动配置文件的过程。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 在Spring Boot项目中使用Spock框架

    Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring Boot项目中使用该框架写优雅、高效以及DSL...

    阿杜
  • Spring Boot with Redis

    Spring Boot是为了简化Spring开发而生,从Spring 3.x开始,Spring社区的发展方向就是弱化xml配置文件而加大注解的戏份。最近召开的S...

    阿杜
  • Java Web技术经验总结(七)

    如果我在获取kp参数的时候使用#符号,则执行过程中,参数会以占位符的形式出现,并按照参数的类型赋值。当表中该参数有索引的时候,只有类型对才能用到索引。例如,这里...

    阿杜
  • springcloud(十):服务网关zuul初级篇

    前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud...

    纯洁的微笑
  • 单点登录设计 之Spring Cloud GateWay

    Spring Cloud Gateway该项目提供了一个用于在Spring MVC之上构建API网关的库。 Spring Cloud Gateway旨在提供一种...

    架构探险之道
  • Python进阶学习笔记【干货分享】(二)

    print(id(a))a = 4print(id(a))# 重新赋值之后,内存地址发生改变

    商业新知
  • Arduino 机器学习实战入门(上)

    这是来自Arduino团队的Sandeep Mistry和Dominic Pajak的一篇客座文章。

    AiTechYun
  • 干货 | Python进阶系列之学习笔记(二)

    磐创AI
  • Spring和SpringDataJpa整合详解

    当然还有其他中间件,主流使用的就是Mybatis和Spring-data-jpa。

    品茗IT
  • 图解LeetCode第 279 号问题: 完全平方数

    给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

    五分钟学算法

扫码关注云+社区

领取腾讯云代金券