相信在了解SpringBoot时后已经知道了starter是SpringBoot的核心组成部分,SpringBoot为我们提供了尽可能完善的封装,提供了一系列的自动化配置的starter插件,我们在使用只需要在pom.xml配置文件内添加依赖就可以了,很好的降低了使用框架时的复杂度。那我们如何实现一个自己的starter呢?
1. 知识点回顾
- @ConfigurationProperties,该注解可以完成将application.properties配置文件内的有规则的配置参数映射到实体内的field内,不过需要提供setter方法
- @Configuration:标识为一个配置类,可以替换xml, 引用了@Component注解表示会被注册的Bean容器中
- @EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源
- @ConditionalOnBean:当SpringIoc容器内存在指定Bean的条件
- @ConditionalOnClass:当SpringIoc容器内存在指定Class的条件
- @ConditionalOnExpression:基于SpEL表达式作为判断条件
- @ConditionalOnJava:基于JVM版本作为判断条件
- @ConditionalOnJndi:在JNDI存在时查找指定的位置
- @ConditionalOnMissingBean:当SpringIoc容器内不存在指定Bean的条件
- @ConditionalOnMissingClass:当SpringIoc容器内不存在指定Class的条件
- @ConditionalOnNotWebApplication:当前项目不是Web项目的条件
- @ConditionalOnProperty:指定的属性是否有指定的值
- @ConditionalOnResource:类路径是否有指定的值
- @ConditionalOnSingleCandidate:当指定Bean在SpringIoc容器内只有一个,或者虽然有多个但是指定首选的Bean
- @ConditionalOnWebApplication:当前项目是Web项目的条件
以上Conditional开头的注解都是内置的条件注解,是元注解@Conditional演变而来的,根据条件判断是否需要创建对应实例。
2. 自定义Starter
2.1 模块及命名规范
starter一般都包含两部分:自动配置部分及业务逻辑starter。虽然官方提供的starter都是spring-boot开头,但是自定的不建议这样做。而是xxx-spring-boot-starter这种命名方式,xxx表示自定义的业务。
You may combine the auto-configuration code and the dependency management in a single module if you do not need to separate those two concerns.
You should make sure to provide a proper namespace for your starter. Do not start your module names with spring-boot, even if you use a different Maven groupId. We may offer official support for the thing you auto-configure in the future.
As a rule of thumb, you should name a combined module after the starter. For example, assume that you are creating a starter for “acme” and that you name the auto-configure module acme-spring-boot-autoconfigure and the starter acme-spring-boot-starter. If you only have one module that combines the two, name it acme-spring-boot-starter.
2.2 创建工程
创建一个简单的Maven工程,引入自动配置包。
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qqxhb</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
</dependencies>
</project>
2.3 创建属性配置实体类
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("qqxhb")//指定配置文件前缀
public class DemoProperties {
private String name = "庆余年";
private String year = "2019";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
}
在上面代码中,@ConfigurationProperties注解内配置了属性preffix值为‘qqxhb’,该属性配置了读取参数的前缀,根据上面的实体属性对应配置文件内的配置则是qqxhb.name、qqxhb.year,同时指定了默认值,配置文件内不进行配置时则是使用默认值。
2.4 创建业务逻辑类
public class DemoService {
private DemoProperties demoProperties;
public DemoProperties getDemoProperties() {
return demoProperties;
}
public void setDemoProperties(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
public String getProperties() {
if (demoProperties == null) {
return null;
}
return demoProperties.getName() + " " + demoProperties.getYear();
}
}
业务逻辑只是简单的依赖属性配置类,并且提供显示属性值的方法。
2.5 实现自动配置逻辑
自动配置,这一块是starter的核心部分,自动配置提供实体bean的验证以及初始化,配置该部分后在启动项目时才会自动加载配置。
创建自动配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 标识配置类
@EnableConfigurationProperties(DemoProperties.class) // 开启DemoProperties对象
@ConditionalOnClass(DemoService.class) // 存在DemoService时初始化该配置类
//存在对应配置信息时初始化该配置类
@ConditionalOnProperty(prefix = "qqxhb", // 存在配置前缀qqxhb
value = "enabled", // 开启
matchIfMissing = true// 缺失检查
)
public class DemoAutoConfiguration{
@Autowired
private DemoProperties demoProperties;
/**
* 根据条件实例化DemoService
*
* @return
*/
@Bean
@ConditionalOnMissingBean(DemoService.class) // 缺失DemoService实体bean时,初始化DemoService并添加到SpringIoc
public DemoService demoService() {
DemoService demoService = new DemoService();
demoService.setDemoProperties(demoProperties);
return demoService;
}
}
创建自动配置文件META-INF/spring.factories
springboot自动配置原理
在 resources 下创建文件夹 META-INF 并在 META-INF 下创建文件 spring.factories。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.qqxhb.stater.demo.DemoAutoConfiguration
2.6 测试
先将starter打包install到本地仓库,然后引入测试项目。
<dependency>
<groupId>com.qqxhb</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
编写测试类,使用默认配置测试。
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoStarterTest {
@Autowired
DemoService demoService;
@Test
public void starterTest() {
System.out.println(demoService.getProperties());
}
}
测试通过后,可以在配置文件application.properties中添加配置,再次执行测试代码。
qqxhb.name=爱情公寓第五季
qqxhb.year=2020
到此自定义starter就结束了,相信你已经学会了,期望对你有所帮助。