上篇Blog我们学习了SpringBoot是如何实现自动配置的,通过@Configuration的全注解配置方式和Spring Factories 发现机制自动将组件注入到容器,然后依据配置绑定,确定组件的属性值该是多少,实现了全自动的实现方式,不用我们再操心。在第一篇Blog中我们就提到过场景启动器,那么本篇Blog结合SpringBoot自动配置以及前面学的yaml配置文件等知识来自己实现一个自定义的场景启动器。
starter基本概念
我们再回顾下starter的概念:starter 是 SpringBoot 中一种非常重要的机制,它可以繁杂的配置统一集成到 starter 中,我们只需要通过 maven 将 starter 依赖引入到项目中,SpringBoot 就能自动扫描并加载相应的默认配置。starter 的出现让开发人员从繁琐的框架配置中解放出来,将更多的精力专注于业务逻辑的开发,极大的提高了开发效率。在一些特殊情况下,我们也可以将一些通用功能封装成自定义的 starter 进行使用
命名规范
SpringBoot 提供的 starter 以 spring-boot-starter-xxx 的形式命名。为了与 SpringBoot 生态提供的 starter 进行区分,官方建议第三方开发者或技术(例如 Druid、Mybatis 等等)厂商自定义的 starter 使用 xxx-spring-boot-starter 的形式命名,例如 mybatis-spring-boot-starter、druid-spring-boot-starter 等等
模块规范
Spring Boot 官方建议我们在自定义 starter 时,创建两个 Module :autoConfigure Module 和 starter Module,其中 starter Module 依赖于 autoConfigure Module。当然,这只是 Spring Boot 官方的建议,并不是硬性规定,若不需要自动配置代码和依赖项目分离,我们也可以将它们组合到同一个 Module 里
自定义starter步骤
自定义 starter 可以分为以下 7 步:创建工程—》添加 POM 依赖—》定义 propertie 类—》定义 Service 类–》定义自动配置类—》创建 spring.factories文件—》构建 starter,接下来我们按照步骤实现一遍
1 创建工程
在IDEA中新建一个空项目
填写项目信息:tml-spring-boot-starter
创建第一个模块:tml-spring-boot-starter
创建第二个模块:tml-spring-boot-starter-autoconfiguration
两个模块都按照如下选项创建:
两个模块选择好后如下:
创建完后项目界面如下:
2 添加 POM 依赖
在tml-spring-boot-starter的 pom.xml 中添加以下代码,将 tml-spring-boot-starter-autoconfiguration 作为其依赖项
<!--添加自动配置模块为其依赖--> <dependency> <groupId>com.example</groupId> <artifactId>tml-spring-boot-starter-autoconfiguration</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
3 定义 propertie 类
在 tml-spring-boot-starter-autoconfiguration 的properties 包中,创建一个实体类:PersonProperties,通过它来映射配置信息
package com.example.tmlspringbootstarterautoconfiguration.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; /** * 实体类,用来映射配置信息 */ @Data @ConfigurationProperties(prefix = "person") @PropertySource(value = "classpath:application.properties") public class PersonProperties { private String prefix; private String suffix; }
4 定义 Service 类
在tml-spring-boot-starter-autoconfiguration 的 services包中,创建一个 Service 类:PersonService,供其他项目使用
package com.example.tmlspringbootstarterautoconfiguration.services; import com.example.tmlspringbootstarterautoconfiguration.properties.PersonProperties; import javax.annotation.Resource; public class PersonService { @Resource PersonProperties personProperties; public String sayHello(String userName) { return personProperties.getPrefix() + userName + personProperties.getSuffix(); } }
5 定义自动配置类
在tml-spring-boot-starter-autoconfiguration的autoConfiguration包 中,创建一个配置类:PersonAutoConfiguration,其代码如下
package com.example.tmlspringbootstarterautoconfiguration.autoConfiguration; import com.example.tmlspringbootstarterautoconfiguration.properties.PersonProperties; import com.example.tmlspringbootstarterautoconfiguration.services.PersonService; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableConfigurationProperties(PersonProperties.class) //启用 PersonProperties,并默认将它添加到容器中 public class PersonAutoConfiguration{ @ConditionalOnMissingBean(PersonService.class) //当容器中没有 PersonService 时生效 @Bean public PersonService personService() { PersonService personService = new PersonService(); return personService ; } }
PersonAutoConfiguration使用了以下 4 个注解:
- @Configuration:表示该类是一个配置类;
- @EnableConfigurationProperties(HelloProperties.class):该注解的作用是为 HelloProperties 开启属性配置功能,并将这个类以组件的形式注入到容器中;
- @ConditionalOnMissingBean(HelloService.class):该注解表示当容器中没有 HelloService 类时,该方法才生效;
- @Bean:该注解用于将方法的返回值以 Bean 对象的形式添加到容器中。
这样自动配置类就将组件和配置绑定并且都注入到容器中了。
6 创建 spring.factories文件
由于 Spring Boot 的自动配置是基于 Spring Factories 机制实现的,因此我们自定义 starter 时,同样需要在项目类路径下创建一个 spring.factories 文件。在tml-spring-boot-starter-autoconfiguration 的类路径下(resources )中创建一个 META-INF 文件夹,并在 META-INF 文件夹中创建一个 spring.factories 文件
将 Spring Boot 的 EnableAutoConfiguration 接口与自定义 starter 的自动配置类 PersonAutoConfiguration组成一组键值对添加到 spring.factories 文件中,以方便 Spring Boot 在启动时,获取到自定义 starter 的自动配置,代码如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.tmlspringbootstarterautoconfiguration.autoConfiguration.PersonAutoConfiguration
7 构建 starter
接下来,我们需要对自定义 starter 进行构建,并将它安装到本地仓库或远程仓库中,供其他项目使用。由于我们是在本地的项目中引用和测试,因此只需要使用 install 命令安装到本地仓库即可,因为存在依赖关系,所以先构建tml-spring-boot-starter-autoconfiguration再构建tml-spring-boot-starter
构建过程中遇到一些问题,只要在pom中的build按照如下内容配置即可避免:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${project.parent.version}</version> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> <!--添加配置跳过测试--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <!--添加配置跳过测试--> </plugins> </build>
使用自定义starter
我们来测试下自定义的starter
1 新建测试项目
新建一个Moudle来使用我们的自定义starter
在其 pom.xml 中引入依赖tml-spring-boot-starter(自定义 starter),配置如下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>test-springboot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>test-springboot</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--引入自定义 starter --> <dependency> <groupId>com.example</groupId> <artifactId>tml-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2 编写配置文件
在 Spring Boot 配置文件 application.yml中,添加以下属性配置
#自定义 starter prefix 属性 person: prefix: 兄弟 #自定义 starter suffix 属性 suffix: 欢迎一起玩儿SpringBoot!
3 创建测试控制器
在 controller包 中创建一个控制器类 PersonController
package com.example.testspringboot.controller; import com.example.tmlspringbootstarterautoconfiguration.services.PersonService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; @Controller public class PersonController { //自动装配自定义 starter 的 service @Resource PersonService personService; @ResponseBody @GetMapping("/personInfo") public String SayHello(String name) { return personService.sayHello(name); } }
请求后结果如下,可以看到配置也被读取出来了
总结一下
其实实现一个自定义starter的过程就是把前面所有SpringBoot相关内容复习一遍的过程,通过SpringBoot自动配置,我们初始化好了自动配置类,把组件和配置信息绑定并注入到容器中,然后将这个场景启动器提供出来,供外边的第三方使用,我们成了一个starter的服务提供商,我觉得这样实现一遍后对SpringBoot的实现理解的更加透彻了。










