Spring 中的条件注解

1、简介

本文将带你了解 Spring 中的 @Conditional 注解。它用于根据特定条件来控制 Bean 的创建和注册。

2、声明条件

首先来看看在哪些情况下可以使用条件注解。

最常见的用法是包含或排除整个配置类:

@Configuration
@Conditional(IsDevEnvCondition.class)
class DevEnvLoggingConfiguration {
}

或者是一个 Bean:

@Configuration
class DevEnvLoggingConfiguration {
    
    @Bean
    @Conditional(IsDevEnvCondition.class)
    LoggingService loggingService() {
        return new LoggingService();
    }
}

如上,这样就可以根据特定条件(如环境类型或特定需求)来调整应用的行为。在上例中,只为开发环境初始化了额外的 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 配置属性设置为 truelogging.level 设置为 DEBUG 时,Spring 才会创建 LoggingService

可以应用另一个条件来检查是否创建了指定的 Bean。

@Service
@ConditionalOnBean(CustomLoggingConfiguration.class)
class LoggingService {
    // ...
}

或指定类存在于 classpath 中:

@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 {
    // ...
}

注意,对于使用嵌入式 Web 服务器的应用,此条件将返回 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、组合条件

对于更复杂的解决方案,可以使用 ORAND 逻辑运算符对条件注解进行分组。

要使用 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 {
}

在上例中,只有同时满足 IsWindowsConditionJava8Condition 时,才会创建 LoggingService

6、总结

本文介绍了如何使用 Spring 中的条件注解,以及如何自定义自己的条件注解。


Ref:https://www.baeldung.com/spring-conditional-annotations