49.3条件Annotations
您几乎总是希望在自动配置类中包含一个或多个 @Conditional 注释。@ConditionalOnMissingBean 注释是一个常见示例,用于允许开发人员
在您的默认值不满意时覆盖自动配置。
Spring Boot包含许多 @Conditional 注释,您可以通过注释 @Configuration 类或单独的 @Bean 方法在您自己的代码中重用这些注释。这些注
释包括:
第49.3.1节“类别条件”
第49.3.2节,“Bean条件”
第49.3.3节,“Property条件”
第49.3.4节“资源条件”
第49.3.5节“Web应用程序条件”
第49.3.6节“SpEL表达条件”
@ConditionalOnClass 和 @ConditionalOnMissingClass 注释允许根据特定类的存在与否来包含配置。由于使用ASM解析注释元数据这一事
实,您可以使用 value 属性来引用真实类,即使该类实际上可能不会出现在正在运行的应用程序类路径中。如果您希望使用 String 值指定类
名,也可以使用 name 属性。
如果您使用 @ConditionalOnClass 或 @ConditionalOnMissingClass 作为元注释的一部分来编写自己的组合注释,则必须使
用 name 作为引用该类的情况,在这种情况下不会处理。
49.3.2 Bean条件
@ConditionalOnBean 和 @ConditionalOnMissingBean 注释允许根据特定beans的存在与否来包括bean。您可以使用 value 属性按类型指定
beans,或使用 name 按名称指定beans。search 属性允许您限制搜索beans时应考虑的 ApplicationContext 层次结构。
置于 @Bean 方法时,目标类型默认为方法的返回类型,如以下示例所示:
@Configuration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService() { ... }
}
在前面的示例中,如果 ApplicationContext 中未包含类型 MyService 的bean,则将创建 myService bean。
您需要非常小心添加bean定义的顺序,因为这些条件是根据到目前为止已处理的内容进行评估的。因此,我们建议仅对自动配置
类使用 @ConditionalOnBean 和 @ConditionalOnMissingBean 注释(因为这些注释保证在添加任何用户定义的bean定义后加
载)。
@ConditionalOnBean 和 @ConditionalOnMissingBean 不会阻止创建 @Configuration 类。在类级别使用这些条件和使用注释
标记每个包含 @Bean 方法的唯一区别是,如果条件不匹配,前者会阻止将 @Configuration 类注册为bean。
49.3.3 Property条件
@ConditionalOnProperty 注释允许基于Spring Environment属性包含配置。使用 prefix 和 name 属性指定应检查的属性。默认情况下,匹
配存在且不等于 false 的任何属性。您还可以使用 havingValue 和 matchIfMissing 属性创建更高级的检查。
49.3.4资源条件
@ConditionalOnResource 注释仅在存在特定资源时才允许配置。可以使用通常的Spring约定来指定资源,如以下示例所
示:file:/home/user/test.dat 。
49.3.5 Web应用程序条件
@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication 注释允许配置,具体取决于应用程序是否为“Web应用程序”。
Web应用程序是使用Spring WebApplicationContext ,定义 session 范围或具有 StandardServletEnvironment 的任何应用程序。
49.3.6 SpEL表达条件
@ConditionalOnExpression 注释允许根据SpEL表达式的结果包含配置。
49.4测试自动配置
自动配置可能受许多因素的影响:用户配置( @Bean 定义和 Environment 自定义),条件评估(存在特定库)等。具体而言,每个测试都应创
建一个定义良好的 ApplicationContext ,它代表这些自定义的组合。ApplicationContextRunner 提供了实现这一目标的好方法。
ApplicationContextRunner 通常被定义为测试类的一个字段,用于收集基本的通用配置。以下示例确保始终调
用 UserServiceAutoConfiguration :
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(UserServiceAutoConfiguration.class));
如果必须定义多个自动配置,则无需按照与运行应用程序时完全相同的顺序调用它们的声明。
每个测试都可以使用运行器来表示特定的用例。例如,下面的示例调用用户配置( UserConfiguration )并检查自动配置是否正确退回。调
用 run 提供了一个可以与 Assert4J 一起使用的回调上下文。
@Test
public void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context.getBean(UserService.class)).isSameAs(
context.getBean(UserConfiguration.class).myUserService());
});
}
@Configuration
static class UserConfiguration {
@Bean
public UserService myUserService() {
return new UserService("mine");
}
}
也可以轻松自定义 Environment ,如以下示例所示:
@Test
public void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context.getBean(UserService.class).getName()).isEqualTo("test123");
});
}
跑步者也可以用来显示 ConditionEvaluationReport 。该报告可以 INFO 或 DEBUG 级别打印。以下示例显示如何使
用 ConditionEvaluationReportLoggingListener 在自动配置测试中打印报表。
@Test
public void autoConfigTest {
ConditionEvaluationReportLoggingListener initializer = new ConditionEvaluationReportLoggingListener(
LogLevel.INFO);
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withInitializer(initializer).run((context) -> {
// Do something...
});
}
49.4.1模拟Web上下文
如果需要测试仅在Servlet或Reactive Web应用程序上下文中运行的自动配置,请分别使用 WebApplicationContextRunner
或 ReactiveWebApplicationContextRunner 。
49.4.2覆盖Classpath
还可以测试在运行时不存在特定类和/或包时发生的情况。Spring Boot装有 FilteredClassLoader ,可以很容易地被跑步者使用。在以下示例
中,我们声明如果不存在 UserService ,则会正确禁用自动配置:
@Test
public void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(UserService.class))
.run((context) -> assertThat(context).doesNotHaveBean("userService"));
}
49.5创建自己的初学者
库的完整Spring Boot启动器可能包含以下组件:
包含自动配置代码的 autoconfigure 模块。
starter 模块,它提供对 autoconfigure 模块以及库的依赖关系以及通常有用的任何其他依赖项。简而言之,添加启动器应该提供开始使
用该库所需的一切。
如果您不需要将这两个问题分开,则可以将自动配置代码和依赖关系管理组合在一个模块中。