java 通用的、注解驱动的事件通知框架
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/178700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generic, annotation-driven event notification frameworks
提问by skaffman
While simple, interface-driven event notification frameworks in Java have been around since pre-Cambrian times (e.g. java.beans.PropertyChangeSupport), it is becoming increasingly popular for frameworks to use annotation-driven event notification instead.
虽然 Java 中简单的、接口驱动的事件通知框架自寒武纪之前就已经存在(例如 java.beans.PropertyChangeSupport),但框架使用注释驱动的事件通知来代替它变得越来越流行。
For an example, see JBossCache 2.2. The listener class has its listener methods annotated, rather than conforming to a rigid interface. This is rather easier to program to, and easier to read, since you don't have to write empty implementations of listener callbacks that you're not interested in (and yes, I know about listener adapter superclasses).
有关示例,请参阅JBossCache 2.2。侦听器类对其侦听器方法进行了注释,而不是遵循严格的接口。这更容易编程,也更容易阅读,因为您不必编写您不感兴趣的侦听器回调的空实现(是的,我知道侦听器适配器超类)。
Here's a sample from the JBossCache docs:
这是 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");
}
}
}
The problem with this, is that it's very much more of an involved process writing the framework to support this sort of thing, due to the annotation-reflection nature of it.
与此有关的问题在于,由于它的注释反射性质,编写框架以支持此类事情的过程要复杂得多。
So, before I charge off down the road of writing a generic framework, I was hoping someone had done it already. Has anyone come across such a thing?
所以,在我开始编写通用框架的道路之前,我希望有人已经完成了。有没有人遇到过这样的事情?
回答by Hendy Irawan
You can already do this today with EventBus.
Following example is from EventBus Getting Started guide. Statusbar that updates based on published events, and no need to register statusbar control/widget as listener of publisher(s). Without EventBus, statusbar will need to be added as listener to many classes. Statusbar can also be created and destroyed at any time.
以下示例来自EventBus 入门指南。基于已发布事件更新的状态栏,无需将状态栏控件/小部件注册为发布者的侦听器。如果没有 EventBus,则需要将状态栏添加为许多类的侦听器。状态栏也可以随时创建和销毁。
public StatusBar extends JLabel {
public StatusBar() {
AnnotationProcessor.process(this);
}
@EventSubscriber(eventClass=StatusEvent.class)
public void updateStatus(StatusEvent statusEvent) {
this.setText(statusEvent.getStatusText();
}
}
A similar project is ELF (Event Listener Framework)but it seems to be less mature.
一个类似的项目是ELF(事件监听器框架),但它似乎不太成熟。
I'm currently researching about event notification frameworks on Publish-Subscribe Event Driven Programming | Kev's Spring vs Java EE Devand the followup articles.
我目前正在研究关于发布-订阅事件驱动编程的事件通知框架| Kev 的 Spring vs Java EE Dev和后续文章。
回答by eric
I've made http://neoevents.googlecode.comto handle this kind of annotation based event handler.
我已经制作了http://neoevents.googlecode.com来处理这种基于注释的事件处理程序。
@actionPerformed
private void onClick() {
//do something
}
protected void initComponents() {
JButton button = new JButton("Click me!!!");
button.addActionListener(new ActionListener(this) );
}
It looks as simple as I was expecting it to be. Annotations are available for every single listener in J2SE.
它看起来和我预期的一样简单。J2SE 中的每个侦听器都可以使用注释。
回答by oxbow_lakes
Don't mistake complicated for clever. It seems to me that this would be:
不要将复杂误认为聪明。在我看来,这将是:
- A nightmare to debug
- Difficult to follow (from a maintenance perspective, or someone attempting to change something 6 months down the line)
- Full of
if (event instanceof NodeCreatedEvent)like code. Why this is better than subclassing anadapterI have no idea!
- 调试的噩梦
- 难以遵循(从维护的角度来看,或者有人试图在 6 个月后改变某些东西)
- 充满
if (event instanceof NodeCreatedEvent)喜欢的代码。为什么这比子类化更好,adapter我不知道!
回答by Scott Stanchfield
The main problem I see here are the method parameters, which restrict which methods can actually be used for which events, and there's no compile-time help for that.
我在这里看到的主要问题是方法参数,它限制了哪些方法可以实际用于哪些事件,并且没有编译时帮助。
This is what makes interfaces attractive to me for observer pattern implementations like the Java event model. Tools like eclipse can autogen method stubs so you can't get the signatures wrong. In your example, it's very easy to use the wrong parameter type and never know it until an event occurs (which might be an error case several months down the line)
这就是接口对 Java 事件模型等观察者模式实现有吸引力的原因。像 eclipse 这样的工具可以自动生成方法存根,所以你不会弄错签名。在您的示例中,很容易使用错误的参数类型,并且在事件发生之前永远不会知道它(这可能是几个月后的错误情况)
One thing you might try are my annotations & processor for implementing observers and null object implementations. Suppose you have
您可能会尝试的一件事是我的注释和处理器,用于实现观察者和空对象实现。假设你有
package a.b.c;
public interface SomeListener {
void fee();
void fie();
void fo();
void fum();
}
and wanted to create a listener instance. You could write
并想创建一个侦听器实例。你可以写
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
}
}
To create a source for these events, you can write
要为这些事件创建源,您可以编写
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
}
See http://code.google.com/p/javadude/wiki/Annotationsif you're interested. Might give you some other ideas as well.
如果您有兴趣,请参阅http://code.google.com/p/javadude/wiki/Annotations。也可以给你一些其他的想法。
回答by skaffman
回答by Nitramz
Here's a similar project called SJES.
这是一个名为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));
}
}
I think this is very easy to use.
我认为这很容易使用。
回答by Jim Hurne
I've been thinking about a generic annotation-driven event framework as well. I like the benefits provided by static typing, but the current interface-driven event model is painful to use (ugly code). Would it be possible to use a custom annotation processor to do some compile-time checking? That might help add some of the missing "safety" that we've all grown used to.
我也一直在考虑通用的注释驱动事件框架。我喜欢静态类型提供的好处,但是当前的接口驱动事件模型使用起来很痛苦(丑陋的代码)。是否可以使用自定义注释处理器进行一些编译时检查?这可能有助于增加我们已经习惯的一些缺失的“安全性”。
A lot of the error checking can also be done at the time that the listeners are "registered" with the event producers. Thus, the application would fail early (when the listeners are registered), possibly even at at startup-time.
许多错误检查也可以在侦听器向事件生产者“注册”时完成。因此,应用程序会提前失败(在注册侦听器时),甚至可能在启动时失败。
Here's an example of what the generic framework I've been toying with might look like:
这是我一直在玩弄的通用框架的示例:
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"));
}
}
The producer uses EventSupport, which uses reflection to invoke the events. As mentioned before, EventSupportcould preform some initial checks when the events listeners are registered.
生产者使用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
}
}
Here, EventSupporthas a static method that uses reflection to auto-register the listener with the event producer. This eliminates the need to manually register with the event source. A custom annotation processor could be used to validate that the @HandlesEventForannotation refers to an actual field of the ExampleListener. The annotation processor could do other checks as well, such as ensuring that the event handler method signature matches up with one of the registration methods on the ExampleProducer(basically, the same check that could be performed at registration-time).
这里EventSupport有一个静态方法,它使用反射自动向事件生产者注册侦听器。这消除了手动注册事件源的需要。自定义注释处理器可用于验证@HandlesEventFor注释是否引用了ExampleListener. 注释处理器也可以进行其他检查,例如确保事件处理程序方法签名与上的注册方法之一匹配ExampleProducer(基本上,可以在注册时执行的相同检查)。
What do you think? Is this worth putting some time into fully developing?
你怎么认为?这值得花一些时间完全开发吗?
回答by bennidi
You can also check out MBassadorIt is annotation driven, very light-weight and uses weak references (thus easy to integrate in environments where objects lifecycle management is done by a framework like spring or guice or somethign).
您还可以查看MBassador它是注释驱动的,非常轻量级并使用弱引用(因此很容易集成到对象生命周期管理由 spring 或 guice 等框架完成的环境中)。
It provides an object filtering mechanism (thus you could subscribe to NodeEvent and attach some filters to restrict message handling to a set of specific types only). You can also define your own annotations to have customized declaration of your handlers.
它提供了对象过滤机制(因此您可以订阅 NodeEvent 并附加一些过滤器以将消息处理限制为一组特定类型)。您还可以定义自己的注释来自定义处理程序的声明。
And it's very fast and resource efficient. Check out this benchmarkshowing a performance graph for different scenarios using Guava or mbassador.
它非常快速且资源高效。查看此基准测试,其中显示了使用 Guava 或 mbassador 的不同场景的性能图。

