通用,注释驱动的事件通知框架
-
05-07-2019 - |
题
虽然Java之前的简单,界面驱动的事件通知框架已经出现在寒武纪之前(例如java.beans.PropertyChangeSupport),但是框架变得越来越流行使用注释驱动的事件通知。
有关示例,请参阅 JBossCache 2.2 。 listener类对其侦听器方法进行了注释,而不是符合严格的接口。这更容易编程,更容易阅读,因为您不必编写您不感兴趣的侦听器回调的空实现(是的,我知道侦听器适配器超类)。
以下是来自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");
}
}
问题在于,由于它的注释 - 反射性质,它更多地是编写框架来支持这类事情的参与过程。
所以,在我开始编写通用框架之前,我希望有人已经完成了它。有没有人遇到这样的事情?
解决方案
您今天已经可以使用 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 vs 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中的每个侦听器都可以使用注释。
不要因为聪明而错误。在我看来,这将是:
- 调试的噩梦
- 很难遵循(从维护角度来看,或有人试图在6个月后改变某些事情)
- 完整的
if(event instanceof NodeCreatedEvent)
之类的代码。为什么这比继承adapter
更好我不知道!
醇>
我在这里看到的主要问题是方法参数,它限制哪些方法可以实际用于哪些事件,并且没有编译时帮助。
这使得接口对我来说对于像Java事件模型这样的观察者模式实现很有吸引力。像eclipse这样的工具可以自动生成方法存根,因此您无法获得错误的签名。在您的示例中,使用错误的参数类型非常容易,并且在事件发生之前永远不会知道它(这可能是几个月后的错误情况)
你可能尝试的一件事是我的注释&用于实现观察者和空对象实现的处理器。假设你有
package a.b.c;
public interface SomeListener {
void fee();
void fie();
void fo();
void fum();
}
并希望创建一个侦听器实例。你可以写
package x.y.z;
import a.b.c.SomeListener;
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 a.b.c;
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/注释如果您有兴趣。可能会给你一些其他的想法。
我一直在考虑通用的注释驱动的事件框架。我喜欢静态类型提供的好处,但是当前的界面驱动事件模型使用起来很痛苦(丑陋的代码)。是否可以使用自定义注释处理器进行一些编译时检查?这可能有助于增加一些缺失的“安全”。我们都习惯了。
许多错误检查也可以在收听者被“注册”时完成。与活动制作人。因此,应用程序会提前失败(当监听器被注册时),甚至可能在启动时失败。
以下是我一直在使用的通用框架的示例:
public class ExampleProducer {
private EventSupport<ActionEvent> eventSupport;
public ExampleProducer() {
eventSupport = new EventSupport<ActionEvent>(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
,它使用反射来调用事件。如前所述, 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
有一个静态方法,它使用反射来自动注册监听器和事件生成器。这消除了手动注册事件源的需要。可以使用自定义注释处理器来验证 @HandlesEventFor
注释是否引用 ExampleListener
的实际字段。注释处理器也可以进行其他检查,例如确保事件处理程序方法签名与 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));
}
}
我认为这很容易使用。