一、@Conditional 注解简介
@Conditional 注解是 Spring4 新提供的注解,它的作用是按照一定的条件进行判断,如果满足条件给 IOC 容器注入相应的 bean
@Conditional 注解源码如下:
// 该注解可以标注在 类、接口、枚举声明、方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
// @Conditional 注解只有一个属性值 value ,它的类型是一个 Class[]
// 该数组中的元素是 Condition 的一个子类
Class<? extends Condition>[] value();
}
我们点开 Condition 进去看一下它到底是什么
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
点开之后发现它是一个接口,并且有一个 matches(...) 的方法,该方法的返回值如果是 true,则判断条件成立,反之,则条件不成立.这样就很明确了,原来 @Conditional() 注解中的属性值是一个 Class 类型的数组,并且该数组中的元素就是 Condition 的实现类
类似于这样 @Conditional({A.class,B.class,C.class}) (注: 这里的类 A、B、C 是 Condition 的实现类)
二、现有需求:通过操作系统作为条件来筛选注册对象
1、编写自定义类 WindowsCondition 实现 Condition 接口
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取环境信息
Environment environment = context.getEnvironment();
// 获取操作系统信息
String property = environment.getProperty("os.name");
// 如果操作系统信息中包含 Windows ,那么返回 true,也就是判断条件成立
if(property.contains("Windows")){
return true;
}
return false;
}
}
2、编写自定义类 LinuxCondition 实现 Condition 接口
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Linux")){
return true;
}
return false;
}
}
3、配置类
@Configuration
public class SpringConfiguration {
@Bean("bill")
// 如果只有一个参数,那么 {} 可以省略
@Conditional({WindowsCondition.class})
public Person person01() {
return new Person("Bill Gates", 66);
}
@Bean("linus")
@Conditional(LinuxCondition.class)
public Person person02() {
return new Person("Linus Torvalds", 44);
}
}
4、测试类
public class SpringTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfiguration.class);
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
5、测试结果
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
// 配置类
springConfiguration
// 由于本机操作系统是 Windows8.1 ,可以看到 linus 没有注册进 Spring 容器
bill
6、更换一下虚拟机参数,切换为 Linux 环境,看一下测试结果
Run---->Edit Configurations----> -Dos.name=Linux
再次启动项目测试结果如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfiguration
// linus 这个 bean 就注入了 Spring 容器中,而 bill 就没有注入进来
linus
三、@Conditional 注解标注位置
通过上面的例子我们可以看到,我们都是将 @Conditional 注解标注在了方法上面,但是我们查看源码的时候可以知道,该注解不但可以标注在方法上,还可以标注在类、接口、枚举声明上面,那么标注在其它地方的时候代表的意思又是什么呢?
1、标注在方法上:由于一个方法只能注入一个 bean 实例,所以 @Conditional 注解标注在方法上只能控制一个 bean 实例是否注入
2、标注在类上:一个类上可以注入很多的 bean 实例,所以 @Conditional 注解标注在类上就能控制一批 bean 实例是否注入
自己测试了一下,结论如下:
@Conditional({A.class,B.class,C.class,D.class...})
@Configuration
public class SpringConfiguration {
......
}
A、B、C、D、....都是 Condition 接口的实现类,如果它们中有一个类的 matches(...) 方法返回值为 false,那么该配置类所有的 bean 都不会注入到 spring 容器中,只有所有类的 matches(...) 方法返回值为 true,那么该配置类才生效
个人猜测,标注在类上的 @Conditional 注解的判断条件为 false,那么整个类所有执行的行为将失效.(我们这里是注入容器的行为,这个以后看了源码再来验证.)