目录
创建您自己的自动配置
如果您在一家开发共享库的公司工作,或者如果您在开源或商业库中工作,您可能想要开发自己的自动配置。自动配置类可以捆绑在外部 jar 中,并且仍然可以被 Spring Boot 拾取。
自动配置可以与提供自动配置代码以及您将使用的典型库的“启动器”相关联。
了解自动配置的 Bean
在底层,自动配置是通过标准@Configuration
类实现的。附加@Conditional
注释用于限制何时应用自动配置。通常,自动配置类使用@ConditionalOnClass
和@ConditionalOnMissingBean
注释。这确保了自动配置仅在找到相关类并且您没有声明自己的类时适用@Configuration
。
定位自动配置候选
META-INF/spring.factories
Spring Boot 检查发布的 jar中是否存在文件。该文件应在键下列出您的配置类EnableAutoConfiguration
,如以下示例所示:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
注意:自动配置只能 以这种方式加载。确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应启用组件扫描以查找其他组件。应该使用 特定的@Import
来代替。:
如果您的配置需要按特定顺序应用,您可以使用@AutoConfigureAfter
or注释。@AutoConfigureBefore
例如,如果您提供特定于 Web 的配置,您的类可能需要在WebMvcAutoConfiguration
.
如果您想排序自动配置,您也可以使用@AutoConfigureOrder
. 该注释与常规注释具有相同的语义,@Order
但为自动配置类提供了专用顺序。
与标准@Configuration
类一样,应用自动配置类的顺序只影响定义它们的 bean 的顺序。随后创建这些 bean 的顺序不受影响,由每个 bean 的依赖关系和任何@DependsOn
关系决定。
条件注释
您几乎总是希望@Conditional
在您的自动配置类中包含一个或多个注释。注解是一个常见的@ConditionalOnMissingBean
例子,如果开发人员对你的默认设置不满意,它可以让他们覆盖自动配置。
Spring Boot 包含许多注释,您可以通过注释类或单个方法@Conditional
在自己的代码中重用它们。
类条件
@ConditionalOnClass
和@ConditionalOnMissingClass
注释允许根据@Configuration
特定类的存在与否来包含类。由于注释元数据是使用ASM解析的,因此您可以使用该value
属性来引用真实的类,即使该类实际上可能不会出现在正在运行的应用程序类路径中。name
如果您更喜欢使用值指定类名,也可以使用该属性String
。
此机制不适用于@Bean
通常返回类型是条件目标的方法:在方法上的条件适用之前,JVM 将加载类和可能处理的方法引用,如果类不加载,这些方法引用将失败展示。
为了处理这种情况,可以使用一个单独的@Configuration
类来隔离条件,如下例所示:
@Configuration(proxyBeanMethods = false)
// Some conditions ...
public class MyAutoConfiguration {
// Auto-configured beans ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService.class)
public static class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
}
Bean条件
@ConditionalOnBean
和注释允许根据@ConditionalOnMissingBean
特定 bean 的存在或不存在来包含 bean。您可以使用该value
属性按类型name
指定bean 或按名称指定bean。该search
属性允许您限制ApplicationContext
在搜索 bean 时应考虑的层次结构。
放置在@Bean
方法上时,目标类型默认为方法的返回类型,如下例所示:
@Configuration(proxyBeanMethods = false)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
属性条件
@ConditionalOnProperty
注释允许基于 Spring Environment 属性包含配置。使用prefix
和name
属性指定应检查的属性。默认情况下,false
匹配任何存在但不等于的属性。您还可以使用havingValue
和matchIfMissing
属性创建更高级的检查。
资源条件
@ConditionalOnResource
注释允许仅在存在特定资源时才包含配置。可以使用通常的 Spring 约定来指定资源,如下例所示file:/home/user/test.dat
:
web条件
@ConditionalOnWebApplication
和注释允许根据@ConditionalOnNotWebApplication
应用程序是否为“Web 应用程序”来包含配置。基于 servlet 的 Web 应用程序是任何使用 Spring WebApplicationContext
、定义session
范围或具有ConfigurableWebEnvironment
. 反应式 Web 应用程序是任何使用ReactiveWebApplicationContext
或具有ConfigurableReactiveWebEnvironment
.
注释允许根据@ConditionalOnWarDeployment
应用程序是否是部署到容器的传统 WAR 应用程序来包含配置。对于使用嵌入式服务器运行的应用程序,此条件将不匹配。
测试您的自动配置
自动配置可能受到许多因素的影响:用户配置(@Bean
定义和Environment
定制)、条件评估(特定库的存在)等。具体来说,每个测试都应该创建一个定义良好ApplicationContext
的,代表这些定制的组合。 ApplicationContextRunner
提供了实现这一目标的好方法。
ApplicationContextRunner
通常被定义为测试类的一个字段,用于收集基本的、通用的配置。以下示例确保MyServiceAutoConfiguration
始终调用它:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
每个测试都可以使用运行器来表示特定的用例。例如,下面的示例调用用户配置 ( UserConfiguration
) 并检查自动配置是否正确退出。Invokingrun
提供了一个回调上下文,可以与AssertJ
.
@Test
void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
MyService myCustomService() {
return new MyService("mine");
}
}
也可以轻松自定义Environment
,如以下示例所示:
@Test
void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
});
}
Runner也可用于显示ConditionEvaluationReport
. 报告可以在INFO
或DEBUG
水平打印。以下示例显示了如何使用ConditionEvaluationReportLoggingListener
打印自动配置测试中的报告。
class MyConditionEvaluationReportingTests {
@Test
void autoConfigTest() {
new ApplicationContextRunner()
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}
}
创建自己的启动器
一个典型的 Spring Boot 启动器包含自动配置和自定义给定技术的基础设施的代码,我们称之为“acme”。为了使其易于扩展,可以将专用命名空间中的许多配置键暴露给环境。最后,提供了一个“starter”依赖项来帮助用户尽可能轻松地开始。
具体来说,自定义启动器可以包含以下内容:
autoconfigure
包含“acme”的自动配置代码的模块。- 提供对模块
starter
的依赖关系的autoconfigure
模块以及“acme”和通常有用的任何其他依赖关系。简而言之,添加启动器应该提供开始使用该库所需的一切。
两个模块中的这种分离绝不是必要的。如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的事实。此外,您还可以制作一个启动器来提供有关这些可选依赖项的意见。同时,其他人只能依靠autoconfigure
模块,制作自己的不同意见的starter。
如果自动配置相对简单并且没有可选功能,那么在启动器中合并两个模块绝对是一种选择。
命名
您应该确保为您的启动器提供适当的命名空间。不要以 . 开头的模块名称spring-boot
,即使您使用不同的 Maven groupId
。我们将来可能会为您自动配置的内容提供官方支持。
根据经验,您应该在启动器之后命名组合模块。例如,假设您正在为“acme”创建一个启动器,并且您命名自动配置模块acme-spring-boot
和启动器acme-spring-boot-starter
。如果您只有一个模块将两者结合起来,请将其命名为acme-spring-boot-starter
.
配置键
如果您的启动器提供配置键,请为它们使用唯一的命名空间。特别是,不要将您的键包含在 Spring Boot 使用的命名空间中(例如server
、management
、spring
等)。如果您使用相同的命名空间,我们将来可能会以破坏您的模块的方式修改这些命名空间。根据经验,在所有键前面加上您拥有的命名空间(例如acme
)。
确保通过为每个属性添加字段 javadoc 来记录配置键,如以下示例所示:
@ConfigurationProperties("acme")
public class AcmeProperties {
/**
* Whether to check the location of acme resources.
*/
private boolean checkLocation = true;
/**
* Timeout for establishing a connection to the acme server.
*/
private Duration loginTimeout = Duration.ofSeconds(3);
// getters/setters ...
}
以下是我们在内部遵循的一些规则,以确保描述一致:
- 不要以“The”或“A”开始描述。
- 为了
boolean
类型,以“是否”或“启用”开始描述。 - 对于基于集合的类型,以“逗号分隔列表”开始描述
- 使用
java.time.Duration
而不是long
描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。 - 除非必须在运行时确定,否则不要在描述中提供默认值。
“自动配置”模块
该autoconfigure
模块包含开始使用该库所需的一切。它还可能包含配置键定义(例如@ConfigurationProperties
)和任何回调接口,可用于进一步自定义组件的初始化方式。
Spring Boot 使用注解处理器来收集元数据文件 ( META-INF/spring-autoconfigure-metadata.properties
) 中的自动配置条件。如果该文件存在,它将用于急切地过滤不匹配的自动配置,这将缩短启动时间。建议在包含自动配置的模块中添加以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
如果您在应用程序中直接定义了自动配置,请确保配置spring-boot-maven-plugin
以防止repackage
目标将依赖项添加到 fat jar 中:
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
入门模块
启动器实际上是一个空罐子。它的唯一目的是提供必要的依赖项以使用该库。您可以将其视为对入门所需内容的固执己见。
不要对添加启动器的项目做出假设。如果您要自动配置的库通常需要其他启动器,请同时提及它们。如果可选依赖项的数量很高,则提供一组适当的默认依赖项可能会很困难,因为您应该避免包含对于库的典型使用而言不必要的依赖项。换句话说,您不应该包含可选依赖项。
自动配置案例
创建一个acme-spring-boot-starter名称的maven项目
项目
acme-spring-boot-starter
- src/main
java
AcmeServer.java 需要被实例化的服务类
AcmeServerProperties.java 配置信息属性类
AcmeServiceAutoConfiguration.java 自动配置类
resources
META-INF/spring.factories 配置自动配置的属性文件
pom
<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.oysept</groupId>
<artifactId>aspectlog-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- springboot版本信息 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 自定义配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
spring-boot-configuration-processor作用:会在源数据文件(META-INF/spring-autoconfigure-metadata.properties)中自动扫描加载和自动配置有关的条件。也就是说,当编写starter时,会读取自动配置的条件,写入源数据文件中。
服务类
public class AcmeServer {
private String name;
public String sayServerName(){
return "I'm " + name + "! ";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
属性类
用于加载yml或properties中的属性
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "acme")
public class AcmeServerProperties {
private static final String NAME = "server0";
private String name = NAME;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
自动配置文件
@Configuration
@EnableConfigurationProperties(AcmeServerProperties.class)//声明开启属性注入
@ConditionalOnClass(AcmeServer.class)//判断类在类路径中是否存在,只有存在时才符合条件
@ConditionalOnProperty(prefix = "acme",value = "enabled",matchIfMissing = true)
public class AcmeServiceAutoConfiguration {
@Autowired
private AcmeServerProperties misServiceProperties;
@Bean(name = "acmeServer")
@ConditionalOnMissingBean(AcmeServer.class)//当容器中没有这个Bean就自动配置这个Bean
public AcmeServer mistraService(){
AcmeServer misService = new AcmeServer();
misService.setName(misServiceProperties.getName());
return misService;
}
}
在创建如下路径文件src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.acme.common.autoconfigure.acme.AcmeServiceAutoConfiguration
测试
使用mvn install方式把该模块自动打包
创建一个springboot项目pom依赖上面的jar
@SpringBootApplication
@RestController
public class DemoApplication {
@Autowired
private AcmeServer acmeService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RequestMapping("/")
public Object index(){
return "hello:"+acmeService.getName();
}
}
配置文件添加debug=true可以查看自动配置类
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY