一、简介
在本教程中,我们将了解 @Conditional 注释。它用于根据定义的条件指示给定组件是否有资格注册。
我们将学习如何使用预定义的条件注释,将它们与不同的条件结合起来,并创建我们自己的自定义的、基于条件的注释。
2. 声明条件
在我们开始实现之前,让我们看看在哪些情况下可以使用条件注释。
最常见的用法是 包含或排除整个配置类 :
@Configuration
@Conditional(IsDevEnvCondition.class)
class DevEnvLoggingConfiguration {
// ...
}
或者只是一个 bean:
@Configuration
class DevEnvLoggingConfiguration {
@Bean
@Conditional(IsDevEnvCondition.class)
LoggingService loggingService() {
return new LoggingService();
}
}
通过这样做,我们可以根据给定的条件(例如环境类型或客户的特定需求)来确定应用程序的行为。在上面的示例中,我们仅为开发环境初始化了额外的日志服务。
使组件成为条件的另一种方法是将条件直接放在组件类上:
@Service
@Conditional(IsDevEnvCondition.class)
class LoggingService {
// ...
}
我们可以将上面的示例应用于使用 @Component 、 @Service 、 @Repository 或 @Controller 注释声明的任何 bean。
3. 预定义条件注释
Spring 附带了一组预定义的条件注释。让我们来看看一些最受欢迎的。
首先,让我们看看如何 使组件基于配置属性值:
@Service
@ConditionalOnProperty(
value="logging.enabled",
havingValue = "true",
matchIfMissing = true)
class LoggingService {
// ...
}
第一个属性 value 告诉我们将要查看的配置属性。第二个是 havingValue, 定义此条件所需的值。最后, matchIfMissing 属性告诉 Spring 如果参数丢失,是否应该匹配条件。
同样,我们可以 将条件基于表达式 :
@Service
@ConditionalOnExpression(
"${logging.enabled:true} and '${logging.level}'.equals('DEBUG')"
)
class LoggingService {
// ...
}
现在,仅当 logging.enabled 配置属性设置为 true 并且 logging.level 设置为 DEBUG 时,Spring才会创建 LoggingService 。
我们可以应用的另一个条件是检查是否创建了给定的 bean:
@Service
@ConditionalOnBean(CustomLoggingConfiguration.class)
class LoggingService {
// ...
}
或者类路径中存在给定的类:
@Service
@ConditionalOnClass(CustomLogger.class)
class LoggingService {
// ...
}
我们可以通过应用 @ConditionalOnMissingBean 或 @ConditionalOnMissingClass 注释来实现相反的行为。
此外,我们可以 将我们的组件依赖于给定的 Java 版本 :
@Service
@ConditionalOnJava(JavaVersion.EIGHT)
class LoggingService {
// ...
}
在上面的示例中,仅当运行环境为Java 8时才会创建 LoggingService 。
最后,我们可以使用 @ConditionalOnWarDeployment 注解来仅在war打包中启用bean:
@Configuration
@ConditionalOnWarDeployment
class AdditionalWebConfiguration {
// ...
}
请注意,对于具有嵌入式服务器的应用程序,此条件将返回 false。
4. 定义自定义条件
Spring 允许我们通过 创建自定义条件模板 来自定义 @Conditional 注释的行为。要创建一个,我们只需实现 Condition 接口:
class Java8Condition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return JavaVersion.getJavaVersion().equals(JavaVersion.EIGHT);
}
}
matches 方法告诉 Spring 条件是否已通过。它有两个参数,为我们提供有关 bean 将初始化的上下文的信息,以及所使用的 @Conditional 注释的元数据。
正如我们在示例中看到的,我们只检查 Java 版本是否为 8。
之后,我们应该将新条件作为属性放置在 @Conditional 注释中:
@Service
@Conditional(Java8Condition.class)
public class Java8DependedService {
// ...
}
这样,仅当 Java8Condition 类中的条件匹配时才会创建 Java8DependentService 。
5. 组合条件
对于更复杂的解决方案,我们可以 使用 OR 或 AND 逻辑运算符对条件注释进行分组 。
要应用 OR 运算符,我们需要创建一个扩展 AnyNestedCondition 类的自定义条件。在其中,我们需要为每个条件创建一个空的 静态 类,并使用适当的 @Conditional 实现对其进行注释。
例如,让我们创建一个需要 Java 8 或 Java 9 的条件:
class Java8OrJava9 extends AnyNestedCondition {
Java8OrJava9() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Conditional(Java8Condition.class)
static class Java8 { }
@Conditional(Java9Condition.class)
static class Java9 { }
}
另一方面,AND 运算符要简单得多。我们可以简单地将条件分组:
@Service
@Conditional({IsWindowsCondition.class, Java8Condition.class})
@ConditionalOnJava(JavaVersion.EIGHT)
public class LoggingService {
// ...
}
在上面的示例中,仅当 IsWindowsCondition 和 Java8Condition 都匹配时才会创建 LoggingService 。
六、总结
在本文中,我们学习了如何使用和创建条件注释。与往常一样,所有源代码都可以在 GitHub 上获取。