Spring 4.2+:如何从 @EventListener 条件表达式中引用 Spring bean 实例
Spring 4.2+ : How to reference a Spring bean instance from inside an @EventListener condition expression
在Spring 4.2+中,我们可以使用带有"condition"表达式的@EventListener注解。
在我的场景中,我需要将事件对象的 ID 与在 .properties 文件中配置的正则表达式相匹配。
但是,似乎不可能从条件的正则表达式中引用任何 bean 的 属性 或方法,因为根上下文似乎是事件对象本身。
到目前为止,我有一个摘要 class,它根据 class 名称设置事件 ID 模式 属性。目标是使每个事件侦听器的实现尽可能干净和简单。
@Service
@PropertySource(value = "classpath:subscriberEventMapping.properties")
public abstract class AbstractEventHandler implements IEventHandler {
private String eventIdPattern;
@Autowired
Environment env;
@Autowired(required = true)
public void configureEventIdPattern() {
String simpleClassName = this.getClass().getSimpleName();
String resolvedEventIdPattern = env.getProperty(
simpleClassName.substring(0,1).toLowerCase() +
simpleClassName.substring(1, simpleClassName.length()));
this.eventIdPattern = resolvedEventIdPattern == null ? ".*" : resolvedEventIdPattern;
}
public String getEventIdPattern() {
return eventIdPattern;
}
}
属性文件如下所示:
regExpEventHandler=^(901|909|998|1000)$
dummyEventHandler=^([1-9][0-9]{0,2}|1000)$
然后,我有一个扩展上述摘要的示例事件监听器 class:
@Service
public class RegExpEventHandler extends AbstractEventHandler {
@Log
private ILog logger;
@Override
@EventListener(condition = "#event.eventid matches @regExpEventHandler.getEventIdPattern()")
public void onEvent(Event event) {
logger.debug("RegExpEventHandler processing : {} with event pattern : {}", event, getEventIdPattern());
}
}
问题是表达式
"#event.eventid 匹配@regExpEventHandler.getEventIdPattern()"
不起作用,因为在 @EventListener 使用的上下文中找不到 bean“@regExpEventHandler”。
这里有办法访问现有 Spring Bean 的方法或属性吗?对于这种情况还有其他更好的方法吗?
我知道我可以使用类似以下内容轻松访问 STATIC 常量或方法:
#event.eventid matches T(my.package.RegExpEventHandler.MY_CONSTANT)
但是字符串常量(静态最终)无法使用@Value 表达式从属性文件中初始化。
使用NON-FINAL静态常量可以工作,但是EACH事件监听器需要添加样板来初始化静态常量来自使用 @Value 表达式的非静态变量,我们希望避免这种情况。
提前致谢!
它对我有用 - 我查看了 EventExpressionEvaluator
并看到它向评估上下文添加了一个 bean 解析器...
public EvaluationContext createEvaluationContext(ApplicationEvent event, Class<?> targetClass,
Method method, Object[] args, BeanFactory beanFactory) {
Method targetMethod = getTargetMethod(targetClass, method);
EventExpressionRootObject root = new EventExpressionRootObject(event, args);
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(
root, targetMethod, args, getParameterNameDiscoverer());
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}
所以我写了一个快速测试...
@SpringBootApplication
public class So43225913Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(So43225913Application.class, args);
context.publishEvent("foo");
}
@EventListener(condition = "@bar.accept(event)")
public void listen(Object event) {
System.out.println("handler:" + event);
}
@Bean
public Bar bar() {
return new Bar();
}
public static class Bar {
public boolean accept(Object o) {
System.out.println("bar:" + o);
return true;
}
}
}
而且效果很好...
bar:org.springframework.context.PayloadApplicationEvent[...
handler:foo
(这是 4.3.7;启动 1.5.2)。
在Spring 4.2+中,我们可以使用带有"condition"表达式的@EventListener注解。
在我的场景中,我需要将事件对象的 ID 与在 .properties 文件中配置的正则表达式相匹配。
但是,似乎不可能从条件的正则表达式中引用任何 bean 的 属性 或方法,因为根上下文似乎是事件对象本身。
到目前为止,我有一个摘要 class,它根据 class 名称设置事件 ID 模式 属性。目标是使每个事件侦听器的实现尽可能干净和简单。
@Service
@PropertySource(value = "classpath:subscriberEventMapping.properties")
public abstract class AbstractEventHandler implements IEventHandler {
private String eventIdPattern;
@Autowired
Environment env;
@Autowired(required = true)
public void configureEventIdPattern() {
String simpleClassName = this.getClass().getSimpleName();
String resolvedEventIdPattern = env.getProperty(
simpleClassName.substring(0,1).toLowerCase() +
simpleClassName.substring(1, simpleClassName.length()));
this.eventIdPattern = resolvedEventIdPattern == null ? ".*" : resolvedEventIdPattern;
}
public String getEventIdPattern() {
return eventIdPattern;
}
}
属性文件如下所示:
regExpEventHandler=^(901|909|998|1000)$
dummyEventHandler=^([1-9][0-9]{0,2}|1000)$
然后,我有一个扩展上述摘要的示例事件监听器 class:
@Service
public class RegExpEventHandler extends AbstractEventHandler {
@Log
private ILog logger;
@Override
@EventListener(condition = "#event.eventid matches @regExpEventHandler.getEventIdPattern()")
public void onEvent(Event event) {
logger.debug("RegExpEventHandler processing : {} with event pattern : {}", event, getEventIdPattern());
}
}
问题是表达式
"#event.eventid 匹配@regExpEventHandler.getEventIdPattern()"
不起作用,因为在 @EventListener 使用的上下文中找不到 bean“@regExpEventHandler”。
这里有办法访问现有 Spring Bean 的方法或属性吗?对于这种情况还有其他更好的方法吗?
我知道我可以使用类似以下内容轻松访问 STATIC 常量或方法:
#event.eventid matches T(my.package.RegExpEventHandler.MY_CONSTANT)
但是字符串常量(静态最终)无法使用@Value 表达式从属性文件中初始化。
使用NON-FINAL静态常量可以工作,但是EACH事件监听器需要添加样板来初始化静态常量来自使用 @Value 表达式的非静态变量,我们希望避免这种情况。
提前致谢!
它对我有用 - 我查看了 EventExpressionEvaluator
并看到它向评估上下文添加了一个 bean 解析器...
public EvaluationContext createEvaluationContext(ApplicationEvent event, Class<?> targetClass,
Method method, Object[] args, BeanFactory beanFactory) {
Method targetMethod = getTargetMethod(targetClass, method);
EventExpressionRootObject root = new EventExpressionRootObject(event, args);
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(
root, targetMethod, args, getParameterNameDiscoverer());
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}
所以我写了一个快速测试...
@SpringBootApplication
public class So43225913Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(So43225913Application.class, args);
context.publishEvent("foo");
}
@EventListener(condition = "@bar.accept(event)")
public void listen(Object event) {
System.out.println("handler:" + event);
}
@Bean
public Bar bar() {
return new Bar();
}
public static class Bar {
public boolean accept(Object o) {
System.out.println("bar:" + o);
return true;
}
}
}
而且效果很好...
bar:org.springframework.context.PayloadApplicationEvent[...
handler:foo
(这是 4.3.7;启动 1.5.2)。