ConditionalOnExpression 无法将配置属性与枚举类型进行比较,作为字符串工作

Posted

技术标签:

【中文标题】ConditionalOnExpression 无法将配置属性与枚举类型进行比较,作为字符串工作【英文标题】:ConditionalOnExpression fails to compare configuration property to enum type, works as string 【发布时间】:2019-01-22 01:30:03 【问题描述】:

我想根据属性文件中的枚举加载@Configuration 类,所以我有以下类:

@Configuration
@ConditionalOnExpression("# $demo.spel.demo-enum eq T(demo.spel.DemoEnum).VALUE ")
public class DemoConfig 

我有:demo.spel.demo-enum=valueapplication.properties

这不起作用,并抛出异常:

原因:org.springframework.expression.spel.SpelEvaluationException:EL1008E:在“org.springframework.beans.factory.config.BeanExpressionContext”类型的对象上找不到属性或字段“值” - 可能不公开或不公开有效吗?

奇怪的是,如果我将单引号添加到属性部分,并在表达式的枚举部分添加toString(),则没有例外,条件为真,并且创建了 bean(由在调试日志中检查控制台输出):

@ConditionalOnExpression("# '$demo.spel.demo-enum' eq T(demo.spel.DemoEnum).VALUE.toString() ")

问题:

为什么像这样比较枚举会失败?为什么 Spring 可以成功地将值转换为字符串而不是类型?

这是在 Spring Boot 2.0.4 上

【问题讨论】:

你为什么不像@ConditionalOnProperty(prefix = "demo.spel", name = "demo-enum", havingValue="VALUE")那样使用ConditionalOnProperty 这就是我最终使用的。我知道如何解决这个问题,但我的问题特别问为什么会失败,以及在转换为字符串值时它是如何工作的:) 我可以看到自己在条件表达式中使用枚举的这个特性来进行属性比较,如果这样的话工作 【参考方案1】:

我遇到了类似的问题。 我有一个功能,默认情况下是启用的。要禁用它,应用程序配置文件应该明确禁用它。 demo.feature.disable:true

我有一个以这个属性为条件的 spring bean(默认启用)。

@ConditionalOnExpression("# $demo.feature.disable != true ")
@Component
public class FeatureModule 

问题是,当 demo.spel.value 未在配置文件中定义 - application.yml 时,该组件的初始化将失败并显示

Caused by: org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly()'

为了解决这个问题,我提供了默认值。

@ConditionalOnExpression("# $demo.feature.disable:false != true ")
@Component
public class FeatureModule 

现在,当我测试它时。 默认情况下,此组件已初始化。

如果配置文件没有demo.feature.disable,这个组件将被初始化。

如果配置文件有demo.feature.disable:true,这个组件将不会被初始化。

如果配置文件有demo.feature.disable:false这个组件将被初始化。

【讨论】:

【参考方案2】:

应该很明显,真的。

考虑以下 Java 代码:

foo.equals(DemoEnum.VALUE)

它需要一个对象foo,可能是this 上的一个字段:

this.foo.equals(DemoEnum.VALUE)

如果您的属性占位符计算结果为“foo”,则您的第一个 SpEL 表达式等效于

#this.foo eq T(DemoEnum).VALUE

所以 SpEL 在 #this 上寻找属性 foo

编辑

如果你创建一个类:

public class Foo 

    @Value("$some.property")
    private DemoEnum enum;

    public getEnum() 
        return this.enum;
    


然后将 bean 添加到名为“foo”的上下文中,然后您可以使用

foo.enum eq ...

因为#thisBeanExpressionContext 允许您引用其他bean。

【讨论】:

我不确定我是否理解。在您的示例中,无论如何这都非常有意义。假设this 有字段private DemoEnum foo = DemoEnum.VALUE;,检查foo.equals(DemoEnum.VALUE) 将返回true。此外,这并不能解释为什么当转换为字符串值时,表达式会返回 true。 否,因为在评估 #... 时,#thisBeanExpressionContext - 它允许您使用 someBean.someProperty 引用 bean。查看我的编辑。 >...doesn't explain - 是的;您现在正在将字符串与字符串进行比较。您还可以使用T(DemoEnum).valueOf('$some.property') eq T(DemoEnum).VALUE 来比较 2 个枚举。 属性只是字符串值;如果有足够的信息,Spring 可以对它们应用转换(例如我的@Value 示例)。在 SpEL 的情况下,属性是简单的字符串,因为没有任何东西可以提供转换提示。使用#$some.property ...,属性值被认为是一个bean名称;因此,SpEL 表达式将在应用程序上下文中查找名为 value 的 bean。使用#'$some.property' ...,属性值被视为字符串文字。 是的,你的理解是正确的。对于您的第二条评论,如果配置正确,该字段不应为空,因为 SpEL 将从上下文中 get() bean,这将触发它的自动装配。对话时间过长(SO 管理员会冲我们咆哮)。如果仍然无法按预期工作,请提出一个新问题并显示所有相关配置。

以上是关于ConditionalOnExpression 无法将配置属性与枚举类型进行比较,作为字符串工作的主要内容,如果未能解决你的问题,请参考以下文章

#私藏项目实操分享#Spring专题「实战系列」spring注解@ConditionalOnExpression详细使用说明

Spring专题「实战系列」针对Spring注解@ConditionalOnExpression详细使用说明

Spring专题「实战系列」针对Spring注解@ConditionalOnExpression详细使用说明

Spring Boot注解

Spring Boot注解

springboot注解使用