Springs ApplicationEvents的自定义过滤器

时间:2019-06-25 08:36:08

标签: java spring java-annotations

在Spring中,当我编写一个EventHandler时,可以设置一个 conditon ,以过滤出不感兴趣的事件,如下所示:

// I use lombok
public class TopicEvent extends ApplicationEvent {
    @Getter @Setter private String topic;
    @Getter @Setter private PayloadObject payload;
}

...

@EventListener(condition = "#event.topic eq \"ecology\"")
public void onEcologyTopicEvent(TopicEvent e) {
    ...
}

哪个已经很好了。但是它没有什么好处

@EventListener
public void onEcologyTopicEvent(TopicEvent e) {
    if (!e.getTopic().equals("ecology") { return; }
    ...
}

我想为TopicEvent的用户提供一个注释

@TopicEventListener(topic = "ecology")
public void onEcologyTopicEvent(TopicEvent e) {
    ...
}

我对此有三个想法:

1: Spring提供了综合的注释和@AliasFor。也许可以使用

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventListener
public @interface TopicEventListner {

    @AliasFor(annotation = EventListener.class, /* can I tweak topic to the string #event.topic eq $topic? */)
    String topic;
}

2: (似乎更合理)我可以注册一些基础结构组件,也许是自定义的ApplicationEventMulticaster还是在运行时向EventListener添加过滤器?如果是这样,那么从哪里开始是一个好地方,即哪个是我要分别在 where?中注册的类/组件,我应该在哪里实现?迷上了吗?

3 :在编译时将@TopicEventListener(topic = "ecology")替换为@EventListener(condition = "#event.topic eq \"ecology\"")。但是这种方法似乎...可能有点过大了,我对这类事情没有丝毫的线索,并希望它非常复杂。
...但是这可能是我用C ++(带有宏)解决它的方式

2 个答案:

答案 0 :(得分:1)

如何定义@EcologyTopicEventListener

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@EventListener(condition = "#event.topic eq ecology")
public @interface EcologyTopicEventListener {
}

如果您有预定义的主题列表,则此方法比@TopicEventListener(topic="ecology")更好,因为它消除了“生态”中可能出现的问题

如果您在编译时不知道此列表,则很可能无法采用所介绍的第一种方法。

在这种情况下,如果要在运行时定义Bean(在应用程序上下文启动期间更为精确),则可以使用Bean工厂后处理器。简而言之,它们允许以动态方式将bean定义注册到应用程序上下文中。 因此,您可以自己创建侦听器bean,甚至可以动态生成它们。

对于第三种方法,如果您问我,我也认为这是一个过大的杀手

答案 1 :(得分:0)

由于Mark Bramnik的想法,我草拟了此解决方案:

注释不继承@EventListener

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyEventAnnotation
{
   String topic() default "";
}

相反,我使用BeanPostProcessorApplicationContext创建ApplicationListeners并为创建的每个Bean添加它们:

@Component
public class MyEventAnnotationBeanPostProcessor implements BeanPostProcessor
{
   private static Logger logger = LoggerFactory.getLogger(MyEventAnnotationBeanPostProcessor.class);

   @Autowired
   AbstractApplicationContext ctx;

   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
   {
      for (Method method: bean.getClass().getMethods()) {
         if (method.isAnnotationPresent(MyEventAnnotation.class)) {
            MyEventAnnotation annotation = method.getAnnotation(MyEventAnnotation.class);
            ctx.addApplicationListener(createApplicationListener(method, bean, annotation.topic()));
         }
      }
      return bean;
   }

   private ApplicationListener<MyEvent> createApplicationListener(Method m, Object bean, String topic) {
      return (MyEvent e) -> {
         if (topic.equals("") || e.getTopic().equals(topic)) { // Filter here!
            try {
               m.invoke(bean, e);
            } catch (IllegalAccessException e1) {
               e1.printStackTrace();
            } catch (InvocationTargetException e1) {
               e1.printStackTrace();
            }
         }
      };
   }
}
  

这只是该想法的粗略概图,可能包含不安全的操作。我只是Java的初学者,所以如果您复制此代码,请不要怪我。但是随时建议修复方法:D