作用:
@Conditional
是 Spring4 新提供的注解,它的作用是按照一定的条件进行判断,满足条件的才给容器注册Bean。
一、概述
1、@Conditional 注解定义
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
2、Condition
我们点进去看后,发现它是一个接口,有一个方法。
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
3、ConditionContext
它持有不少有用的对象,可以用来获取很多系统相关的信息,来丰富条件判断,接口定义如下
public interface ConditionContext {
/**
* 获取Bean定义
*/
BeanDefinitionRegistry getRegistry();
/**
* 获取Bean工程,因此就可以获取容器中的所有bean
*/
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
/**
* environment 持有所有的配置信息
*/
Environment getEnvironment();
/**
* 资源信息
*/
ResourceLoader getResourceLoader();
/**
* 类加载信息
*/
@Nullable
ClassLoader getClassLoader();
}
二、案例
需求
根据当前系统环境的的不同实例不同的 Bean,比如现在是Mac
那就实例一个 Bean,如果是Window
系统实例另一个 Bean。
1、SystemBean
首先创建一个Bean类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class SystemBean {
/**
* 系统名称
*/
private String systemName;
/**
* 系统code
*/
private String systemCode;
}
2、通过Configuration配置实例化 Bean
@Slf4j
@Configuration
public class ConditionalConfig {
/**
* 如果 WindowsCondition 的实现方法返回 true,则注入这个 bean
*/
@Bean("windows")
@Conditional({WindowsCondition.class})
public SystemBean systemWi() {
log.info("ConditionalConfig方法注入 windows实体");
return new SystemBean("windows系统","002");
}
/**
* 如果 MacCondition 的实现方法返回 true,则注入这个 bean
*/
@Bean("mac")
@Conditional({MacCondition.class})
public SystemBean systemMac() {
log.info("ConditionalConfig方法注入 mac实体");
return new SystemBean("Mac ios系统","001");
}
}
3、WindowsCondition 和 MacCondition
这两个类都实现了 Condition
接口, 只有 matches 方法返回 true 才会实例化当前 Bean。
1)WindowsCondition**
@Slf4j
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
if (property.contains("Windows")){
log.info("当前操作系统是:Windows");
return true;
}
return false;
}
}
2) MacCondition**
@Slf4j
public class MacCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Mac")) {
log.info("当前操作系统是:Mac OS X");
return true;
}
return false;
}
}
4、测试类测试
/**
* @author xub
* @date 2019/6/13 下午10:42
*/
@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class TestConditionOn {
@Autowired
private SystemBean windows;
@Autowired
private SystemBean mac;
@Test
public void test() {
if (windows != null) {
System.out.println("windows = " + windows);
}
if (mac != null) {
System.out.println("linux = " + mac);
}
}
}
运行结果**
通过运行结果可以看出
- 虽然配置两个 Bean,但这里只实例化了一个 Bean,因为我这边是Mac电脑,所以实例化的是 mac 的 SystemBean
- 注意一点,我们可以看出
window
并不为 null,而是mac 实例化的 Bean。说明只要实例化一个 Bean 的,不管你命名什么,都可以注入这个Bean。
修改一下**
这里做一个修改,我们把ConditionalConfig
中的这行代码注释掉。
// @Conditional({WindowsCondition.class})
再运行下代码
通过运行结果可以看出,配置类的两个 Bean 都已经注入成功了。
注意
当同一个对象被注入两次及以上的时候,那么你在使用当前对象的时候,名称一定要是两个 bean 名称的一个,否则报错。比如修改为
@Autowired
private SystemBean windows;
@Autowired
private SystemBean mac;
@Autowired
private SystemBean linux;
在启动发现,报错了。
意思很明显,就是上面只实例化成功一个 SystemBean 的时候,你取任何名字,反正就是把当前已经实例化的对象注入给你就好了。
但是你现在同时注入了两个 SystemBean,你这个时候有个名称为 linux,它不知道应该注入那个 Bean,所以采用了报错的策略。<br />
GitHub源码
https://github.com/yudiandemingzi/spring-boot-study 项目名称 03-conditional