文章目录
自定义Spring Boot AutoConfiguration和Spring-boot-starter
前言
在自定义spring-boot属性文章中,我们能够自定义属性,并在application.yml文件中配置,其实自定义属性一般常和自动配置结合使用。这里讲一下如何自定义自定配置。
场景
什么情况下我们会编写针对于spring boot 的自动配置类和自定义的spring-boot-starter呢?
目前很大一部分应用开发是基于spring boot的
- 所开发的框架开源并适配spring boot,如Druid、Mybatis-plus、Feign.
- 公司内部成熟的自己开发的框架,为了支持更多的场景,针对spring boot进行适配
官方文档
spring boot关于自定义自动配置的详细文档以及示例项目.一般自动配置会封装为一个spring-boot-starter的jar,对外提供
设计结构
目前各种框架的spring-boot-starter基本有两种。
- 框架源码和starter独立的,在提供starter的时候,引入框架的依赖。这时候使用者只需引入框架的starter即可。不需要引用框架。采取这种方式的框架如阿里的Druid数据源
- 框架源码和starter不是独立的,要想在spring-boot应用中自动配置,要引入两个依赖,框架的主要依赖,和对应的starter依赖。采取这种方式的框架如Mybatis-plus
这里采用第一种方式,即引入starter即可使用对应框架的全部功能。本文示例框架为自定义的一个小型项目,名字为my-app
一个标准的spring-boot-starter结构原则基本如下:
-
pom文件名字为框架-spring-boot-starter。本文是my-app-spring-boot-starter
-
在starter项目中,要有名为autoconfigure的包,自动配置类放置在此包下。
-
其他的属性类放置在autocinfigure包下的子包下,子包自定义命名
-
在resources文件夹下的META-INF文件夹下建立spring.factories文件
框架一般都带有具体的属性,对于自定义属性
- 需要在META-INF下建立spring-configuration-metadata.json
编码
引入依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<name>my-app-spring-boot-starter</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!--引入spring-boot依赖,放在dependencyManagent标签中。关于imoprt的使用见maven使用的文章-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 自动配置类所需依赖,官方建议加上这个依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!--自定义属性application.yml中的属性所需依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!--引入框架源码依赖-->
<dependency>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--test依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</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>
<version>2.3.3.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
关于上述pom文件中spring-boot-dependencies的使用疑问,详见Maven的使用
编写自定义属性文件spring-configuration-metadata.json
该文件放置在resources文件夹下的META-INF文件夹下
{
"groups": [
{
"name": "my-app",
"type": "com.mycompany.app.autoconfigure.properties.MyAppProperties",
"sourceType": "com.mycompany.app.autoconfigure.properties.MyAppProperties"
}
],
"properties": [
{
"name": "my-app.enabled",
"type": "java.lang.Boolean",
"defaultValue": false,
"sourceType": "com.mycompany.app.autoconfigure.properties.MyAppProperties"
},
{
"name": "my-app.info",
"type": "java.lang.String",
"sourceType": "com.mycompany.app.autoconfigure.properties.MyAppProperties"
}
]
}
自定义属性文件,基本由groups和properties两大部分组成。更详细的文档介绍见官方文档
建立自定义属性映射的Java类
上面说过,starter项目下都要放在autoconfigure下,对于不属于自动配置类的内容,建立子包。这里针对自定义的属性类,建立子包名为properties的包,并在该包下建立建立属性映射类,名为MyAppProperties。并使用@ConfigurationProperties注解标注,prefix属性是指具体属性的父级名字,这里对应的是groups里的内容
/**
* @author jacksparrow414
* @date 2021/2/3
*/
@ConfigurationProperties(prefix = "my-app")
public class MyAppProperties {
private Boolean enabled;
private String info;
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(final Boolean enabled) {
this.enabled = enabled;
}
public String getInfo() {
return info;
}
public void setInfo(final String info) {
this.info = info;
}
}
此类的完全限定名对应上述json文件中的sourceType属性。
建立自动配置类
一般spring-boot的@ConditionalXXX注解使用场景基本就在自动配置类这里。@Conditional相关注解文档
目标
建立此配置类的目标是,当项目中引用了my-app依赖,并且spring容器中没有MyAppLog相关的bean,则自动配置
Java类
@ConditionalOnProperty(prefix = "my-app", name = "enabled", havingValue = "true", matchIfMissing = false)
@ConditionalOnClass(MyAppLog.class)
@Configuration
@EnableConfigurationProperties(value = {MyAppProperties.class})
public class MyAppAutoConfiguration {
@ConditionalOnMissingBean(MyAppLog.class)
@Bean
public MyAppLog myAppDefaultLog() {
return new MyAppLogDefaultImpl();
}
}
此自动配置类生效的条件是my-app.enabled的属性为true,此配置才生效
建立spring.factories
上面建立好自动配置类后,将其放置在spring.factories文件中。文件内容格式为
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycompany.app.autoconfigure.MyAppAutoConfiguration
Spring-boot的自动配置类的完全限定名=自定义自动配置类的完全限定名,如果有多个,以逗号分隔开
整个项目结构图
测试
对于自动配置的测试,spring-boot提供了ApplicationContextRunner进行验证。详细文档
测试类如下
public class MyAppAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test
public void assertMyAppAutoConfiguration() {
contextRunner
.withPropertyValues("my-app.enabled=true")
.withUserConfiguration(MyAppAutoConfiguration.class)
.run(context -> {
context.getBean("myAppDefaultLog");
});
}
}
对于我们自定义的属性的条件生效测试(@ConditionOnProperty),使用withPropertyValues进行赋值,即使在默认的application.yml文件中填写属性,test测试也拿不到属性
而对于其他一些特殊的注解@ConditionalOnMissingClass,如果想要测试其是否生效,spring-boot也提供了对应的测试方法withClassLoader
实际使用
将my-app-spring-boot-starter打包,在另外一个项目中使用
引入依赖
<dependency>
<groupId>com.mycompany.app</groupId>-->
<artifactId>my-app-spring-boot-starter</artifactId>-->
<version>1.0-SNAPSHOT</version>-->
</dependency>
配置属性
在application.yml文件中配置
my-app:
enabled: true
info: "myAppTest"
启动项目
在项目主类上添加@EnableAutoConfiguration,高版本的spring-boot可以不加,因为@SpringBootApplication注解中已经包含了此注解
启动项目发现,在我们外部没有配置MyAppLog的相关bean的情况下,my-app框架使用默认的实现,打印出default Impl信息
使用自定义配置覆盖默认配置
my-app框架同时提供了拓展的功能,只要实现MyAppLog接口并配置即可
@Configuration
@AutoConfigureBefore(MyAppAutoConfiguration.class)
public class CustomMyAppConfiguration {
@Bean
public MyAppLog myCustomLog() {
return new MyCustomLog();
}
}
要求此配置在自动配置类前生效。这样,就会使用我们自己拓展的实现,而不是自动配置的默认实现
结束
至此为止,自定义属性,并利用自定义属性开启我们的自定义的自动配置,到测试,到实际使用,整体流程就结束了
项目中用到的代码
使用须知
按照顺序下载源码,下载之后按照顺序执行
mvn install
命令,将源码对应的依赖install到本地,再重新刷新引用了相关依赖的pom文件