创建自己的自动配置
如果您在开发共享库的公司工作,或者您在开源或商业库中工作,则可能需要开发自己的自动配置。自动配置类可以捆绑在外部jar中,仍然可以通过Spring Boot获取。
自动配置可以与“启动器”相关联,该“启动器”提供自动配置代码以及您将使用它的典型库。我们首先介绍了构建自己的自动配置需要了解的内容,然后我们将继续介绍创建自定义启动器所需的 典型步骤。
可以使用演示项目来展示如何逐步创建启动器。
了解自动配置的Bean
在引擎盖下,自动配置使用标准@Configuration
类实现。其他@Conditional
注释用于约束何时应用自动配置。通常,自动配置类使用@ConditionalOnClass
和 @ConditionalOnMissingBean
注释。这可确保仅在找到相关类时以及未声明自己的类时才应用自动配置 @Configuration
。
您可以浏览源代码spring-boot-autoconfigure
以查看@Configuration
Spring提供的类(请参阅 META-INF/spring.factories
文件)。
找到自动配置候选者
Spring Boot会检查META-INF/spring.factories
已发布jar中是否存在文件。该文件应列出EnableAutoConfiguration
密钥下的配置类 ,如以下示例所示:
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
自动配置,必须加载这种方式只。确保它们在特定的包空间中定义,并且它们永远不是组件扫描的目标。此外,自动配置类不应启用组件扫描以查找其他组件。应该使用特定的
@ Import
s代替。
如果需要按特定顺序应用配置,则可以使用 @AutoConfigureAfter
或 @AutoConfigureBefore
注释。例如,如果您提供特定于Web的配置,则可能需要在之后应用您的类 WebMvcAutoConfiguration
。
如果您想订购某些不应该彼此直接了解的自动配置,您也可以使用@AutoConfigureOrder
。该注释与常规注释具有相同的语义,@Order
但为自动配置类提供了专用顺序。
条件注释
您几乎总是希望@Conditional
在自动配置类中包含一个或多个注释。该@ConditionalOnMissingBean
注释是用来让开发者重写自动配置,如果他们不满意自己的缺省值一个常见的例子。
Spring Boot包含许多@Conditional
注释,您可以通过注释@Configuration
类或单个@Bean
方法在自己的代码中重用这些注释。这些注释包括:
- Class条件
- Bean条件
- Property条件
- Resource条件
- Web Application条件
- spEL表达式条件
Class Conditions
@ConditionalOnClass
和@ConditionalOnMissingClass
注解让 @Configuration
类基于特定类的存在或不存在被包括在内。由于使用ASM解析注释元数据这一事实,您可以使用该value
属性来引用真实类,即使该类实际上可能不会出现在正在运行的应用程序类路径中。name
如果您希望使用String
值指定类名,也可以使用该属性。
此机制不适用于@Bean
通常返回类型是条件的目标的方法:在方法的条件适用之前,JVM将加载类和可能处理的方法引用,如果类不是当下。
要处理此方案,@Configuration
可以使用单独的类来隔离条件,如以下示例所示:
1 |
|
如果您使用
@ConditionalOnClass
或@ConditionalOnMissingClass
作为元注释的一部分来组成您自己的组合注释,则必须使用name
在这种情况下引用该类而不处理。
Bean Conditions
@ConditionalOnBean
和@ConditionalOnMissingBean
注解让豆基于特定豆的存在或不存在被包括在内。您可以使用该value
属性按类型name
指定bean 或按名称指定bean。该search
属性允许您限制ApplicationContext
搜索Bean时应考虑的层次结构。
放置在@Bean
方法上时,目标类型默认为方法的返回类型,如以下示例所示:
1 |
|
在前面的示例中,myService
如果没有MyService
包含类型的bean,则将创建bean ApplicationContext
。
您需要非常小心添加bean定义的顺序,因为这些条件是根据到目前为止已处理的内容进行评估的。出于这个原因,我们建议仅对自动配置类使用
@ConditionalOnBean
和@ConditionalOnMissingBean
注释(因为在添加任何用户定义的bean定义之后,这些类保证加载)。
@ConditionalOnBean
并且@ConditionalOnMissingBean
不会阻止@Configuration
创建类。在类级别使用这些条件和@Bean
使用注释标记每个包含的方法之间的唯一区别是,@Configuration
如果条件不匹配,前者会阻止将类注册为bean。
Property Conditions
该@ConditionalOnProperty
注解让配置基于Spring的环境属性被包括在内。使用prefix
和name
属性指定应检查的属性。默认情况下,false
匹配存在且不相等的任何属性。您还可以使用havingValue
和 matchIfMissing
属性创建更高级的检查。
Resource Conditions
该@ConditionalOnResource
注解让配置被包括仅当特定资源是否存在。可以使用常用的Spring约定来指定资源,如以下示例所示:
1 | file:/home/user/test.dat |
Web Application Conditions
在@ConditionalOnWebApplication
和@ConditionalOnNotWebApplication
注释,让配置取决于应用程序是否是一个“Web应用程序”被包括在内。Web应用程序是使用Spring WebApplicationContext
,定义session
范围或具有的任何应用程序StandardServletEnvironment
。
spEL Conditions
该@ConditionalOnExpression
注解让配置基于一个的结果被包括使用SpEL表达。
测试自动配置
自动配置可能受许多因素的影响:用户配置(@Bean
定义和Environment
自定义),条件评估(存在特定库)等。具体而言,每个测试都应创建一个定义良好的ApplicationContext
代表,以表示这些自定义的组合。 ApplicationContextRunner
提供了实现这一目标的好方法。
ApplicationContextRunner
通常被定义为测试类的一个字段来收集基本的通用配置。以下示例确保 UserServiceAutoConfiguration
始终调用:
1 | private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() |
如果必须定义多个自动配置,则无需按照与运行应用程序时完全相同的顺序调用它们的声明。
每个测试都可以使用运行器来表示特定的用例。例如,下面的示例调用用户配置(UserConfiguration
)并检查自动配置是否正确退回。调用run
提供了可以使用的回调上下文Assert4J
。
1 |
|
也可以轻松自定义Environment
,如下例所示:
1 |
|
Runner也可以用来显示ConditionEvaluationReport
。报告可以打印INFO
或DEBUG
级别打印。以下示例显示如何使用ConditionEvaluationReportLoggingListener
在自动配置测试中打印报表。
1 |
|
模拟Web上下文
如果您需要测试仅在Servlet或Reactive Web应用程序上下文中运行的自动配置,请分别使用WebApplicationContextRunner
或ReactiveWebApplicationContextRunner
。
覆盖Classpath
还可以测试在运行时不存在特定类和/或包时发生的情况。Spring Boot附带一个FilteredClassLoader
可以由跑步者轻松使用的。在以下示例中,我们声明如果UserService
不存在,则会正确禁用自动配置:
1 |
|
创建自己的启动器
的完整Spring Boot启动程序可能包含以下组件:
autoconfigure
包含自动配置代码的模块。- 该
starter
模块提供对autoconfigure
模块以及库的依赖关系以及通常有用的任何其他依赖关系。简而言之,添加启动器应该提供开始使用该库所需的一切。
如果您不需要将这两个问题分开,则可以将自动配置代码和依赖关系管理组合在一个模块中。
命名
您应该确保为您的启动器提供适当的命名空间。spring-boot
即使您使用其他Maven,也不要使用它来启动模块名称groupId
。我们可能会为您将来自动配置的内容提供官方支持。
根据经验,您应该在启动器后命名组合模块。例如,假设您正在为“acme”创建启动器,并且您将自动配置模块acme-spring-boot-autoconfigure
和启动器命名为acme-spring-boot-starter
。如果您只有一个组合两者的模块,请将其命名acme-spring-boot-starter
。
此外,如果您的启动器提供配置密钥,请为它们使用唯一的命名空间。特别是,不包括你在春天开机使用的命名空间键(如 server
,management
,spring
,等)。如果您使用相同的命名空间,我们将来可能会以破坏您的模块的方式修改这些命名空间。
确保 触发元数据生成,以便为您的密钥提供IDE帮助。您可能希望查看生成的元数据(META-INF/spring-configuration-metadata.json
)以确保正确记录您的密钥。
autoconfigure模块
该autoconfigure
模块包含开始使用库所需的所有内容。它还可以包含配置键定义(例如 @ConfigurationProperties
)和任何可用于进一步自定义组件初始化方式的回调接口。
您应该将库的依赖项标记为可选,以便您可以
autoconfigure
更轻松地将模块包含在项目中。如果以这种方式执行,则不提供库,默认情况下,Spring Boot会退出。
Spring Boot使用注释处理器来收集元数据文件(META-INF/spring-autoconfigure-metadata.properties
)中自动配置的条件。如果该文件存在,则用于热切过滤不匹配的自动配置,这将缩短启动时间。建议在包含自动配置的模块中添加以下依赖项:
1 | <dependency> |
对于Gradle 4.5及更早版本,应在compileOnly
配置中声明依赖项,如以下示例所示:
1 | dependencies { |
使用Gradle 4.6及更高版本时,应在annotationProcessor
配置中声明依赖项,如以下示例所示:
1 | dependencies { |
启动器模块
启动器是一个空罐子。它的唯一目的是提供必要的依赖项来使用库。您可以将其视为对入门所需内容的一种看法。
不要对添加启动器的项目做出假设。如果您自动配置的库通常需要其他启动器,请同时提及它们。如果可选依赖项的数量很高,则提供一组适当的默认依赖项可能很难,因为您应该避免包含对典型库的使用不必要的依赖项。换句话说,您不应该包含可选的依赖项。
无论哪种方式,您的启动器必须
spring-boot-starter
直接或间接引用核心Spring Boot启动器()(如果您的启动器依赖于另一个启动器,则无需添加它)。如果只使用自定义启动器创建项目,则Spring Boot的核心功能将通过核心启动器的存在来实现。