在学习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会包括下面几个方面的内容:

  • 自动配置文件,根据classpath是否存在指定的类来决定是否要执行该功能的自动配置。
  • spring.factories,非常重要,指导Spring Boot找到指定的自动配置文件。
  • endpoint:可以理解为一个admin,包含对服务的描述、界面、交互(业务信息的查询)
  • 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接口。

两个需要注意的点

    • @ConditionalOnMissingBean的作用是:只有对应的ban在系统中都没有被创建,它修饰的初始化代码块才会执行,用户自己手动创建的bean优先

    • Spring Boot starter如何找到自动配置文件(xxxxAutoConfiguration之类的文件)?

      • spring.factories:由Spring Boot触发探测classpath目录下的类,进行自动配置;
      • @Enable:有时需要由starter的用户触发*查找自动配置文件的过程。

Spring Boot:定制自己的starter的更多相关文章

  1. Spring Boot 定制与优化内置的Tomcat容器

    1.Spring Boot定制与优化内置Tomcat容器. > 内置的容器有三个分别是Undertow.Jetty.Tomcat,Spring Boot 对这三个容器分别进行了实现,它们上层接口 ...

  2. 75. Spring Boot 定制URL匹配规则【从零开始学Spring Boot】

    在之前有一篇文章说了,博客名称从原来的<从零开始学Spring Boot>更改为<Spring Boot常见异常汇总>,后来写了几篇文章之后发展,有些文章还是一些知识点,所以后 ...

  3. spring boot定制Jackson ObjectMapper,为什么不生效

    先说结论: 项目中定制了spring 的redisTemplate,而这个template没有使用我自定义的Jackson ObjectMapper.所以不生效. 下面是详细过程: 起因是spring ...

  4. Spring Boot 定制URL匹配规则的方法

    事情的起源:有人问我,说编写了一个/hello访问路径,但是吧,不管是输入/hello还是/hello.html,还是/hello.xxx都能进行访问.当时我还以为他对代码进行处理了,后来发现不是,后 ...

  5. Spring Boot定制启动图案

    启动图案 Spring Boot在启动的时候会显示一个默认的Spring的图案,对应的类为SpringBootBanner. . ____ _ __ _ _ /\\ / ___'_ __ _ _(_) ...

  6. spring boot 四大组件之Starter

    1.概述 依赖管理是任何复杂项目的关键方面.手动完成这些操作并不理想; 你花在它上面的时间越多,你在项目的其他重要方面所花费的时间就越少. 构建Spring Boot启动器是为了解决这个问题.Star ...

  7. Spring Boot必备技能之Starter自定义

    本文摘自于<Spring Cloud微服务 入门 实战与进阶>一书.  作者:尹吉欢 Spring Boot的方便体现在简化了很多繁琐的配置,对开发人员来说是一个福音,通过引入各种Spri ...

  8. Spring Boot中如何自定义starter?

    Spring Boot starter 我们知道Spring Boot大大简化了项目初始搭建以及开发过程,而这些都是通过Spring Boot提供的starter来完成的.品达通用权限系统就是基于Sp ...

  9. Spring Boot 整合Mybatis非starter时,mapper一直无法注入解决

    本来呢,直接使用mybatis-spring-boot-starter还是挺好的,但是我们系统比较复杂,有多个数据源,其中一个平台自己的数据源,另外一些是动态配置出来的,两者完全没有关系.所以直接使用 ...

  10. Spring boot 定制自己的错误

    1).如何定制错误的页面: ​ 1).有模板引擎的情况下:error/状态码; [将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下],发生此状态码的错误就会来到 对 ...

随机推荐

  1. RabbitMQ安装与初始配置【转载】

    Erlang安装 rabbitmq依赖于Erlang,需先安装,推荐安装rabbitmq/erlang-rpm: #clone源码 git clone https://github.com/rabbi ...

  2. MongoDb企业应用实战(一) 写在MongoDB应用介绍之前(ii)

    上一篇: MongoDb企业应用实战(一) 写在MongoDB应用介绍之前(i) 有段时间没跟大家去分享和探讨过一些问题,分享过一些经验了(失败过的,痛苦过的才最有看点啊,不知道各位同仁们怎么去看这个 ...

  3. byte字节的输入流 建议使用字节数组形式接受内容 因为当输入流是汉字时候 会超出字节的范围 出现无法读取的现象

    byte字节的输入流 建议使用字节数组形式接受内容 因为当输入流是汉字时候 会超出字节的范围 出现无法读取的现象

  4. 【bzoj1877】[SDOI2009]晨跑 费用流

    题目描述 Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑.仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑. 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街 ...

  5. 使用canvas控制gif图片的播放与暂停

    if ('getContext' in document.createElement('canvas')) { HTMLImageElement.prototype.play = function() ...

  6. 完全理解Python的 '==' 和 'is'

    '==' 比较的是两个对象的值 'is' 比较的是两个对象的内存地址(id) 下面我们着重理解 'is'.对于这个,我们需要知道:小整数对象池,大整数对象池,以及intern机制 小整数池:Pytho ...

  7. python将字符串转换成字典的几种方法

    当我们遇到类似于{‘a’:1, 'b':2, 'c':3}这种字符串时,想要把它转换成字典进行处理,可以使用以下几种方法: 1. Python自带的eval函数(不安全) dictstr = '{&q ...

  8. JS内存空间详细图解

    JS内存空间详细图解 变量对象与堆内存 var a = 20; var b = 'abc'; var c = true; var d = { m: 20 } 因为JavaScript具有自动垃圾回收机 ...

  9. CF9d How many trees?

    题意:求节点数为n的,高度大于等于h的二叉树的个数. 题解: 一开始没看到二叉树的限制,,,想了好久.因为数据范围很小,所以可以考虑一些很暴力的做法. 有2种DP方式都可以过. 1,f[i][j]表示 ...

  10. BZOJ5292 & 洛谷4457 & LOJ2513:[BJOI2018]治疗之雨——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=5292 https://www.luogu.org/problemnew/show/P4457 ht ...