存在这样一个需求,引入了xx类后,就创建A类型bean对象,否则创建B类型bean对象。
我们先来做一个简单的实现
static class MyCondition1 implements Condition { // 存在 Druid 依赖
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
}
}
static class MyCondition2 implements Condition { // 不存在 Druid 依赖
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return !ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
}
}
@Configuration // 第三方的配置类
@Conditional(MyCondition1.class)
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration // 第三方的配置类
@Conditional(MyCondition2.class)
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
在上述实现中,MyCondition1,MyCondition2实现了Condition接口的matches方法,并作为参数传递给@Conditional注解,当系统中引入了"com.alibaba.druid.pool.DruidDataSource"类后,MyCondition1的判断为true,AutoConfiguration1被加载,bean1被创建。
上述实现符合我们的需求,但它有两个明显的问题:
(1)直接固定了xx类的名字为"com.alibaba.druid.pool.DruidDataSource"
(2)MyCondition1,MyCondition2的条件很像,却写了两个类。
针对这两个问题,我们做下改造。
首先我们在MyCondition中加入判断条件exists,使得MyCondition能兼顾MyCondition1和MyCondition2的功能,
className参数使得MyCondition可以支持对不同的类进行判断。
static class MyCondition implements Condition { // 存在 Druid 依赖
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
String className = attributes.get("className").toString();
boolean exists = (boolean) attributes.get("exists");
boolean present = ClassUtils.isPresent(className, null);
return exists ? present : !present;
}
}
由于@Conditional只支持一个参数,所以我们要实现新的注解来支持exists参数。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Conditional(MyCondition.class)
@interface ConditionalOnClass {
boolean exists(); // true 判断存在 false 判断不存在
String className(); // 要判断的类名
}
在ConditionalOnClass注解中,支持传入className和exists参数。
这两个参数最终会被MyCondition的match方法读取到,从而实现根据类名进行判断的功能。
改造的使用示例为:
@Configuration // 第三方的配置类
@ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = false)
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration // 第三方的配置类
@ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = true)
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}