Типовые рамки уведомлений о событиях на основе аннотаций - PullRequest
7 голосов
/ 07 октября 2008

Несмотря на то, что простые документированные структуры уведомлений о событиях в 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 ]

8 голосов
/ 30 декабря 2009

Вы уже можете сделать это сегодня с 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 (Event Listener Framework) , но он кажется менее зрелым.

В настоящее время я изучаю фреймворки уведомлений о событиях в Публикация-подписка Программирование на основе событий | Kev's Spring против Java EE Dev и последующие статьи.

4 голосов
/ 11 января 2010

Я сделал 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.

3 голосов
/ 07 октября 2008

Не путайте сложное с умным. Мне кажется, что это будет:

  1. Кошмар для отладки
  2. Трудно следовать (с точки зрения обслуживания, или кто-то пытается что-то изменить через 6 месяцев)
  3. Полный if (event instanceof NodeCreatedEvent) как код. Почему это лучше, чем создавать подклассы adapter Понятия не имею!
2 голосов
/ 07 октября 2008

Основная проблема, которую я вижу здесь, это параметры метода, которые ограничивают, какие методы могут фактически использоваться для каких событий, и для этого нет справки во время компиляции.

Это то, что делает интерфейсы привлекательными для меня для реализации шаблонов наблюдателей, таких как модель событий 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 голос
/ 03 мая 2010

Вот похожий проект под названием 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));
}
}

Я думаю, что это очень удобно.

1 голос
/ 13 мая 2009

Я также думал об общей структуре событий, управляемой аннотациями. Мне нравятся преимущества, предоставляемые статической типизацией, но текущая модель событий, управляемая интерфейсом, является болезненной для использования (ужасный код). Можно ли будет использовать пользовательский процессор аннотаций для проверки во время компиляции? Это может помочь добавить некоторую недостающую «безопасность», к которой мы все привыкли.

Большая часть проверки ошибок также может быть выполнена в то время, когда слушатели «зарегистрированы» у производителей событий. Таким образом, приложение может выйти из строя рано (при регистрации слушателей), возможно, даже во время запуска.

Вот пример того, как может выглядеть универсальная структура, с которой я играю:

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 (в основном, такая же проверка, которая может быть выполнена во время регистрации). *

Что вы думаете? Стоит ли тратить время на полное развитие?

0 голосов
/ 23 октября 2012

Вы также можете проверить MB Ambassador Он основан на аннотациях, очень легок и использует слабые ссылки (таким образом, его легко интегрировать в среды, где управление жизненным циклом объектов осуществляется с помощью инфраструктуры, такой как spring, guice или somethign ).

Он предоставляет механизм фильтрации объектов (таким образом, вы можете подписаться на NodeEvent и прикрепить некоторые фильтры, чтобы ограничить обработку сообщений только набором определенных типов). Вы также можете определить свои собственные аннотации, чтобы настроить объявление ваших обработчиков.

И это очень быстро и эффективно для использования ресурсов. Проверьте этот бенчмарк , показывающий график производительности для различных сценариев, использующих Guava или mb Ambassador.

...