SpringBoot基础篇Bean之条件注入@Condition使用姿势
前面几篇关于Bean的基础博文中,主要集中在Bean的定义和使用,但实际的情况中有没有一些场景是不加载我定义的bean,或者只有满足某些前提条件的时候才加载我定义的Bean呢?
本篇博文将主要介绍bean的加载中,条件注解@Conditional
的相关使用
I. @Conditional
注解
这个注解在Spring4中引入,其主要作用就是判断条件是否满足,从而决定是否初始化并向容器注册Bean
1. 定义
@Conditional
注解定义如下,其内部主要就是利用了Condition接口,来判断是否满足条件,从而决定是否需要加载Bean
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
下面是Condtion
接口的定义,这个可以说是最基础的入口了,其他的所有条件注解,归根结底,都是通过实现这个接口进行扩展的
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
这个接口中,有个参数比较有意思ConditionContext
,它持有不少有用的对象,可以用来获取很多系统相关的信息,来丰富条件判断,接口定义如下
public interface ConditionContext {
// 获取Bean定义
BeanDefinitionRegistry getRegistry();
// 获取Bean工程,因此就可以获取容器中的所有bean
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
// environment 持有所有的配置信息
Environment getEnvironment();
// 资源信息
ResourceLoader getResourceLoader();
// 类加载信息
@Nullable
ClassLoader getClassLoader();
}
2. 使用说明
通过一个小例子,简单的说一下如何使用Condition和@Conditional
注解,来实现bean的条件加载
首先我们定义一个随机产生数据的类,其功能就是随机生成一些数据
public class RandDataComponent<T> {
private Supplier<T> rand;
public RandDataComponent(Supplier<T> rand) {
this.rand = rand;
}
public T rand() {
return rand.get();
}
}
我们目前提供两种随机数据生成的bean,但是需要根据配置来选择具体选中的方式,因此我们如下定义Bean
@Configuration
public class ConditionalAutoConfig {
@Bean
@Conditional(RandIntCondition.class)
public RandDataComponent<Integer> randIntComponent() {
return new RandDataComponent<>(() -> {
Random random = new Random();
return random.nextInt(1024);
});
}
@Bean
@Conditional(RandBooleanCondition.class)
public RandDataComponent<Boolean> randBooleanComponent() {
return new RandDataComponent<>(() -> {
Random random = new Random();
return random.nextBoolean();
});
}
}
上面的配置,先不管@Conditional
注解的内容,单看两个Bean的定义,一个是定义int随机数生成;一个是定义boolean随机生成;
但是我们的系统中,只需要一个随机数据生成器即可,我们选择根据配置conditional.rand.type
的值来选择到底用哪个,配置如下
# int 表示选择随机产生int数据; 非int 表示随机产生boolean数据
conditional.rand.type=int
接下来就得看这个条件如何加上了,也就是上面配置类ConditionalAutoConfig
中两个注解的内容了,两个类都是实现Condition
的接口,具体如下
public class RandBooleanCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
return "boolean".equalsIgnoreCase(type);
}
}
public class RandIntCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
return "int".equalsIgnoreCase(type);
}
}
上面的实现也比较清晰,获取配置值,然后判断,并返回true/fase;返回true,则表示这个条件满足,那么这个Bean就可以被加载了;否则这个Bean就不会创建
3. 测试与验证
针对上面的配置与实现,写一个测试类如下
@RestController
@RequestMapping(path = "/conditional")
public class ConditionalRest {
@Autowired
private RandDataComponent randDataComponent;
@GetMapping(path = "/show")
public String show() {
String type = environment.getProperty("conditional.rand.type");
return randDataComponent.rand() + " >>> " + type;
}
}
当配置文件的值为int时,每次访问返回的应该都是正整数,演示如下图
将配置的值改成boolean之后,再次测试如下图