1. 首页
  2. IT资讯

手把手教你定制标准Spring Boot starter,看起来神清气爽

“u003Ch1u003Eu003Cstrongu003E写在前面u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E我们每次构建一个 Spring 应用程序时,我们都不希望从头开始实现具有「横切关注点」的内容;相反,我们希望一次性实现这些功能,并根据需要将它们包含到任何我们要构建的应用程序中u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003E横切关注点u003Cu002Fpu003Eu003Cpu003E横切关注点: 指的是一些具有横越多个模块的行为 (来自维基百科的介绍)u003Cbru002Fu003Eu003Cstrongu003E说白了就是多个项目或模块都可以用到的内容,比如一个 SDKu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cpu003E在Spring Boot中,用于表示提供这种横切关注点的模块的术语是 u003Cstrongu003Estarteru003Cu002Fstrongu003E,通过依赖 starter 可以轻松使用其包含的一些功能特性,u003Cstrongu003E无论你的工作中是否会构建自己的 starter,你都要具有构建 「starter」的思想u003Cu002Fstrongu003E,本文将结合 Spring Boot 官方标准构建一个简单的 starteru003Cu002Fpu003Eu003Ch1u003Eu003Cstrongu003E自定义 starteru003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E在我们深入了解如何自定义 starter 之前,为了更好的理解我们每一步在干什么,以及 starter 是如何起作用的,我们先从宏观角度来看 starter 的结构组成到底是什么样的u003Cu002Fpu003Eu003Cpu003E通常一个完整的 starter 需要包含下面两个组件:u003Cu002Fpu003Eu003Col start=”1″u003Eu003Cliu003EAuto-Configure Moduleu003Cu002Fliu003Eu003Cliu003EStarter Moduleu003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003E如果你看下面这两个组件的解释有些抽象,大概了解一下,阅读完该文章回看这里就会豁然开朗了u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EAuto-Configure Moduleu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EAuto-Configure Module (自动配置模块) 是包含自动配置类的 Maven 或 Gradle 模块。通过这种方式,u003Cstrongu003E我们可以构建可以自动贡献于应用程序上下文的模块,以及添加某个特性或提供对某个外部库的访问u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EStarter Moduleu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003ESpring Boot Starter 是一个 Maven 或 Gradle 模块,其唯一目的是提供 "启动" 某个特性所需的所有依赖项。可以包含一个或多个 Auto-Configure Module (自动配置模块)的依赖项,以及可能需要的任何其他依赖项。这样,在Spring 启动应用程序中,我们只需要添加这个 starter 依赖就可以使用其特性u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003E⚠️: Spring 官方参考手册建议将自动配置分离,并将每个自动配置启动到一个独立的 Maven 或 Gradle 模块中,从而将自动配置和依赖项管理分离开来。如果你没有建立一个供成千上万用户使用的开源库,也可以将二者合并到一个 module 中u003Cbru002Fu003EYou may combine the auto-configuration code and the dependency management in a single module if you do not need to separate those two concernsu003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cpu003Eu003Cstrongu003E命名u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E来自 Spring 官方的 starter 都是 以 spring-boot-starter 开头,比如:u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003Espring-boot-starter-webu003Cu002Fliu003Eu003Cliu003Espring-boot-starter-aopu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E如果我们自定义 starter 功能名称叫acme,那么我们的命名是这样的:u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003Eacme-spring-boot-starteru003Cu002Fliu003Eu003Cliu003Eacme-spring-boot-autoconfigureu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E如果 starter 中用到了配置 keys,也要注意不要使用 Spring Boot 使用的命名空间,比如(server,management,spring)u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EParent Module 创建u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cp class=””u003E先来全局看一下项目结构:u003Cbru002Fu003Eu003Cstrongu003E一级目录结构:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpreu003E.u003Cbru003E├── pom.xmlu003Cbru003E├── rgyb-spring-boot-autoconfigureu003Cbru003E├── rgyb-spring-boot-sampleu003Cbru003E└── rgyb-spring-boot-starteru003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E二级目录结构:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpreu003E.u003Cbru003E├── pom.xmlu003Cbru003E├── rgyb-spring-boot-autoconfigureu003Cbru003E│ ├── pom.xmlu003Cbru003E│ └── srcu003Cbru003E├── rgyb-spring-boot-sampleu003Cbru003E│ ├── pom.xmlu003Cbru003E│ └── srcu003Cbru003E└── rgyb-spring-boot-starteru003Cbru003E ├── pom.xmlu003Cbru003E └── srcu003Cu002Fpreu003Eu003Cpu003E创建一个空的父亲 Maven Module,主要提供依赖管理,这样 SubModule 不用单独维护依赖版本号,来看 pom.xml 内容:u003Cu002Fpu003Eu003Cpreu003E<dependencyManagement>u003Cbru003E <dependencies>u003Cbru003E <dependency>u003Cbru003E <groupId>org.springframework.boot<u002FgroupId>u003Cbru003E <artifactId>spring-boot-dependencies<u002FartifactId>u003Cbru003E <version>${spring-boot.version}<u002Fversion>u003Cbru003E <type>pom<u002Ftype>u003Cbru003E <scope>import<u002Fscope>u003Cbru003E <u002Fdependency>u003Cbru003E <u002Fdependencies>u003Cbru003Eu003Cbru003E <!– 添加其他全局依赖管理到这里,submodule默认不引入这些依赖,需要显式的指定 –>u003Cbru003E<u002FdependencyManagement>u003Cu002Fpreu003Eu003Cpu003EAuto-Configure Module 构建u003Cu002Fpu003Eu003Cpu003E新建类 GreetingAutoConfigurationu003Cu002Fpu003Eu003Cpreu003E@Configurationu003Cbru003Epublic class GreetingAutoConfiguration {u003Cbru003Eu003Cbru003E @Beanu003Cbru003E public GreetingService greetingService(GreetingProperties greetingProperties){u003Cbru003E return new GreetingService(greetingProperties.getMembers());u003Cbru003E }u003Cbru003E}u003Cu002Fpreu003Eu003Cpu003E我们用 @Configuration 注解标记类 GreetingAutoConfiguration,作为 starter 的入口点。这个配置包含了我们需要提供starter特性的所有 @Bean 定义,在本例中,为了简单阐述问题,我们只将 GreetingService Bean 添加到应用程序上下文u003Cu002Fpu003Eu003Cpu003EGreetingService 内容如下:u003Cu002Fpu003Eu003Cpreu003E@AllArgsConstructoru003Cbru003Epublic class GreetingService {u003Cbru003Eu003Cbru003E private List<String> members = new ArrayList<>();u003Cbru003Eu003Cbru003E public void sayHello(){u003Cbru003E members.forEach(s -> System.out.println("hello " + s));u003Cbru003E }u003Cbru003E}u003Cu002Fpreu003Eu003Cpu003E在 resources 目录下新建文件 META-INFu002Fspring.factories (如果目录 META-INF 不存在需要手工创建),向文件写入内容:u003Cu002Fpu003Eu003Cpreu003Eorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\u003Cbru003E top.dayarch.autoconfigure.GreetingAutoConfigurationu003Cu002Fpreu003Eu003Cpu003ESpring 启动时会在其 classpath 中所有的 spring.factoreis 文件,并加载里面的声明配置,GreetingAutoConfiguration 类就绪后,我们的 Spring Boot Starter 就有了一个自动激活的入口点u003Cu002Fpu003Eu003Cpu003E到这里这个 "不完全的 starter" 已经可以使用了。但因为它是自动激活的,为了个让其灵活可用,我们需要让其按照我们的意愿来激活使用,所以我们需要条件注解来帮忙u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E条件配置u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E为类添加两个条件注解:u003Cu002Fpu003Eu003Cpreu003E@Configurationu003Cbru003E@ConditionalOnProperty(value = "rgyb.greeting.enable", havingValue = "true")u003Cbru003E@ConditionalOnClass(DummyEmail.class)u003Cbru003Epublic class GreetingAutoConfiguration {u003Cbru003E …u003Cbru003E}u003Cu002Fpreu003Eu003Cul class=””u003Eu003Cliu003E通过使用 @ConditionalOnProperty 注解,我们告诉 Spring,只有属性 rgyb.greeting.enable 值被设置为 true 时,才将 GreetingAutoConfiguration (以及它声明的所有 bean ) 包含到应用程序上下文中u003Cu002Fliu003Eu003Cliu003E通过使用 @ConditionalOnClass 注解,我们告诉Spring 只有类 DummyEmail.class 存在于 classpath 时,才将 GreetingAutoConfiguration (以及它声明的所有 bean ) 包含到应用程序上下文中u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E多个条件是 andu002F与的关系,既只有满足全部条件时,才会加载 GreetingAutoConfigurationu003Cu002Fpu003Eu003Cpu003E如果你对条件注解的使用还不是很明确,可以查看我之前的文章: @Conditional注解,灵活配置 Spring Bootu003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E配置属性管理u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E上面使用了 @ConditionalOnProperty 注解,实际 starter 中可能有非常多的属性,所以我们需要将这些属性集中管理:u003Cu002Fpu003Eu003Cpreu003E@Datau003Cbru003E@ConfigurationProperties(prefix = "rgyb.greeting")u003Cbru003Epublic class GreetingProperties {u003Cbru003Eu003Cbru003E u002F**u003Cbru003E * GreetingProperties 开关u003Cbru003E *u002Fu003Cbru003E boolean enable = false;u003Cbru003Eu003Cbru003E u002F**u003Cbru003E * 需要打招呼的成员列表u003Cbru003E *u002Fu003Cbru003E List<String> members = new ArrayList<>();u003Cbru003E}u003Cu002Fpreu003Eu003Cpu003E我们知道这些属性是要在 application.yml 中使用的,当我们需要使用这些属性时,为了让 IDE 给出更友好的提示,我们需要在 pom.xml 中添加依赖:u003Cu002Fpu003Eu003Cpreu003E<dependency>u003Cbru003E <groupId>org.springframework.boot<u002FgroupId>u003Cbru003E <artifactId>spring-boot-configuration-processor<u002FartifactId>u003Cbru003E <optional>true<u002Foptional>u003Cbru003E<u002Fdependency>u003Cu002Fpreu003Eu003Cpu003E这样当我们 mvn compile 时,会在生成一个名为 spring-configuration-metadata.json JSON 文件,文件内容如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Fbb83bf158eab4a75bdb37dbca6a303c4″ img_width=”800″ img_height=”720″ alt=”手把手教你定制标准Spring Boot starter,看起来神清气爽” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E生成的内容在接下来的内容中用到,且看u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E提升启动时间u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E对于类路径上的每个自动配置类,Spring Boot 必须计算 @Conditional… 条件值,用于决定是否加载自动配置及其所需的所有类,根据 Spring 启动应用程序中 starter 的大小和数量,这可能是一个非常昂贵的操作,并且会影响启动时间,为了提升启动时间,我们需要在 pom.xml 中添加另外一个依赖:u003Cu002Fpu003Eu003Cpreu003E<dependency>u003Cbru003E <groupId>org.springframework.boot<u002FgroupId>u003Cbru003E <artifactId>spring-boot-autoconfigure-processor<u002FartifactId>u003Cbru003E <optional>true<u002Foptional>u003Cbru003E<u002Fdependency>u003Cu002Fpreu003Eu003Cpu003E这个注解会生成一个名为 spring-autoconfigure-metadata.properties Property 文件,其内容如下:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Ff17bfc4b4c6b4ec2b439b95ef4c4c542″ img_width=”800″ img_height=”120″ alt=”手把手教你定制标准Spring Boot starter,看起来神清气爽” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E这样,Spring Boot 在启动期间读取这些元数据,可以过滤出不满足条件的配置,而不必实际检查这些类,提升启动速度u003Cu002Fpu003Eu003Cpu003E到这里关于 Auto-Configure Module 就构建完了,我们需要继续完成 Starter Module 的构建u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EStarter Module 构建u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EStarter Module 的构建很简单了,你可以认为它就是一个空 module,除了依赖 Auto-Configure Module,其唯一作用就是为了使用 starter 功能特性提供所有必须依赖,所以我们为 starter module 的 pom.xml 文件添加如下内容:u003Cu002Fpu003Eu003Cpreu003E<dependencies>u003Cbru003E <dependency>u003Cbru003E <groupId>top.dayarch.learnings<u002FgroupId>u003Cbru003E <artifactId>rgyb-spring-boot-autoconfigure<u002FartifactId>u003Cbru003E <version>1.0.0.RELEASE<u002Fversion>u003Cbru003E <u002Fdependency>u003Cbru003Eu003Cbru003E <!– 在此处添加其他必要依赖,保证starter可用 –>u003Cbru003E<u002Fdependencies>u003Cu002Fpreu003Eu003Cpu003E同样在 resources 目录下新建文件 META-INFu002Fspring.providers , 其内容如下:u003Cu002Fpu003Eu003Cpreu003Eproviders: rgyb-spring-boot-autoconfigureu003Cu002Fpreu003Eu003Cpu003E该文件主要作用是说明 starter module 的依赖信息,多个依赖以逗号分隔就好,该文件不会影响 starter 的使用,可有可无u003Cu002Fpu003Eu003Cpu003EStarter Module 就可以这么简单,将两个 module 分别 mvn install 到本地 Maven Repository,接下来我们创建 sample module 引入这个 starter 依赖时就会从本地 Maven Repository 中拉取u003Cu002Fpu003Eu003Ch1u003Eu003Cstrongu003E创建 Sample Moduleu003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E我们可以通过 Spring Initializr 正常初始化一个 Spring Boot 项目 (rgyb-spring-boot-sample),引入我们刚刚创建的 starter 依赖,在 sample pom.xml 中添加依赖:u003Cu002Fpu003Eu003Cpreu003E<dependency>u003Cbru003E <groupId>top.dayarch.learnings<u002FgroupId>u003Cbru003E <artifactId>rgyb-spring-boot-starter<u002FartifactId>u003Cbru003E <version>1.0.0.RELEASE<u002Fversion>u003Cbru003E<u002Fdependency>u003Cu002Fpreu003Eu003Cpu003E接下来配置 application.yml 属性u003Cu002Fpu003Eu003Cpreu003Ergyb:u003Cbru003E greeting:u003Cbru003E enable: trueu003Cbru003E members:u003Cbru003E – 李雷u003Cbru003E – 韩梅梅u003Cu002Fpreu003Eu003Cpu003E在我们配置 YAML 的时候,会出现下图的提示,这样会更友好,当然为了规范,属性描述最好也用英文描述,这里为了说明问题用了中文描述:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F6d4fecb4a71d4d29a4518debc2b566f0″ img_width=”800″ img_height=”181″ alt=”手把手教你定制标准Spring Boot starter,看起来神清气爽” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Eu003Cstrongu003E编写测试类u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E我们编写测试用例:u003Cu002Fpu003Eu003Cpreu003E@Autowired(required = false)u003Cbru003Eprivate GreetingService greetingService;u003Cbru003Eu003Cbru003E@Testu003Cbru003Epublic void testGreeting() {u003Cbru003E greetingService.sayHello();u003Cbru003E}u003Cu002Fpreu003Eu003Cpu003E测试结果如下:u003Cu002Fpu003Eu003Cpreu003Ehello 李雷u003Cbru003Ehello 韩梅梅u003Cu002Fpreu003Eu003Ch1u003Eu003Cstrongu003E总结u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E到这里完整的 starter 开发就结束了,希望大家了解其构建过程,目录结构及命名等标准,这样有相应的业务需求时都可以开发自己的 starter 被其他人应用起来u003Cu002Fpu003Eu003Cpu003Estarter 开发好了,别人可以手动添加依赖引入 starter 的相关功能,那我们如何像 Spring Initializr 一样,通过下来菜单选择我们的 starter 呢,这样直接初始化好整个项目,接下来的文章我们会模仿 Spring Initializr 自定义我们自的 Initializru003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E知识点说明u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EDependency optinalu003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003E为什么 Auto-Configure Module 的 dependency 都是 optional = true 呢?u003Cbru002Fu003E这涉及到 Maven 传递性依赖的问题,详情请看 Maven 依赖传递性透彻理解u003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cpu003Espring.factoriesu003Cu002Fpu003Eu003Cblockquoteu003Eu003Cpu003ESpring Boot 是如何加载这个文件并找到我们的配置类的u003Cbru002Fu003E下图是 Spring Boot 应用程序启动的调用栈的一部分,我添加了断点:u003Cu002Fpu003Eu003Cu002Fblockquoteu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F3d22c278516741a7abf33826ae37fb78″ img_width=”800″ img_height=”330″ alt=”手把手教你定制标准Spring Boot starter,看起来神清气爽” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E打开 SpringFactoriesLoader 类,映入眼帘的就是这个内容:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F59c4b7a701364ce5981c2e8705df6497″ img_width=”800″ img_height=”159″ alt=”手把手教你定制标准Spring Boot starter,看起来神清气爽” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E这两张图应该足够说明问题了,是 SPI 的一种加载方式,更细节的内容请大家自己去发现吧u003Cu002Fpu003Eu003Cpu003E实际案例u003Cu002Fpu003Eu003Cpu003E这里推荐查看 mybatis-spring-boot-starter 这个非 Spring 官方的案例,从中我们:u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003E模仿其目录结构u003Cu002Fliu003Eu003Cliu003E模仿其设计理念u003Cu002Fliu003Eu003Cliu003E模仿其编码规范u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F2414f5c02fdb4b70b0b6eface23c6d4e” img_width=”800″ img_height=”410″ alt=”手把手教你定制标准Spring Boot starter,看起来神清气爽” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:手把手教你定制标准Spring Boot starter,看起来神清气爽

主题测试文章,只做测试使用。发布者:程序员,转转请注明出处:http://www.cxybcw.com/26404.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code