@Conditional
是Spring4
新提供的注解,它的作用是按照一定条件进行判断,满足条件就将bean注册到容器。
Contidional 介绍
Conditional
是由SpringFramework
提供的一个注解,位于 org.springframework.context.annotation
包内,定义如下。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
Class<? extends Condition>[] value();
}
我们可以将 Spring
的@Conditional
注解用于以下场景:
- 可以作为类级别的注解直接或者间接的与
@Component
相关联,包括@Configuration类
; - 可以作为元注解,用于自动编写构造性注解;
- 作为方法级别的注解,作用在任何@Bean方法上。
Condition 接口
Condition 将在即将注册 Bean 定义之前进行检查,并且可以根据它的 matches()
方法返回值动态决定是否注册组件(true-允许注册;false-不允许注册)。
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
注意:Condition 必须遵循与 BeanFactoryPostProcessor 相同的限制,并注意永远不要与 bean 实例交互。对于与 @Configuration bean 交互的条件的更细粒度控制,请考虑实现 ConfigurationCondition 接口。
Contidional 申明
- 某个
@Configuration
注解的配置类上 - 某个
@Configuration
注解配置类中带有@Bean
注解的方法上 - 任何用
@Component
、@Service
、@Repository
或@Controller
注释声明的 Bean
@Configuration
@Conditional(IsDevEnvCondition.class)
class DevEnvLoggingConfiguration {
// ...
}
@Configuration
class DevEnvLoggingConfiguration {
@Bean
@Conditional(IsDevEnvCondition.class)
LoggingService loggingService() {
return new LoggingService();
}
}
@Service
@Conditional(IsDevEnvCondition.class)
class LoggingService {
// ...
}
Contidional 实战
自定义条件
检查 Java 版本是否为 8
class Java8Condition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return JavaVersion.getJavaVersion().equals(JavaVersion.EIGHT);
}
}
将 Java8Condition
作为 @Conditional
注释中的一个属性:
@Service
@Conditional(Java8Condition.class)
public class Java8DependedService {
// ...
}
常见的条件注解
ConditionalOnProperty
value
属性,指定要检查的配置属性;haveValue
属性,定义这个条件所需的值;matchIfMissing
属性,如果缺少参数,是否应该匹配条件。
@Service
@ConditionalOnProperty(
value="logging.enabled",
havingValue = "true",
matchIfMissing = true)
class LoggingService {
// ...
}
ConditionalOnExpression
相比较前面的Bean,Class是否存在,配置参数是否存在或者有某个值而言,这个依赖SPEL
表达式的,就显得更加的高级了;
其主要就是执行Spel
表达式,根据返回的true
/false
来判断是否满足条件
@Service
@ConditionalOnExpression(
"${logging.enabled:true} and '${logging.level}'.equals('DEBUG')"
)
class LoggingService {
// ...
}
ConditionalOnBean
只有在容器中注册了某个bean,该条件才起作用
@Service
@ConditionalOnBean(CustomLoggingConfiguration.class)
class LoggingService {
// ...
}
Contidional 进阶
多重注解条件
此处demo实现满足某个环境变量时该条件才能生效
创建注解类 Env
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Env {
String[] value();
}
创建 Condition 实现
public class EnvCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
// 查找@EnvCondition作用的方法或者类上的@Env注解
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(Env.class.getName());
if (annotationAttributes != null) {
String[] envList = (String[]) annotationAttributes.get("value");
// 获取配置的环境变量,通过Java启动参数(vm options)配置 -Denv=dev
String env = context.getEnvironment().getProperty("env");
return Arrays.stream(envList).anyMatch(r -> Objects.equals(r, env));
}
return false;
}
}
自定义条件使用
@Configuration
public class DemoConfiguration{
@Env("dev")
@EnvCondition
public EnvBean envBean(){
// ...
}
@Env("prod")
@EnvCondition
public EnvBean prodBean(){
// ...
}
}
写在最后
码字不易,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激