Spring @EventListener条件省略枚举全限定名引发SpEL解析异常
解决Spring @EventListener中SpEL解析异常并省略枚举全限定名的问题
问题背景
环境:Spring、Java 8
定义枚举类:
public enum NEStatus { Online(0), Offline(1), Unknown(2); }
原本使用枚举全限定名的@EventListener代码可正常运行:
@EventListener(condition = "#sdnStatusEvent.getNeStatus() != null && #sdnStatusEvent.getNeStatus() == T(com.mycompany.myproject.NEStatus).Offline") public void handleSdnStatusEvent(SdnStatusEvent sdnStatusEvent) { LOGGER.info("handleSdnStatusEvent: sdnStatusEvent = {}", sdnStatusEvent); }
修改为省略全限定名的写法后触发SpEL解析异常:
@EventListener(condition = "#sdnStatusEvent.getNeStatus() != null && #sdnStatusEvent.getNeStatus() == #object.NEStatus.name() == 'Offline'") public void handleSdnStatusEvent(SdnStatusEvent sdnStatusEvent) { LOGGER.info("handleSdnStatusEvent: sdnStatusEvent = {}", sdnStatusEvent); }
异常信息:
2023-07-05 12:25:34.424 ERROR [ContextLoader:localhost-startStop-1] Context initialization failed org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'eq(==)' at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:129) ~[spring-expression-4.3.22.RELEASE.jar:4.3.22.RELEASE]
错误原因
修改后的SpEL表达式存在两个核心问题:
- SpEL不支持链式相等判断(连续使用
==),这是触发解析异常的直接原因。 #object.NEStatus的写法不符合SpEL语法,#object在@EventListener的condition上下文里并非指向枚举类实例。
解决方法
方法1:直接调用枚举实例的name()方法比较字符串
这是最简洁的方案:利用枚举类自带的name()方法获取常量名称,直接与目标字符串比较,无需枚举全限定名,同时语法完全符合SpEL规范。
修改后的代码:
@EventListener(condition = "#sdnStatusEvent.getNeStatus() != null && #sdnStatusEvent.getNeStatus().name() == 'Offline'") public void handleSdnStatusEvent(SdnStatusEvent sdnStatusEvent) { LOGGER.info("handleSdnStatusEvent: sdnStatusEvent = {}", sdnStatusEvent); }
方法2:将枚举类注册为SpEL全局变量(适用于多处复用场景)
如果需要在多个@EventListener或其他SpEL场景中复用该枚举类,可以在配置类中将枚举类注册为SpEL全局变量:
@Configuration public class SpelConfig { @Bean public StandardEvaluationContext standardEvaluationContext() { StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("NEStatus", NEStatus.class); return context; } }
之后在condition中即可直接使用变量调用枚举常量:
@EventListener(condition = "#sdnStatusEvent.getNeStatus() != null && #sdnStatusEvent.getNeStatus() == #NEStatus.Offline") public void handleSdnStatusEvent(SdnStatusEvent sdnStatusEvent) { LOGGER.info("handleSdnStatusEvent: sdnStatusEvent = {}", sdnStatusEvent); }
总结
推荐优先使用方法1,无需额外配置,代码简洁且完全满足需求;如果存在多处复用枚举类的场景,可选择方法2实现全局复用。
内容的提问来源于stack exchange,提问作者Alexei
相关产品推荐
相关产品推荐

