Creating Your Own Auto-configuration
如果你在一家开发共享库的公司工作,或者你在一个开源库或商业库上工作,你可能想要开发自己的自动化配置。自动化配置类可以捆绑在外部 jar 中,并且仍然会被 Spring Boot 提取。
If you work in a company that develops shared libraries, or if you work on an open-source or commercial library, you might want to develop your own auto-configuration. Auto-configuration classes can be bundled in external jars and still be picked up by Spring Boot.
自动化配置可以关联到 “starter”,既提供自动化配置代码,也提供你将与该代码一起使用的典型库。我们首先介绍生成自己的自动化配置所需的知识,然后继续介绍 typical steps required to create a custom starter。
Auto-configuration can be associated to a “starter” that provides the auto-configuration code as well as the typical libraries that you would use with it. We first cover what you need to know to build your own auto-configuration and then we move on to the typical steps required to create a custom starter.
Understanding Auto-configured Beans
实现自动化配置的类用 @AutoConfiguration
进行注解。此注解本身用 @Configuration
进行元注解,使自动化配置成为标准 @Configuration
类。其他 @Conditional
注解用于约束自动化配置的应用时机。通常,自动化配置类使用 @ConditionalOnClass
和 @ConditionalOnMissingBean
注解。这确保了仅在找到相关类且你未声明自己的 @Configuration
时才应用自动化配置。
Classes that implement auto-configuration are annotated with @AutoConfiguration
.
This annotation itself is meta-annotated with @Configuration
, making auto-configurations standard @Configuration
classes.
Additional @Conditional
annotations are used to constrain when the auto-configuration should apply.
Usually, auto-configuration classes use @ConditionalOnClass
and @ConditionalOnMissingBean
annotations.
This ensures that auto-configuration applies only when relevant classes are found and when you have not declared your own @Configuration
.
你可以浏览 spring-boot-autoconfigure
的源代码,查看 Spring 提供的 @AutoConfiguration
类(参见 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件)。
You can browse the source code of {code-spring-boot-autoconfigure-src}[spring-boot-autoconfigure
] to see the @AutoConfiguration
classes that Spring provides (see the {code-spring-boot}/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports[META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
] file).
Locating Auto-configuration Candidates
Spring Boot 检查已发布 jar 中是否存在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件。该文件应该列出你的配置类,每行一个类名,如下面的示例所示:
Spring Boot checks for the presence of a META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
file within your published jar.
The file should list your configuration classes, with one class name per line, as shown in the following example:
com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
你可以使用 |
You can add comments to the imports file using the |
自动化配置必须通过在导入文件中命名来 only 加载。确保它们在特定软件包空间中定义,并且它们永远不会成为组件扫描的目标。此外,自动化配置类不应启用组件扫描以查找其他组件。而应该使用特定的 |
Auto-configurations must be loaded only by being named in the imports file.
Make sure that they are defined in a specific package space and that they are never the target of component scanning.
Furthermore, auto-configuration classes should not enable component scanning to find additional components.
Specific |
如果需要按特定顺序应用你的配置,则可以在 @AutoConfiguration
注解或专用 @AutoConfigureBefore
和 @AutoConfigureAfter
注解中使用 {code-spring-boot-autoconfigure-src}/AutoConfiguration.java 的 before
、beforeName
、after
和 afterName
属性。例如,如果你提供特定于 Web 的配置,则可能需要在 WebMvcAutoConfiguration
之后应用你的类。
If your configuration needs to be applied in a specific order, you can use the before
, beforeName
, after
and afterName
attributes on the {code-spring-boot-autoconfigure-src}/AutoConfiguration.java[@AutoConfiguration
] annotation or the dedicated {code-spring-boot-autoconfigure-src}/AutoConfigureBefore.java[@AutoConfigureBefore
] and {code-spring-boot-autoconfigure-src}/AutoConfigureAfter.java[@AutoConfigureAfter
] annotations.
For example, if you provide web-specific configuration, your class may need to be applied after WebMvcAutoConfiguration
.
如果你想对某些自动化配置进行排序,但这些配置不应该相互直接了解,则还可以使用 @AutoConfigureOrder
。该注解具有与常规 @Order
注解相同语义,但为自动化配置类提供了专用顺序。
If you want to order certain auto-configurations that should not have any direct knowledge of each other, you can also use @AutoConfigureOrder
.
That annotation has the same semantic as the regular @Order
annotation but provides a dedicated order for auto-configuration classes.
与标准 @Configuration
类一样,应用自动化配置类的顺序只影响它们 Bean 定义的顺序。随后创建这些 Bean 的顺序不受影响,并且由每个 Bean 的依赖关系和任何 @DependsOn
关系决定。
As with standard @Configuration
classes, the order in which auto-configuration classes are applied only affects the order in which their beans are defined.
The order in which those beans are subsequently created is unaffected and is determined by each bean’s dependencies and any @DependsOn
relationships.
Condition Annotations
你几乎总想在自动化配置类上包含一个或多个 @Conditional
注解。@ConditionalOnMissingBean
注解是一个常见示例,用于允许开发人员在对默认值不满意时覆盖自动化配置。
You almost always want to include one or more @Conditional
annotations on your auto-configuration class.
The @ConditionalOnMissingBean
annotation is one common example that is used to allow developers to override auto-configuration if they are not happy with your defaults.
Spring Boot 包含许多 @Conditional
注解,你可以通过注解 @Configuration
类或 @Bean
单个方法在自己的代码中重复使用这些注解。这些注解包括:
Spring Boot includes a number of @Conditional
annotations that you can reuse in your own code by annotating @Configuration
classes or individual @Bean
methods.
These annotations include:
Class Conditions
@ConditionalOnClass
和 @ConditionalOnMissingClass
注释根据特定类的存在或不存在将 @Configuration
类包含在内。由于使用 ASM 解析注释元数据,因此您可以使用 value
属性引用实际类,即使该类可能实际上未出现在正在运行的应用程序类路径中。如果您更愿意使用 String
值指定类名,则还可以使用 name
属性。
The @ConditionalOnClass
and @ConditionalOnMissingClass
annotations let @Configuration
classes be included based on the presence or absence of specific classes.
Due to the fact that annotation metadata is parsed by using ASM, you can use the value
attribute to refer to the real class, even though that class might not actually appear on the running application classpath.
You can also use the name
attribute if you prefer to specify the class name by using a String
value.
此机制不适用于 @Bean
方法,在该方法中通常返回类型是条件的目标:在方法上的条件应用之前,JVM 将加载类并可能处理类不存在时将失败的方法引用。
This mechanism does not apply the same way to @Bean
methods where typically the return type is the target of the condition: before the condition on the method applies, the JVM will have loaded the class and potentially processed method references which will fail if the class is not present.
要处理此场景,可以使用单独的 @Configuration
类来隔离条件,如下例所示:
To handle this scenario, a separate @Configuration
class can be used to isolate the condition, as shown in the following example:
如果你使用 |
If you use |
Bean Conditions
@ConditionalOnBean
和 @ConditionalOnMissingBean
注释允许基于特定 bean 的存在或不存在包含 bean。你可以使用 value
属性按类型指定 bean,或使用 name
按名称指定 bean。search
属性允许你在搜索 bean 时限定应考虑的 ApplicationContext
层次结构。
The @ConditionalOnBean
and @ConditionalOnMissingBean
annotations let a bean be included based on the presence or absence of specific beans.
You can use the value
attribute to specify beans by type or name
to specify beans by name.
The search
attribute lets you limit the ApplicationContext
hierarchy that should be considered when searching for beans.
当放置在 @Bean
方法上时,目标类型默认为该方法的返回类型,如下例所示:
When placed on a @Bean
method, the target type defaults to the return type of the method, as shown in the following example:
在前面的示例中,如果 ApplicationContext
中尚未包含类型为 SomeService
的 bean,则将创建 someService
bean。
In the preceding example, the someService
bean is going to be created if no bean of type SomeService
is already contained in the ApplicationContext
.
你需要非常仔细地考虑 bean 定义的添加顺序,因为这些条件是根据到目前为止处理的内容进行评估的。因此,我们建议仅对自动配置类使用 |
You need to be very careful about the order in which bean definitions are added, as these conditions are evaluated based on what has been processed so far.
For this reason, we recommend using only |
|
|
在声明 |
When declaring a |
Property Conditions
@ConditionalOnProperty
注释允许基于 Spring Environment 属性包含配置。使用 prefix
和 name
属性指定应检查的属性。默认情况下,将匹配任何存在且不等于 false
的属性。你还可以通过使用 havingValue
和 matchIfMissing
属性创建更高级的检查。
The @ConditionalOnProperty
annotation lets configuration be included based on a Spring Environment property.
Use the prefix
and name
attributes to specify the property that should be checked.
By default, any property that exists and is not equal to false
is matched.
You can also create more advanced checks by using the havingValue
and matchIfMissing
attributes.
Resource Conditions
@ConditionalOnResource
注释仅在存在特定资源时才能包含配置。可以使用通常的 Spring 约定指定资源,如下例所示:file:/home/user/test.dat
。
The @ConditionalOnResource
annotation lets configuration be included only when a specific resource is present.
Resources can be specified by using the usual Spring conventions, as shown in the following example: file:/home/user/test.dat
.
Web Application Conditions
@ConditionalOnWebApplication
和 @ConditionalOnNotWebApplication
注释允许根据应用程序是否是 Web 应用程序包含配置。基于 servlet 的 Web 应用程序是指使用 Spring WebApplicationContext
、定义 session
作用域或有 ConfigurableWebEnvironment
的任何应用程序。反应式 Web 应用程序是指使用 ReactiveWebApplicationContext
或有 ConfigurableReactiveWebEnvironment
的任何应用程序。
The @ConditionalOnWebApplication
and @ConditionalOnNotWebApplication
annotations let configuration be included depending on whether the application is a web application.
A servlet-based web application is any application that uses a Spring WebApplicationContext
, defines a session
scope, or has a ConfigurableWebEnvironment
.
A reactive web application is any application that uses a ReactiveWebApplicationContext
, or has a ConfigurableReactiveWebEnvironment
.
@ConditionalOnWarDeployment
和 @ConditionalOnNotWarDeployment
注释允许根据应用程序是否是部署到 servlet 容器的传统 WAR 应用程序包含配置。此条件不适用于使用嵌入式 Web 服务器运行的应用程序。
The @ConditionalOnWarDeployment
and @ConditionalOnNotWarDeployment
annotations let configuration be included depending on whether the application is a traditional WAR application that is deployed to a servlet container.
This condition will not match for applications that are run with an embedded web server.
SpEL Expression Conditions
@ConditionalOnExpression
注释允许根据 {url-spring-framework-docs}/core/expressions.html[SpEL 表达式] 的结果包含配置。
The @ConditionalOnExpression
annotation lets configuration be included based on the result of a {url-spring-framework-docs}/core/expressions.html[SpEL expression].
在表达式中引用 bean 将导致在上下文字符串处理非常早地初始化该 bean。因此,该 bean 将不符合进行后期处理(例如配置属性绑定)的条件,并且其状态可能不完整。 |
Referencing a bean in the expression will cause that bean to be initialized very early in context refresh processing. As a result, the bean won’t be eligible for post-processing (such as configuration properties binding) and its state may be incomplete. |
Testing your Auto-configuration
自动配置会受到许多因素的影响:用户配置(@Bean
定义和 Environment
自定)、条件评估(特定库的存在)等。具体来说,每个测试都应该创建一个明确定义的 ApplicationContext
,它表示那些自定的组合。ApplicationContextRunner
提供了一个实现这一目标的好方法。
An auto-configuration can be affected by many factors: user configuration (@Bean
definition and Environment
customization), condition evaluation (presence of a particular library), and others.
Concretely, each test should create a well defined ApplicationContext
that represents a combination of those customizations.
ApplicationContextRunner
provides a great way to achieve that.
在原生映像中运行测试时,ApplicationContextRunner
不起作用。
ApplicationContextRunner
doesn’t work when running the tests in a native image.
ApplicationContextRunner`通常定义为测试类的一个字段,用于收集基础、通用配置。以下示例确保总是调用 `MyServiceAutoConfiguration
:
ApplicationContextRunner
is usually defined as a field of the test class to gather the base, common configuration.
The following example makes sure that MyServiceAutoConfiguration
is always invoked:
如果必须定义多个自动配置,则无需对其声明排序,因为它们按与运行应用程序完全相同的顺序被调用。 |
If multiple auto-configurations have to be defined, there is no need to order their declarations as they are invoked in the exact same order as when running the application. |
每个测试都可以使用 runner 来表示特定的用例。例如,以下示例调用用户配置 (UserConfiguration
) 并检查自动配置是否正确关闭。调用 run
提供了一个回调上下文,可与 AssertJ
一起使用。
Each test can use the runner to represent a particular use case.
For instance, the sample below invokes a user configuration (UserConfiguration
) and checks that the auto-configuration backs off properly.
Invoking run
provides a callback context that can be used with AssertJ
.
还可以轻松自定义 Environment
,如下例所示:
It is also possible to easily customize the Environment
, as shown in the following example:
该 runner 还可用于显示 ConditionEvaluationReport
。该报告可以在 `INFO`或 `DEBUG`级别打印。以下示例展示了如何使用 `ConditionEvaluationReportLoggingListener`在自动配置测试中打印报告。
The runner can also be used to display the ConditionEvaluationReport
.
The report can be printed at INFO
or DEBUG
level.
The following example shows how to use the ConditionEvaluationReportLoggingListener
to print the report in auto-configuration tests.
Simulating a Web Context
如果您需要测试仅在 servlet 或响应式 web 应用程序上下文中运行的自动配置,请分别使用 WebApplicationContextRunner
或 ReactiveWebApplicationContextRunner
。
If you need to test an auto-configuration that only operates in a servlet or reactive web application context, use the WebApplicationContextRunner
or ReactiveWebApplicationContextRunner
respectively.
Overriding the Classpath
还可以测试在运行时不存在特定类和/或包时会发生的情况。Spring Boot 随附一个 FilteredClassLoader
,该 runner 可以轻松使用它。在以下示例中,我们断言如果 MyService
不存在,则自动配置将被正确实用禁用:
It is also possible to test what happens when a particular class and/or package is not present at runtime.
Spring Boot ships with a FilteredClassLoader
that can easily be used by the runner.
In the following example, we assert that if MyService
is not present, the auto-configuration is properly disabled:
Creating Your Own Starter
一个典型的 Spring Boot starter 包含用于自动配置和自定义给定技术基础设施的代码,我们称之为“acme”。为了使其易于扩展,可以将专用命名空间中的一些配置键公开给环境。最后,提供一个“starter”依赖项,以帮助用户尽可能轻松地入门。
A typical Spring Boot starter contains code to auto-configure and customize the infrastructure of a given technology, let’s call that "acme". To make it easily extensible, a number of configuration keys in a dedicated namespace can be exposed to the environment. Finally, a single "starter" dependency is provided to help users get started as easily as possible.
具体来说,自定义 starter 可以包含以下内容:
Concretely, a custom starter can contain the following:
-
The
autoconfigure
module that contains the auto-configuration code for "acme". -
The
starter
module that provides a dependency to theautoconfigure
module as well as "acme" and any additional dependencies that are typically useful. In a nutshell, adding the starter should provide everything needed to start using that library.
这两个模块中的分离绝不是必需的。如果“acme”有几个类型、选项或可选特性,那么最好将自动配置分开,因为您可以清楚地表示某些特性是可选的。此外,您可以创建 starter 来说明对这些可选依赖项的观点。同时,其他人只能依赖 `autoconfigure`模块并用不同的观点制定自己的 starter。
This separation in two modules is in no way necessary.
If "acme" has several flavors, options or optional features, then it is better to separate the auto-configuration as you can clearly express the fact some features are optional.
Besides, you have the ability to craft a starter that provides an opinion about those optional dependencies.
At the same time, others can rely only on the autoconfigure
module and craft their own starter with different opinions.
如果自动配置相对简单且没有可选特性,则将 starter 中的两个模块合并肯定是一种选择。
If the auto-configuration is relatively straightforward and does not have optional features, merging the two modules in the starter is definitely an option.
Naming
您应确保为 starter 提供适当的命名空间。不要以 `spring-boot`开头模块名称,即使您使用不同的 Maven `groupId`也是如此。我们可能在将来为要在其中进行自动配置的事物提供官方支持。
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.
一般而言,您应当以启动器命名合并模块。例如,假定您正在为“acme”创建启动器,并将自动配置模块命名为 acme-spring-boot
,将启动器命名为 acme-spring-boot-starter
。如果您只有一个模块将两者结合在一起,那么应将它命名为 acme-spring-boot-starter
。
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
and the starter acme-spring-boot-starter
.
If you only have one module that combines the two, name it acme-spring-boot-starter
.
Configuration keys
如果您的启动器提供配置键,请为它们使用唯一的命名空间。特别是,请勿将您的键包含在 Spring Boot 使用的命名空间中(例如 server
、management
、spring`等)。如果您使用相同的命名空间,我们可能会将来以破坏您的模块的方式修改这些命名空间。一般而言,使用您自己的命名空间为所有键加上前缀(例如 `acme
)。
If your starter provides configuration keys, use a unique namespace for them.
In particular, do not include your keys in the namespaces that Spring Boot uses (such as server
, management
, spring
, and so on).
If you use the same namespace, we may modify these namespaces in the future in ways that break your modules.
As a rule of thumb, prefix all your keys with a namespace that you own (for example acme
).
确保通过为每个属性添加字段 javadoc 文档化配置键,如下面的示例所示:
Make sure that configuration keys are documented by adding field javadoc for each property, as shown in the following example:
您应该只将纯文本与 `@ConfigurationProperties`字段 Javadoc 一起使用,因为在将它们添加到 JSON 之前不会对其进行处理。 |
You should only use plain text with |
以下是在内部遵循的一些规则以确保描述一致:
Here are some rules we follow internally to make sure descriptions are consistent:
-
Do not start the description by "The" or "A".
-
For
boolean
types, start the description with "Whether" or "Enable". -
For collection-based types, start the description with "Comma-separated list"
-
Use
java.time.Duration
rather thanlong
and describe the default unit if it differs from milliseconds, such as "If a duration suffix is not specified, seconds will be used". -
Do not provide the default value in the description unless it has to be determined at runtime.
确保 trigger meta-data generation,以使 IDE 帮助也适用于您的键。您可能需要查看生成的元数据 (META-INF/spring-configuration-metadata.json
) 以确保您的键已正确记录。在兼容的 IDE 中使用您自己的启动器也是验证元数据质量的好主意。
Make sure to trigger meta-data generation so that IDE assistance is available for your keys as well.
You may want to review the generated metadata (META-INF/spring-configuration-metadata.json
) to make sure your keys are properly documented.
Using your own starter in a compatible IDE is also a good idea to validate that quality of the metadata.
The “autoconfigure” Module
autoconfigure`模块包含开始使用该库所需的一切。它还可能包含配置键定义(例如 `@ConfigurationProperties
)和任何可用于进一步自定义组件初始化方式的回调接口。
The autoconfigure
module contains everything that is necessary to get started with the library.
It may also contain configuration key definitions (such as @ConfigurationProperties
) and any callback interface that can be used to further customize how the components are initialized.
您应将对该库的依赖项标记为可选的,以便可以更轻松地在项目中包含 `autoconfigure`模块。如果您这样做,则不会提供该库,并且默认情况下,Spring Boot 会进行备份。 |
You should mark the dependencies to the library as optional so that you can include the |
Spring Boot 使用注释处理器在元数据文件 (META-INF/spring-autoconfigure-metadata.properties
) 中收集自动配置的条件。如果该文件存在,则它将用于热切地过滤不匹配的自动配置,这将改善启动时间。
Spring Boot uses an annotation processor to collect the conditions on auto-configurations in a metadata file (META-INF/spring-autoconfigure-metadata.properties
).
If that file is present, it is used to eagerly filter auto-configurations that do not match, which will improve startup time.
在使用 Maven 构建时,建议在包含自动配置的模块中添加以下依赖项:
When building with Maven, it is recommended to add the following dependency in a module that contains auto-configurations:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
如果您已在应用程序中直接定义自动配置,请确保配置 `spring-boot-maven-plugin`以防止 `repackage`目标向 uber jar 中添加依赖项:
If you have defined auto-configurations directly in your application, make sure to configure the spring-boot-maven-plugin
to prevent the repackage
goal from adding the dependency into the uber 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>
对于 Gradle,应在 `annotationProcessor`配置中声明依赖项,如下面的示例所示:
With Gradle, the dependency should be declared in the annotationProcessor
configuration, as shown in the following example:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}
Starter Module
启动器实际上是一个空 jar。它的唯一目的是提供与该库协同工作的必要依赖项。您可以将其视为开始所需内容的一种观点。
The starter is really an empty jar. Its only purpose is to provide the necessary dependencies to work with the library. You can think of it as an opinionated view of what is required to get started.
请不要对添加启动程序的项目做出假设。如果您要自动配置的库通常需要其他启动程序,请也提及它们。如果可选依赖项的数量很大,则提供一组适当的 `@`1 依赖项可能很困难,因为您应该避免包含对于库的典型用途来说不必要的依赖项。换句话说,您不应该包含可选的依赖项。
Do not make assumptions about the project in which your starter is added. If the library you are auto-configuring typically requires other starters, mention them as well. Providing a proper set of default dependencies may be hard if the number of optional dependencies is high, as you should avoid including dependencies that are unnecessary for a typical usage of the library. In other words, you should not include optional dependencies.
无论哪种方式,您的启动程序都必须直接或间接引用核心 Spring Boot 启动程序 (`@`2)(如果您的启动程序依赖另一个启动程序,则无需添加它)。如果只创建了自定义启动程序的项目,Spring Boot 的核心功能将通过核心启动程序的存在得到尊重。 |
Either way, your starter must reference the core Spring Boot starter ( |