此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.1Spring中文文档

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.1Spring中文文档

如果您在开发共享库的公司工作,或者您从事开源或商业库的工作,则可能需要开发自己的自动配置。 自动配置类可以捆绑在外部 jar 中,并且仍可由 Spring Boot 拾取。Spring中文文档

自动配置可以与提供自动配置代码以及您将与之一起使用的典型库的“启动器”相关联。 我们首先介绍构建自己的自动配置所需的知识,然后我们继续介绍创建自定义启动器所需的典型步骤Spring中文文档

了解自动配置的 Bean

实现自动配置的类用 批注。 此注解本身是元注解的,使自动配置成为标准类。 其他注释用于限制何时应用自动配置。 通常,自动配置类使用和注释。 这可确保自动配置仅在找到相关类且未声明自己的类时才适用。@AutoConfiguration@Configuration@Configuration@Conditional@ConditionalOnClass@ConditionalOnMissingBean@ConfigurationSpring中文文档

您可以浏览spring-boot-autoconfigure的源码,查看Spring提供的类(参见META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件)。@AutoConfigurationSpring中文文档

查找自动配置候选项

Spring Boot 会检查已发布的 jar 中是否存在文件。 该文件应列出您的配置类,每行有一个类名,如以下示例所示:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsSpring中文文档

com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
您可以使用该字符向导入文件添加注释。#
自动配置只能通过在导入文件中命名来加载。 确保它们在特定的包空间中定义,并且它们永远不会成为组件扫描的目标。 此外,自动配置类不应启用组件扫描以查找其他组件。 应改用特定的注释。@Import

如果需要按特定顺序应用配置,可以在@AutoConfiguration注或专用@AutoConfigureBefore@AutoConfigureAfter注中使用 、 和 属性。 例如,如果提供特定于 Web 的配置,则可能需要在 之后应用类。beforebeforeNameafterafterNameWebMvcAutoConfigurationSpring中文文档

如果要订购某些不应相互直接了解的自动配置,也可以使用 . 该注释与常规注释具有相同的语义,但为自动配置类提供了专用顺序。@AutoConfigureOrder@OrderSpring中文文档

与标准类一样,自动配置类的应用顺序仅影响其 Bean 的定义顺序。 随后创建这些 Bean 的顺序不受影响,由每个 Bean 的依赖关系和任何关系决定。@Configuration@DependsOnSpring中文文档

您可以使用该字符向导入文件添加注释。#
自动配置只能通过在导入文件中命名来加载。 确保它们在特定的包空间中定义,并且它们永远不会成为组件扫描的目标。 此外,自动配置类不应启用组件扫描以查找其他组件。 应改用特定的注释。@Import

条件注释

您几乎总是希望在自动配置类中包含一个或多个注释。 注解是一个常见示例,用于允许开发人员在对默认值不满意时覆盖自动配置。@Conditional@ConditionalOnMissingBeanSpring中文文档

Spring Boot 包含许多注释,您可以通过注释类或单个方法在自己的代码中重用这些注释。 这些注释包括:@Conditional@Configuration@BeanSpring中文文档

上课条件

和 注释允许根据特定类的存在与否来包含类。 由于注释元数据是使用 ASM 分析的,因此可以使用该属性来引用实际类,即使该类实际上可能没有出现在正在运行的应用程序类路径上。 如果您希望使用值来指定类名,也可以使用该属性。@ConditionalOnClass@ConditionalOnMissingClass@ConfigurationvaluenameStringSpring中文文档

此机制不适用于通常以返回类型为条件目标的方法:在方法上的条件应用之前,JVM 将加载类和可能已处理的方法引用,如果该类不存在,则这些引用将失败。@BeanSpring中文文档

若要处理此方案,可以使用单独的类来隔离条件,如以下示例所示:@ConfigurationSpring中文文档

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@AutoConfiguration
// 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();
		}

	}

}
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
// Some conditions ...
class MyAutoConfiguration {

	// Auto-configured beans ...
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(SomeService::class)
	class SomeServiceConfiguration {

		@Bean
		@ConditionalOnMissingBean
		fun someService(): SomeService {
			return SomeService()
		}

	}

}
如果使用 或作为元注释的一部分来编写自己的组合注释,则必须使用,因为在这种情况下不处理引用类。@ConditionalOnClass@ConditionalOnMissingClassname

豆类条件

和 注释允许根据特定 Bean 的存在与否来包含 Bean。 您可以使用该属性按类型指定 Bean 或按名称指定 Bean。 该属性允许您限制搜索 Bean 时应考虑的层次结构。@ConditionalOnBean@ConditionalOnMissingBeanvaluenamesearchApplicationContextSpring中文文档

放置在方法上时,目标类型默认为方法的返回类型,如以下示例所示:@BeanSpring中文文档

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
public class MyAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public SomeService someService() {
		return new SomeService();
	}

}
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	fun someService(): SomeService {
		return SomeService()
	}

}

在前面的示例中,如果 中没有 类型的 Bean 已包含在 中,则将创建 Bean。someServiceSomeServiceApplicationContextSpring中文文档

您需要非常小心添加 Bean 定义的顺序,因为这些条件是根据到目前为止处理的内容来评估的。 出于这个原因,我们建议在自动配置类上仅使用注释(因为这些注释保证在添加任何用户定义的 Bean 定义后加载)。@ConditionalOnBean@ConditionalOnMissingBean
@ConditionalOnBean并且不阻止创建类。 在类级别使用这些条件和用注释标记每个包含的方法之间的唯一区别是,如果条件不匹配,前者会阻止将类注册为 Bean。@ConditionalOnMissingBean@Configuration@Bean@Configuration
声明方法时,请在方法的返回类型中提供尽可能多的类型信息。 例如,如果 Bean 的 concrete 类实现了一个接口,那么 bean 方法的返回类型应该是 concrete 类,而不是接口。 在使用 Bean 条件时,在方法中提供尽可能多的类型信息尤为重要,因为它们的评估只能依赖于方法签名中可用的类型信息。@Bean@Bean

物业条件

该注释允许基于 Spring Environment 属性包含配置。 使用 and 属性指定应检查的属性。 默认情况下,任何存在且不等于的属性都将匹配。 您还可以使用 和 属性创建更高级的检查。@ConditionalOnPropertyprefixnamefalsehavingValuematchIfMissingSpring中文文档

如果属性中指定了多个名称,则所有属性都必须通过测试才能匹配条件。nameSpring中文文档

资源条件

注释允许仅在存在特定资源时才包含配置。 可以使用通常的 Spring 约定指定资源,如以下示例所示:.@ConditionalOnResourcefile:/home/user/test.datSpring中文文档

Web 应用程序条件

和 注释允许根据应用程序是否为 Web 应用程序来包含配置。 基于 servlet 的 Web 应用程序是使用 Spring 、定义作用域或具有 . 反应式 Web 应用程序是使用 或具有 .@ConditionalOnWebApplication@ConditionalOnNotWebApplicationWebApplicationContextsessionConfigurableWebEnvironmentReactiveWebApplicationContextConfigurableReactiveWebEnvironmentSpring中文文档

和 注释允许包含配置,具体取决于应用程序是否是部署到 servlet 容器的传统 WAR 应用程序。 对于使用嵌入式 Web 服务器运行的应用程序,此条件将不匹配。@ConditionalOnWarDeployment@ConditionalOnNotWarDeploymentSpring中文文档

SpEL表达条件

注释允许根据 SpEL 表达式的结果包含配置。@ConditionalOnExpressionSpring中文文档

在表达式中引用 Bean 将导致该 Bean 在上下文刷新处理中很早就初始化。 因此,Bean 将不符合后处理条件(例如配置属性绑定),并且其状态可能不完整。
如果使用 或作为元注释的一部分来编写自己的组合注释,则必须使用,因为在这种情况下不处理引用类。@ConditionalOnClass@ConditionalOnMissingClassname
您需要非常小心添加 Bean 定义的顺序,因为这些条件是根据到目前为止处理的内容来评估的。 出于这个原因,我们建议在自动配置类上仅使用注释(因为这些注释保证在添加任何用户定义的 Bean 定义后加载)。@ConditionalOnBean@ConditionalOnMissingBean
@ConditionalOnBean并且不阻止创建类。 在类级别使用这些条件和用注释标记每个包含的方法之间的唯一区别是,如果条件不匹配,前者会阻止将类注册为 Bean。@ConditionalOnMissingBean@Configuration@Bean@Configuration
声明方法时,请在方法的返回类型中提供尽可能多的类型信息。 例如,如果 Bean 的 concrete 类实现了一个接口,那么 bean 方法的返回类型应该是 concrete 类,而不是接口。 在使用 Bean 条件时,在方法中提供尽可能多的类型信息尤为重要,因为它们的评估只能依赖于方法签名中可用的类型信息。@Bean@Bean
在表达式中引用 Bean 将导致该 Bean 在上下文刷新处理中很早就初始化。 因此,Bean 将不符合后处理条件(例如配置属性绑定),并且其状态可能不完整。

测试自动配置

自动配置可能受到许多因素的影响:用户配置(定义和自定义)、条件评估(存在特定库)等。 具体而言,每个测试都应创建一个定义良好的定义,以表示这些自定义的组合。 提供了实现这一目标的好方法。@BeanEnvironmentApplicationContextApplicationContextRunnerSpring中文文档

ApplicationContextRunner在本机映像中运行测试时不起作用。

ApplicationContextRunner通常定义为测试类的一个字段来收集基础、通用配置。 以下示例确保始终调用该示例:MyServiceAutoConfigurationSpring中文文档

	private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
		.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
	val contextRunner = ApplicationContextRunner()
		.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration::class.java))
如果必须定义多个自动配置,则无需对它们的声明进行排序,因为它们的调用顺序与运行应用程序时的顺序完全相同。

每个测试都可以使用运行器来表示特定的用例。 例如,下面的示例调用用户配置 () 并检查自动配置是否正确回退。 调用提供了可与 一起使用的回调上下文。UserConfigurationrunAssertJSpring中文文档

	@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");
		}

	}
	@Test
	fun defaultServiceBacksOff() {
		contextRunner.withUserConfiguration(UserConfiguration::class.java)
			.run { context: AssertableApplicationContext ->
				assertThat(context).hasSingleBean(MyService::class.java)
				assertThat(context).getBean("myCustomService")
					.isSameAs(context.getBean(MyService::class.java))
			}
	}

	@Configuration(proxyBeanMethods = false)
	internal class UserConfiguration {

		@Bean
		fun myCustomService(): MyService {
			return MyService("mine")
		}

	}

也可以轻松自定义 ,如以下示例所示:EnvironmentSpring中文文档

	@Test
	void serviceNameCanBeConfigured() {
		this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
			assertThat(context).hasSingleBean(MyService.class);
			assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
		});
	}
	@Test
	fun serviceNameCanBeConfigured() {
		contextRunner.withPropertyValues("user.name=test123").run { context: AssertableApplicationContext ->
			assertThat(context).hasSingleBean(MyService::class.java)
			assertThat(context.getBean(MyService::class.java).name).isEqualTo("test123")
		}
	}

运行器还可用于显示 . 报告可以在 OR 级别打印。 以下示例演示如何使用 在自动配置测试中打印报表。ConditionEvaluationReportINFODEBUGConditionEvaluationReportLoggingListenerSpring中文文档

import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

class MyConditionEvaluationReportingTests {

	@Test
	void autoConfigTest() {
		new ApplicationContextRunner()
			.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
			.run((context) -> {
				// Test something...
			});
	}

}
import org.junit.jupiter.api.Test
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
import org.springframework.boot.logging.LogLevel
import org.springframework.boot.test.context.assertj.AssertableApplicationContext
import org.springframework.boot.test.context.runner.ApplicationContextRunner

class MyConditionEvaluationReportingTests {

	@Test
	fun autoConfigTest() {
		ApplicationContextRunner()
			.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
			.run { context: AssertableApplicationContext? -> }
	}

}

模拟 Web 上下文

如果需要测试仅在 servlet 或响应式 Web 应用程序上下文中运行的自动配置,请分别使用 或。WebApplicationContextRunnerReactiveWebApplicationContextRunnerSpring中文文档

覆盖类路径

还可以测试当特定类和/或包在运行时不存在时会发生什么。 Spring Boot 附带了一个可以被运行器轻松使用。 在以下示例中,我们断言如果不存在,则自动配置已正确禁用:FilteredClassLoaderMyServiceSpring中文文档

	@Test
	void serviceIsIgnoredIfLibraryIsNotPresent() {
		this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
			.run((context) -> assertThat(context).doesNotHaveBean("myService"));
	}
	@Test
	fun serviceIsIgnoredIfLibraryIsNotPresent() {
		contextRunner.withClassLoader(FilteredClassLoader(MyService::class.java))
			.run { context: AssertableApplicationContext? ->
				assertThat(context).doesNotHaveBean("myService")
			}
	}
ApplicationContextRunner在本机映像中运行测试时不起作用。
如果必须定义多个自动配置,则无需对它们的声明进行排序,因为它们的调用顺序与运行应用程序时的顺序完全相同。

创建自己的入门

典型的Spring Boot启动器包含用于自动配置和自定义给定技术的基础结构的代码,我们称之为“极致”。 为了使其易于扩展,可以向环境公开专用命名空间中的许多配置键。 最后,提供了单个“启动器”依赖项,以帮助用户尽可能轻松地入门。Spring中文文档

具体而言,自定义启动器可以包含以下内容:Spring中文文档

  • 包含“acme”自动配置代码的模块。autoconfigureSpring中文文档

  • 为模块提供依赖项的模块,以及“acme”和通常有用的任何其他依赖项。 简而言之,添加启动器应该提供开始使用该库所需的一切。starterautoconfigureSpring中文文档

在两个模块中进行这种分离是没有必要的。 如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的。 此外,您还可以制作一个启动器,提供有关这些可选依赖项的意见。 同时,其他人只能依靠模块,用不同的意见制作自己的启动器。autoconfigureSpring中文文档

如果自动配置相对简单且没有可选功能,则在启动器中合并两个模块绝对是一种选择。Spring中文文档

命名

应确保为启动器提供适当的命名空间。 不要以 开头的模块名称,即使您使用不同的 Maven 。 我们可能会为您将来自动配置的东西提供官方支持。spring-bootgroupIdSpring中文文档

根据经验,您应该在启动器之后命名组合模块。 例如,假设您正在为“acme”创建一个启动器,并且您将自动配置模块和启动器命名为 。 如果您只有一个将两者结合在一起的模块,请将其命名为 。acme-spring-bootacme-spring-boot-starteracme-spring-boot-starterSpring中文文档

配置键

如果初学者提供配置键,请为它们使用唯一的命名空间。 特别是,不要在 Spring Boot 使用的命名空间(如 、 、 等)中包含密钥。 如果您使用相同的命名空间,我们将来可能会以破坏模块的方式修改这些命名空间。 根据经验,请在所有键前面加上您拥有的命名空间(例如 )。servermanagementspringacmeSpring中文文档

通过为每个属性添加字段 javadoc 来确保记录配置键,如以下示例所示:Spring中文文档

import java.time.Duration;

import org.springframework.boot.context.properties.ConfigurationProperties;

@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 ...

	public boolean isCheckLocation() {
		return this.checkLocation;
	}

	public void setCheckLocation(boolean checkLocation) {
		this.checkLocation = checkLocation;
	}

	public Duration getLoginTimeout() {
		return this.loginTimeout;
	}

	public void setLoginTimeout(Duration loginTimeout) {
		this.loginTimeout = loginTimeout;
	}

}
import org.springframework.boot.context.properties.ConfigurationProperties
import java.time.Duration

@ConfigurationProperties("acme")
class AcmeProperties(

	/**
	 * Whether to check the location of acme resources.
	 */
	var isCheckLocation: Boolean = true,

	/**
	 * Timeout for establishing a connection to the acme server.
	 */
	var loginTimeout:Duration = Duration.ofSeconds(3))
您应该只使用带有字段 Javadoc 的纯文本,因为它们在添加到 JSON 之前不会被处理。@ConfigurationProperties

如果与记录类一起使用,则应通过类级 Javadoc 标记提供记录组件的描述(记录类中没有显式实例字段来放置常规字段级 Javadoc)。@ConfigurationProperties@paramSpring中文文档

以下是我们在内部遵循的一些规则,以确保描述的一致性:Spring中文文档

  • 不要以“The”或“A”开头描述。Spring中文文档

  • 对于类型,请以“是否”或“启用”开始描述。booleanSpring中文文档

  • 对于基于集合的类型,请以“逗号分隔列表”开头说明Spring中文文档

  • 如果默认单位与毫秒不同,则使用而不是描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。java.time.DurationlongSpring中文文档

  • 不要在描述中提供默认值,除非必须在运行时确定它。Spring中文文档

请确保触发元数据生成,以便 IDE 帮助也可用于密钥。 您可能需要查看生成的元数据 () 以确保您的密钥已正确记录。 在兼容的 IDE 中使用自己的启动器也是验证元数据质量的好主意。META-INF/spring-configuration-metadata.jsonSpring中文文档

“autoconfigure”模块

该模块包含开始使用库所需的所有内容。 它还可能包含配置键定义(例如 )和任何可用于进一步自定义组件初始化方式的回调接口。autoconfigure@ConfigurationPropertiesSpring中文文档

应将库的依赖项标记为可选,以便更轻松地将模块包含在项目中。 如果这样做,则不会提供该库,并且默认情况下,Spring Boot 会退出。autoconfigure

Spring Boot 使用注释处理器在元数据文件 () 中收集自动配置的条件。 如果存在该文件,则用于急切地过滤不匹配的自动配置,这将缩短启动时间。META-INF/spring-autoconfigure-metadata.propertiesSpring中文文档

使用 Maven 构建时,建议在包含自动配置的模块中添加以下依赖:Spring中文文档

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-autoconfigure-processor</artifactId>
	<optional>true</optional>
</dependency>

如果您已直接在应用程序中定义了自动配置,请确保配置以防止目标将依赖项添加到 uber jar 中:spring-boot-maven-pluginrepackageSpring中文文档

<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>

使用 Gradle 时,应在配置中声明依赖项,如以下示例所示:annotationProcessorSpring中文文档

dependencies {
	annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}

入门模块

启动器实际上是一个空罐子。 它的唯一目的是提供必要的依赖项来使用库。 你可以把它看作是对开始所需的一种固执己见的观点。Spring中文文档

不要对添加启动器的项目做出假设。 如果您要自动配置的库通常需要其他启动器,请同时提及它们。 如果可选依赖项的数量很多,则提供一组适当的默认依赖项可能很困难,因为应避免包含库的典型用法中不必要的依赖项。 换言之,不应包含可选依赖项。Spring中文文档

无论哪种方式,您的启动器都必须直接或间接引用核心 Spring Boot 启动器 ()(如果您的启动器依赖于另一个启动器,则无需添加它)。 如果仅使用自定义启动器创建项目,则 Spring Boot 的核心功能将因核心启动器的存在而受到尊重。spring-boot-starter
您应该只使用带有字段 Javadoc 的纯文本,因为它们在添加到 JSON 之前不会被处理。@ConfigurationProperties
应将库的依赖项标记为可选,以便更轻松地将模块包含在项目中。 如果这样做,则不会提供该库,并且默认情况下,Spring Boot 会退出。autoconfigure
无论哪种方式,您的启动器都必须直接或间接引用核心 Spring Boot 启动器 ()(如果您的启动器依赖于另一个启动器,则无需添加它)。 如果仅使用自定义启动器创建项目,则 Spring Boot 的核心功能将因核心启动器的存在而受到尊重。spring-boot-starter