2008-10-07 37 views
7

尽管简单的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"); 
    } 

}

这里的问题是,它是非常多的复杂过程编写框架来支持这样的事情,由于到它的注释反射性质。因此,在我写下一个通用框架之前,我希望有人已经做到了。有没有人遇到过这样的事情?

回答

8

您今天已经可以用EventBus这样做了。

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

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

一个类似的项目是ELF (Event Listener Framework)但它似乎不太成熟。

我目前正在研究有关Publish-Subscribe Event Driven Programming | Kev's Spring vs Java EE Dev和后续文章的事件通知框架。

3

不要把错误复杂化为聪明。在我看来,这将是:

  1. 一场噩梦调试
  2. 难以跟随(从维护的角度,或者有人企图的修改6个月下来的东西)像
  3. 充满if (event instanceof NodeCreatedEvent)码。为什么这比子类化更好adapter我不知道!
+0

1.我没有看到它比解耦接口更糟糕,当然也没有比我们现在(或应该)使用的AOP重框架更糟糕。 – skaffman 2008-10-07 16:13:31

+0

2.我不明白。您的IDE将向您显示对注释的引用,就像向您展示对接口的引用一样。 – skaffman 2008-10-07 16:14:05

+0

3.因为你在子类化过程中只能得到一个镜头,并且适配器使用它的方式很差。如果你已经被分类了,会怎么样?您或者需要接受命中并实现每个方法都是空的存根,或者引入一个委托对象来处理事件。 – skaffman 2008-10-07 16:14:53

2

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

这使得接口对于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/Annotations如果你有兴趣。也可以给你一些其他的想法。

1

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

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

这里的通用框架我一直在玩弄可能看起来像一个例子:

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上的注册方法之一(基本上,可以在注册时执行相同的检查)相匹配。

您认为如何?这值得花一些时间充分发展吗?

4

我已经让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

这是一个类似的项目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)); 
} 
} 

我觉得这很容易使用。

0

您也可以检出MBassador它是注释驱动的,非常轻量级且使用弱引用(因此易于集成到对象生命周期管理由像spring或guice或somethign这样的框架完成的环境中)。它提供了一个对象过滤机制(因此您可以订阅NodeEvent并附加一些过滤器来将消息处理限制为仅限于一组特定类型)。 您也可以定义自己的注释以对您的处理程序进行自定义声明。

而且它非常快速和资源高效。查看这benchmark显示使用番石榴或mbassador不同场景的性能图表。