前提介绍
通过 Spring 框架进行判断的 Bean,Class 是否存在,配置参数是否存在或者有某个值而言,这个依赖 SPEL 表达式的,就显得更加的高级了;其主要就是执行 Spel 表达式,根据返回的 true/false 来判断是否满足条件。至于 SPEL 是什么东西,下面以一个简单的 demo 进行演示它的使用姿势。
@ConditionalOnExpression
接口定义
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnExpressionCondition.class)
public @interface ConditionalOnExpression {
/**
* The SpEL expression to evaluate. Expression should return {@code true} if the
* condition passes or {@code false} if it fails.
* @return the SpEL expression
*/
String value() default "true";
}
复制代码
实例测试
用一个简单的例子,当配置参数中,根据是否满足某个条件来决定是否需要加载 bean
测试用例
定义一个满足条件和一个不满足的 bean
public class ExpressFalseBean {
private String name;
public ExpressFalseBean(String name) {
this.name = name;
}
public String getName() {
return "express bean :" + name;
}
}
public class ExpressTrueBean {
private String name;
public ExpressTrueBean(String name) {
this.name = name;
}
public String getName() {
return "express bean :" + name;
}
}
复制代码
采用 #{}的方式进行分析配置
采用 #{}通过 environment 的对象进行获取相关的配置信息 key
@Configuration
public class ExpressAutoConfig {
/**
* 当存在配置,且配置为true时才创建这个bean
* @return
*/
@Bean
@ConditionalOnExpression("#{'true'.equals(environment['conditional.express'])}")
public ExpressTrueBean expressTrueBean() {
return new ExpressTrueBean("express true");
}
/**
* 配置不存在,或配置的值不是true时,才创建bean
* @return
*/
@Bean
@ConditionalOnExpression("#{!'true'.equals(environment.getProperty('conditional.express'))}")
public ExpressFalseBean expressFalseBean() {
return new ExpressFalseBean("express != true");
}
}
复制代码
对应的配置如下
实例演示
@RestController
@RequestMapping(path = "express")
public class ExpressRest {
@Autowired(required = false)
private ExpressTrueBean expressTrueBean;
@Autowired(required = false)
private ExpressFalseBean expressFalseBean;
@GetMapping(path = "show")
public String show() {
Map<String, String> result = new HashMap<>(4);
result.put("expressTrueBean", expressTrueBean == null ? "null ==> false" : expressTrueBean.getName());
result.put("expressFalseBean", expressFalseBean == null ? "null ==> true": expressFalseBean.getName());
return JSONObject.toJSONString(result);
}
}
复制代码
采用 ${}的方式进行分析配置
@ConditionalOnExpression("${mq.consumer.enabled}==1&&${rabbitmq.comsumer.enabled:true}")
@ConditionalOnExpression("'${mq.consumer}'.equals('rabbitmq')")
复制代码
解析配置类
使用 @ConditionalOnExpression("'${task.enable}'.equals('true')")这种方式的话,是可以的。由于这个配置在自定义的文件中,因此使用了
@PropertySource(value= "classpath:taskconfig.yml", ignoreResourceNotFound = true , factory = MixPropertySourceFactory.class)
引入配置文件。 MixPropertySourceFactory 是自定义的类,用于加载 yml 类型的配置文件。
public class MixPropertySourceFactory extends DefaultPropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
String sourceName = name != null ? name : resource.getResource().getFilename();
if (!resource.getResource().exists()) {
return new PropertiesPropertySource(sourceName, new Properties());
} else if (sourceName.endsWith(".yml") || sourceName.endsWith(".yaml")) {
Properties propertiesFromYaml = loadYml(resource);
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
} else {
return super.createPropertySource(name, resource);
}
}
private Properties loadYml(EncodedResource resource) throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
}
}
复制代码
在 application.properties
写如下配置就不会是实例化这个 bean
@Bean
@ConditionalOnExpression("!${database.isEmbedded:true}")
public ConnectionPool dataSourceMBean(DataSourceProxy dataSourceProxy) throws SQLException {
return dataSourceProxy.createPool().getJmxPool();
}
复制代码
Condition 注解使用
Spring 提供了很多 Condition 给我们用,@ConditionalOnExpression 表示基于 SpEL 表达式作为判断条件,这个组合了 @Conditional 元注解,只是使用了不同的条件(Conditional)
@Conditional,满足特定条件创建一个 Bean,SpringBoot 就是利用这个特性进行自动配置的。
首先,两个 Condition,判断当前系统是否是 Windows 或者 Linux(True False)
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return arg0.getEnvironment().getProperty("os.name").contains("Linux");
}
}
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return arg0.getEnvironment().getProperty("os.name").contains("Windows");
}
}
复制代码
然后,2 个 ListService 实现类,表明不同系统下的 ListService 实现。
public interface ListService {
public String showListCmd();
}
public class LinuxListService implements ListService{
@Override
public String showListCmd() {
return "ls";
}
}
public class WindowsListService implements ListService{
@Override
public String showListCmd() {
return "dir";
}
}
复制代码
然后 ConditionConfig 使用了 Java 配置与 @Conditional 注解,根据 LinuxCondition,或者 WindowsCondition 作为判断条件
@Configuration
public class ConditionConfig {
@Bean
@Conditional(WindowsCondition.class)
public ListService windowsListService() {
return new WindowsListService();
}
@Bean
@Conditional(LinuxCondition.class)
public ListService linuxListService() {
return new LinuxListService();
}
}
复制代码
最后,App.java 测试成功。
public class App {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(
ConditionConfig.class);
ListService ls = context.getBean(ListService.class);
System.out.println(context.getEnvironment().getProperty("os.name")
+ "系统下的列表命令为:" + ls.showListCmd());
}
}
复制代码
参考资料
https://cloud.tencent.com/developer/article/1403118
评论