前面几篇关于Bean的基础博文中,主要集中在Bean的定义和使用,但实际的状况中有没有一些场景是不加载我定义的bean,或者只有知足某些前提条件的时候才加载我定义的Bean呢?java
本篇博文将主要介绍bean的加载中,条件注解@Conditional
的相关使用git
@Conditional
注解这个注解在Spring4中引入,其主要做用就是判断条件是否知足,从而决定是否初始化并向容器注册Beangithub
@Conditional
注解定义以下,其内部主要就是利用了Condition接口,来判断是否知足条件,从而决定是否须要加载Beanweb
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
复制代码
下面是Condtion
接口的定义,这个能够说是最基础的入口了,其余的全部条件注解,归根结底,都是经过实现这个接口进行扩展的spring
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
复制代码
这个接口中,有个参数比较有意思ConditionContext
,它持有很多有用的对象,能够用来获取不少系统相关的信息,来丰富条件判断,接口定义以下app
public interface ConditionContext {
// 获取Bean定义
BeanDefinitionRegistry getRegistry();
// 获取Bean工程,所以就能够获取容器中的全部bean
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
// environment 持有全部的配置信息
Environment getEnvironment();
// 资源信息
ResourceLoader getResourceLoader();
// 类加载信息
@Nullable
ClassLoader getClassLoader();
}
复制代码
经过一个小例子,简单的说一下如何使用Condition和@Conditional
注解,来实现bean的条件加载框架
首先咱们定义一个随机产生数据的类,其功能就是随机生成一些数据dom
public class RandDataComponent<T> {
private Supplier<T> rand;
public RandDataComponent(Supplier<T> rand) {
this.rand = rand;
}
public T rand() {
return rand.get();
}
}
复制代码
咱们目前提供两种随机数据生成的bean,可是须要根据配置来选择具体选中的方式,所以咱们以下定义Beanide
@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随机生成;spring-boot
可是咱们的系统中,只须要一个随机数据生成器便可,咱们选择根据配置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就不会建立
针对上面的配置与实现,写一个测试类以下
@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以后,再次测试以下图
上面的测试演示了经过配置文件选择注入Bean的状况,若是一个Bean是经过自动扫描加载的,是否能够直接在Bean的类上添加注解来决定是否载入呢?
从使用来说,和前面的没有什么区别,只是将注解放在具体的类上而言,一样给出一个示例,先定义一个bean
@Component
@Conditional(ScanDemoCondition.class)
public class ScanDemoBean {
@Value("${conditional.demo.load}")
private boolean load;
public boolean getLoad() {
return load;
}
}
复制代码
对应的判断条件以下,当配置文件中conditional.demo.load
为true时,才会加载这个配置,不然不实例化
public class ScanDemoCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return "true".equalsIgnoreCase(conditionContext.getEnvironment().getProperty("conditional.demo.load"));
}
}
复制代码
测试类和前面差很少,稍微注意下的就是自动注入时,改一下必要条件,避免bean不存在时报错
@Autowired(required = false)
private ScanDemoBean scanDemoBean;
@GetMapping(path = "/scan")
public String showDemo() {
String type = environment.getProperty("conditional.demo.load");
if (scanDemoBean == null) {
return "not exists! >>>" + type;
} else {
return "load : " + scanDemoBean.getLoad() + " >>>" + type;
}
}
复制代码
当配置为true时,bean应该存在,走上面的else逻辑
当配置为false时,不会加载bean,走if逻辑
经过@Conditional
注解配合Condition
接口,来决定给一个bean是否建立和注册到Spring容器中,从而实现有选择的加载bean
这样作的目的是什么呢?
上面能够控制bean的建立,但经过上面的流程,会发现有一点繁琐,有没有什么方式能够简化上面的流程呢?
只用一个注解就好,不要本身再来实现Condtion接口,Spring框架提供了一系列相关的注解,以下表
注解 | 说明 |
---|---|
@ConditionalOnSingleCandidate |
当给定类型的bean存在而且指定为Primary的给定类型存在时,返回true |
@ConditionalOnMissingBean |
当给定的类型、类名、注解、昵称在beanFactory中不存在时返回true.各种型间是or的关系 |
@ConditionalOnBean |
与上面相反,要求bean存在 |
@ConditionalOnMissingClass |
当给定的类名在类路径上不存在时返回true,各种型间是and的关系 |
@ConditionalOnClass |
与上面相反,要求类存在 |
@ConditionalOnCloudPlatform |
当所配置的CloudPlatform为激活时返回true |
@ConditionalOnExpression |
spel表达式执行为true |
@ConditionalOnJava |
运行时的java版本号是否包含给定的版本号.若是包含,返回匹配,不然,返回不匹配 |
@ConditionalOnProperty |
要求配置属性匹配条件 |
@ConditionalOnJndi |
给定的jndi的Location 必须存在一个.不然,返回不匹配 |
@ConditionalOnNotWebApplication |
web环境不存在时 |
@ConditionalOnWebApplication |
web环境存在时 |
@ConditionalOnResource |
要求制定的资源存在 |
基础篇
应用篇
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
一灰灰blog