虽然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中的每个侦听器都可以使用注释。

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

  1. 调试的噩梦
  2. 很难遵循(从维护角度来看,或有人试图在6个月后改变某些事情)
  3. 完整的 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));
}
}

我认为这很容易使用。

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

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

它非常快速且资源效率高。查看基准,显示使用Guava或mbassador的不同场景的性能图。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top