通用,注释驱动的事件通知框架

虽然Java之前的简单,界面驱动的事件通知框架已经存在于寒武纪之前(例如java.beans.PropertyChangeSupport),但是框架使用注释驱动的事件通知变得越来越流行。

有关示例,请参阅JBossCache 2.2 。 侦听器类对其侦听器方法进行了注释,而不是符合严格的接口。 这更容易编程,更容易阅读,因为您不必编写您不感兴趣的侦听器回调的空实现(是的,我知道侦听器适配器超类)。

以下是JBossCache文档中的示例:

@CacheListener public class MyListener { @CacheStarted @CacheStopped public void cacheStartStopEvent(Event e) { switch (e.getType()) { case Event.Type.CACHE_STARTED: System.out.println("Cache has started"); break; case Event.Type.CACHE_STOPPED: System.out.println("Cache has stopped"); break; } } @NodeCreated @NodeRemoved @NodeVisited @NodeModified @NodeMoved public void logNodeEvent(NodeEvent ne) { log("An event on node " + ne.getFqn() + " has occured"); } 

}

问题在于,由于它的注释 – reflection性质,它更多地是编写框架以支持此类事物的参与过程。

所以,在我开始编写通用框架之前,我希望有人已经完成了它。 有没有人遇到这样的事情?

您今天已经可以使用EventBus执行此操作 。

以下示例来自EventBus入门指南 。 状态栏根据已发布的事件进行更新,无需将状态栏控件/窗口小部件注册为发布者的侦听器。 如果没有EventBus,则需要将状态栏添加为许多类的侦听器。 也可以随时创建和销毁状态栏。

 public StatusBar extends JLabel { public StatusBar() { AnnotationProcessor.process(this); } @EventSubscriber(eventClass=StatusEvent.class) public void updateStatus(StatusEvent statusEvent) { this.setText(statusEvent.getStatusText(); } } 

一个类似的项目是ELF(事件监听器框架),但似乎不太成熟。

我目前正在研究发布 – 订阅事件驱动编程的事件通知框架 Kev的Spring与Java EE Dev以及后续文章。

我已经制作了http://neoevents.googlecode.com来处理这种基于注释的事件处理程序。

  @actionPerformed private void onClick() { //do something } protected void initComponents() { JButton button = new JButton("Click me!!!"); button.addActionListener(new ActionListener(this) ); } 

它看起来就像我期待它一样简单。 J2SE中的每个侦听器都可以使用注释。

不要因为聪明而错误。 在我看来,这将是:

  1. 调试的噩梦
  2. 很难遵循(从维护角度来看,或者有人试图在6个月后改变某些东西)
  3. 充满if (event instanceof NodeCreatedEvent)类的代码。 为什么这比inheritanceadapter更好我不知道!

我在这里看到的主要问题是方法参数,它限制哪些方法实际可用于哪些事件,并且没有编译时帮助。

这使得接口对我来说对于像Java事件模型这样的观察者模式实现很有吸引力。 像eclipse这样的工具可以自动生成方法存根,因此您无法获得错误的签名。 在您的示例中,使用错误的参数类型非常容易,并且在事件发生之前永远不会知道它(这可能是几个月后的错误情况)

您可能尝试的一件事是我的注释和处理器,用于实现观察者和空对象实现。 假设你有

 package abc; public interface SomeListener { void fee(); void fie(); void fo(); void fum(); } 

并且想要创建一个监听器实例。 你可以写

 package xyz; import abcSomeListener; import com.javadude.annotation.Bean; import com.javadude.annotation.NullObject; @Bean(nullObjectImplementations = {@NullObject(type = SomeListener.class) }) public class Foo extends FooGen implements SomeListener { @Override public void fie() { // whatever code you need here } } 

要为这些事件创建源,您可以编写

 package abc; import com.javadude.annotation.Bean; import com.javadude.annotation.Observer; @Bean(observers = {@Observer(type = SomeListener.class)}) public class Source extends SourceGen { // SourceGen will have add/remove listener and fire methods // for each method in SomeListener } 

如果您有兴趣,请参阅http://code.google.com/p/javadude/wiki/Annotations 。 可能还会给你一些其他的想法。

Google Guava v11添加了一个使用此样式的EventBus组件 。 他们还解释了为什么他们决定使用注释而不是接口 。

我一直在考虑一个通用的注释驱动的事件框架。 我喜欢静态类型提供的好处,但当前的界面驱动事件模型使用起来很痛苦(丑陋的代码)。 是否可以使用自定义注释处理器进行一些编译时检查? 这可能有助于增加我们已经习惯的一些缺失的“安全”。

许多错误检查也可以在监听器“注册”事件生产者时完成。 因此,应用程序会提前失败(当监听器被注册时),甚至可能在启动时。

这是我一直在使用的通用框架的示例:

 public class ExampleProducer { private EventSupport eventSupport; public ExampleProducer() { eventSupport = new EventSupport(this); } @AddListenersFor(ActionEvent.class) public void addActionListener(Object listener) { eventSupport.addListener(listener); } @RemoveListenersFor(ActionEvent.class) public void removeActionListener(Object listener) { eventSupport.removeListener(listener); } public void buttonClicked() { eventSupport.fire(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Click")); } } 

生产者使用EventSupport ,它使用reflection来调用事件。 如前所述, EventSupport可以在注册事件侦听器时执行一些初始检查。

 public class ExampleListener { private ExampleProducer submitButton; public ExampleListener() { submitButton = new ExampleProducer(); EventSupport.autoRegisterEvents(this); } @HandlesEventFor("submitButton") public void handleSubmitButtonClick(ActionEvent event) { //...some code to handle the event here } } 

这里, EventSupport有一个静态方法,它使用reflection来自动注册监听器和事件生成器。 这消除了手动注册事件源的需要。 可以使用自定义注释处理器来validation@HandlesEventFor注释是否引用@HandlesEventFor的实际字段。 注释处理器也可以进行其他检查,例如确保事件处理程序方法签名与ExampleProducer上的一个注册方法ExampleProducer (基本上,可以在注册时执行相同的检查)。

你怎么看? 这值得花些时间充分发展吗?

这是一个名为SJES的类似项目。

 public class SomeController { private Calculator c1 = new Calculator(); private Calculator c2 = new Calculator(); public SomeController() { c1.registerReceiver(this); c2.registerReceiver(this); c1.add(10, 10); c2.add(20, 20); } @EventReceiver(handleFor="c1") public void onResultC1(Calculator.Event e) { System.out.println("Calculator 1 got: " + e.result); } @EventReceiver(handleFor="c2") public void onResultC2(Calculator.Event e) { System.out.println("Calculator 2 got: " + e.result); } @EventReceiver public void onResultAll(Calculator.Event e) { System.out.println("Calculator got: " + e.result); } } public class Calculator { private EventHelper eventHelper = new EventHelper(this); public class Event { long result; public Event(long result) { this.result = result; } } public class AddEvent extends Event { public AddEvent(long result) { super(result); } } public class SubEvent extends Event { public SubEvent(long result) { super(result); } } public void unregisterReceiver(Object o) { eventHelper.unregisterReceiver(o); } public void registerReceiver(Object o) { eventHelper.registerReceiver(o); } public void add(long a, long b) { eventHelper.fireEvent(new AddEvent(a + b)); } public void sub(long a, long b) { eventHelper.fireEvent(new SubEvent(a - b)); } public void pass(long a) { eventHelper.fireEvent(new Event(a)); } } 

我认为这很容易使用。

您还可以查看MBassador它是注释驱动的,非常轻量级并使用弱引用(因此很容易集成到对象生命周期管理由spring或guice或somethign等框架完成的环境中)。

它提供了一个对象过滤机制(因此您可以订阅NodeEvent并附加一些filter,以仅将消息处理限制为一组特定类型)。 您还可以定义自己的注释,以便自定义处理程序的声明。

它非常快速且资源效率高。 查看此基准测试,显示使用Guava或mbassador的不同方案的性能图表。