Spring中的@conditional注解
今天主要从以下几方面来介绍一下@Conditional注解
-
@Conditional注解是什么
-
@Conditional注解怎么使用
1,@Conditional注解是什么
@Conditional注解是可以根据一些自定义的条件动态的选择是否加载该bean到springIOC容器中去,如果看过springBoot源码的同学会发现,springBoot中大量使用了该注解
2,@Conditional注解怎么使用
查看@Conditional源码你会发现它既可以作用在方法上,同时也可以作用在类上,源码如下:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { /** * All {@link Condition}s that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); }
设置给@conditional的类需要实现Condition接口
我们看一下Condition的源码:
public interface Condition { boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2); }
ConditionContext源码:
public interface ConditionContext { BeanDefinitionRegistry getRegistry(); ConfigurableListableBeanFactory getBeanFactory(); Environment getEnvironment(); ResourceLoader getResourceLoader(); ClassLoader getClassLoader(); }
AnnotatedTypeMetadata源码: 此类能够让我们检查带有@Bean注解的方法上还有其他什么注解
public interface AnnotatedTypeMetadata {
boolean isAnnotated(String var1);
Map<String, Object> getAnnotationAttributes(String var1);
Map<String, Object> getAnnotationAttributes(String var1, boolean var2);
MultiValueMap<String, Object> getAllAnnotationAttributes(String var1);
MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2);
}
a,@Conditional作用在方法上
定义一个Condition如下:
/** * 定义一个bean的Condition * * @author zhangqh * @date 2018年5月1日 */ public class MyCondition implements Condition { public boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); String system = env.getProperty("os.name"); System.out.println("系统环境为 ==="+system); // 系统环境在Windows才加载该bean到容器中 if(system.contains("Windows")){ return true; } return false; } }
定义一个bean加上@Conditional注解如下:
@Conditional({MyCondition.class}) @Bean(value="user1") public User getUser1(){ System.out.println("创建user1实例"); return new User("李四",26); }
测试如下:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class); String[] beanNames = applicationContext2.getBeanDefinitionNames(); for(int i=0;i<beanNames.length;i++){ System.out.println("bean名称为==="+beanNames[i]); }
运行结果:
bean名称为===mainConfig
bean名称为===user0
bean名称为===user1
我这边电脑系统是window所以user1实例是有创建出来的,如果把MyCondition中的判断改成if(system.contains("linux"))那么user1是不会加载到spring容器中的
b,@Conditional作用在类上
修改注解配置如下:
/** * 定义一个注解配置文件 必须要加上@Configuration注解 * * @author zhangqh * @date 2018年4月30日 */ @Conditional({MyCondition.class}) @Configuration public class MainConfig { /** * 定义一个bean对象 * @return */ @Scope @Lazy @Bean(value="user0") public User getUser(){ System.out.println("创建user实例"); return new User("张三",26); } //@Conditional({MyCondition.class}) @Bean(value="user1") public User getUser1(){ System.out.println("创建user1实例"); return new User("李四",26); } }
运行测试:
bean名称为===mainConfig bean名称为===user0 bean名称为===user1
MainConfig中的bean都成功打印出来了,因为我MyCondition条件返回的是true,同样如果我修改成if(system.contains("linux"))那么MainConfig的bean就都不会实例化了
c, @profile注解分析
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional({ProfileCondition.class}) public @interface Profile { String[] value(); }
注意:@Profile本身也使用了@Condition注解,并且引用ProfileCondition作为Condition的实现。
以下为ProfileCondition的实现
class ProfileCondition implements Condition { ProfileCondition() { } public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { if (context.getEnvironment() != null) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { Iterator var4 = ((List)attrs.get("value")).iterator(); Object value; do { if (!var4.hasNext()) { return false; } value = var4.next(); } while(!context.getEnvironment().acceptsProfiles((String[])((String[])value))); return true; } } return true; } }
你所看得到的天才不过是在你看不到的时候还在努力罢了!