@Conditional介绍
作用
@Conditional是Spring4新提供的注解,能够根据一定的条件进行判断,满足条件就给容器注入bean。
@Conditional定义
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
从代码中可以看到,需要传入一个Class数组,并且需要继承Condition接口:
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2)
}
Condition是一个接口,返回true就注入bean,false则不注入。
@Conditional 使用示例
public class User {
private String name;
private String age;
public User(String name, String age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" + "name=" + name + ", age=" + age + "}";
}
// getter/setter方法省略
}
编写配置类BeanConfig,注入两个User实例
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public User user1() {
return new User("Bill Gates", 62);
}
@Bean(name = "linus")
public User user2() {
return new User("Linus", 48);
}
}
编写测试类,查看User实例是否注入
public class ConditionalTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
@Test
public void test1() {
Map<String, User> map = applicationContext.getBeansByType(User.class);
System.out.println(map);
}
}
输出结果如下:
{bill=User{name=Bill Gates, age=62}, linus=User{name=Linus, age=48}}
这时候问题来了,如果我们想根据当前操作系统来注入User实例(windows系统下注入bill, linux系统下注入linus),那么该怎么做呢?
这就需要我们用到@Conditional注解了。
首先我们先继承Condition接口,自定义判断条件
public class WindowsCondition implements Condition {
/**
* @param conditionContext:判断条件能使用的上下文环境
* @param annotatedTypeMetadata:注解所在位置的注解信息
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//获取bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//获取当前系统名
String property = environment.getProperty("os.name");
//如果包含 Windows则说明是windows系统,返回true,否则返回false
if(property.contains("Windows")) {
return true;
}
return false;
}
}
值得一提的是 conditionContext方法提供了多种方法,方便获取各种信息,也是SpringBoot中@ConditionalOnXXX注解多样扩展的基础。
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Linux")) {
return true;
}
return false;
}
}
将上面配置的Condition子类传递给@Conditonal注解
@Configuration
public class BeanConfig {
//只有一个类时,大括号可以省略
@Conditional({WindowsCondition.class})
@Bean(name = "bill")
public User user1() {
return new User("Bill Gates", 62);
}
@Conditional({LinuxCondition.class})
@Bean(name = "linus")
public User user2() {
return new User("Linus", 48);
}
}
测试
public class ConditionalTest {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
@Test
public void test1() {
String osname = applicationContext.getEnvironment().getProperty("os.name");
System.out.println("当前系统为" + osname);
Map<String, User> map = applicationContext.getBeansOfType(User.class);
System.out.println(map);
}
}
运行结果:
当前系统为Windows 10
{bill=User{name='Bill Gates', age=62}}
@Conditional标注在类上
我们从@Conditional注解的定义可以看出,@Conditional注解不仅可以标注在方法上,也是可以标注在类上的。接下来就来看看标注在类上的效果:
@Conditional({WindowsCondition.class})
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public User user1() {
return new User("Bill Gates", 62);
}
@Bean(name = "linus")
public User user2() {
return new User("Linus", 48);
}
}
我们可以猜到,上面代码的效果就是将两个Bean都注入到了IoC容器。
传入多个Condition条件类
上面讲到,@Conditional注解传入的是一个数组。那么,当我们传入多个条件类的时候,会产生什么效果呢?
/**
* 始终返回false
*/
public class NegativeCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return false;
}
}
@Conditional({WindowsCondition.class, NegativeCondition.class})
@Configuration
public class BeanConfig {
@Bean(name = "bill")
public User user1() {
return new User("Bill Gates", 62);
}
@Bean(name = "linus")
public User user2() {
return new User("Linus", 48);
}
}
此时结果为两个bean都没有被注入。
原因是: WindowsCondition判断条件为true,但是NegativeCondition判断条件为false。即当存在多个Condition类时,需要同时满足才会注入bean。