说明
在 springboot 中一个接口有多个实现,我们希望通过配置来控制运行时
实例化哪个对象,springboot 中 @Conditional 注解可以帮助我们细粒度控制 bean 的实例化。
Spring Boot
在 org.springframework.boot.autoconfigure.condition
包下定义了以下注解:
注解名 |
作用 |
@ConditionalOnJava |
基于JVM版本作为判断条件. |
@ConditionalOnBean |
当容器中有指定的Bean的条件下. |
@ConditionalOnClass |
当类路径下游指定的类的条件下. |
@ConditionalOnExpression |
基于SpEL表达式作为判断条件. |
@ConditionalOnJndi |
在JNDI存在的条件下查找指定的位置. |
@ConditionalOnMissingBean |
当容器中没有指定Bean的情况下. |
@ConditionalOnMissingClass |
当类路径下没有指定的类的情况下. |
@ConditionalOnNotWebApplication |
当前项目不是web项目的条件下. |
@ConditionalOnProperty |
指定的属性是否有指定的值. |
@ConditionalOnResource |
类路径是否有指定的值. |
@ConditionalOnSingleCandidate |
当指定Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean. |
@ConditionalOnWebApplication |
当前项目是web项目的条件下. |
@ConditionalOnExpression
@Component
@ConditionalOnExpression("#{'user2'.equals(environment['user.label'])}")
public class TestBean {
@PostConstruct
public void init() {
System.out.println(111);
}
}
@ConditionalOnProperty
@Component
@ConditionalOnProperty(name = "user.label", havingValue = "user2")
public class TestBean {
@PostConstruct
public void init(){
System.out.println(111);
}
}
自定义 Conditional
Bean对象
@Data
@NoArgsConstructor
public class User1 {
private String name;
private int age;
}
@Data
@NoArgsConstructor
public class User2 {
private String name;
private int age;
}
方法一
- 为每类对象创建一个 Conditional,如下为 User1和User2分别创建一个
public class MyConditional1 implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String v = context.getEnvironment().getProperty("user.label");
if (v.equals("user1")) return true;
return false;
}
}
public class MyConditional2 implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String v = context.getEnvironment().getProperty("user.label");
if (v.equals("user2")) return true;
return false;
}
}
@Bean
@Conditional(MyConditional1.class)
public User init(){
System.out.println(111);
return new User();
}
@Bean
@Conditional(MyConditional2.class)
public User2 init2(){
System.out.println(222);
return new User2();
}
user.label=user1
方法二
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(MyConditional.class)
public @interface MyConditionalAnnotation {
String label();
}
public class MyConditional extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(MyConditionalAnnotation.class.getName());
Object value = annotationAttributes.get("label");
if (value == null) {
return ConditionOutcome.noMatch("ERROR");
}
String v = context.getEnvironment().getProperty("user.label");
if (v.equals(value)){
return ConditionOutcome.match("OK");
}
return ConditionOutcome.noMatch("ERROR");
}
}
注意:代码中通过 application.propeties
传入配置优先级比较高,所以通过context可以获取到,如果通过别的配置文件可能无法获取则需要手动加载。
Properties properties = new Properties();
try {
properties.load(conditionContext.getResourceLoader().getResource("test.properties").getInputStream());
} catch (IOException ex) {
ex.printStackTrace();
}
String v = properties.getProperty("user.label");
@MyConditionalAnnotation(label = "MyUserLabel1")
@Bean
public User1 init(){
System.out.println(111);
return new User1();
}
@MyConditionalAnnotation(label = "MyUserLabel2")
@Bean
public User2 init2(){
System.out.println(222);
return new User2();
}
user.label=MyUserLabel2