做用
@Conditional是Spring4新提供的注解,它的做用是按照必定的条件进行判断,知足条件的才给容器注册Bean。java
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); }
咱们点进去看后,发现它是一个接口,有一个方法。linux
@FunctionalInterface public interface Condition { boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2); }
它持有很多有用的对象,能够用来获取不少系统相关的信息,来丰富条件判断,接口定义以下ios
public interface ConditionContext { /** * 获取Bean定义 */ BeanDefinitionRegistry getRegistry(); /** * 获取Bean工程,所以就能够获取容器中的全部bean */ @Nullable ConfigurableListableBeanFactory getBeanFactory(); /** * environment 持有全部的配置信息 */ Environment getEnvironment(); /** * 资源信息 */ ResourceLoader getResourceLoader(); /** * 类加载信息 */ @Nullable ClassLoader getClassLoader(); }
需求
根据当前系统环境的的不一样实例不一样的Bean,好比如今是Mac
那就实例一个Bean,若是是Window
系统实例另外一个Bean。git
首先建立一个Bean类github
@Data @AllArgsConstructor @NoArgsConstructor @ToString public class SystemBean { /** * 系统名称 */ private String systemName; /** * 系统code */ private String systemCode; }
@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"); } /** * 若是LinuxCondition的实现方法返回true,则注入这个bean */ @Bean("mac") @Conditional({MacCondition.class}) public SystemBean systemMac() { log.info("ConditionalConfig方法注入 mac实体"); return new SystemBean("Mac ios系统","001"); } }
这两个类都实现了Condition接口, 只有matches方法返回true才会实例化当前Bean
。spring
1)WindowsConditionwindows
@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) MacConditionspringboot
@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; } }
/** * @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); } } }
运行结果ide
经过运行结果能够看出测试
一、虽然配置两个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,因此采用了报错的策略。
GitHub源码
https://github.com/yudiandemingzi/SpringBootBlog
项目名称 03-conditional
只要本身变优秀了,其余的事情才会跟着好起来(中将3)