写点什么

🍃【Spring 专题】「实战系列」spring 注解 @ConditionalOnExpression 详细使用说明

作者:浩宇天尚
  • 2021 年 12 月 09 日
  • 本文字数:3255 字

    阅读完需:约 11 分钟

🍃【Spring专题】「实战系列」spring注解@ConditionalOnExpression详细使用说明

前提介绍

通过 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


@Configurationpublic 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"); }}
复制代码


对应的配置如下


conditional.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


database.isEmbedded:true
复制代码


@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 作为判断条件


@Configurationpublic 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

发布于: 14 小时前阅读数: 12
用户头像

浩宇天尚

关注

🏆 InfoQ写作平台-签约作者 🏆 2020.03.25 加入

【个人简介】酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“ 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域、Spring生态、MySQL专项、APM专题及微服务/分布式体系等

评论

发布
暂无评论
🍃【Spring专题】「实战系列」spring注解@ConditionalOnExpression详细使用说明