Pergunta

Embora simples, os quadros de notificação de eventos-driven de interface em Java tem sido em torno desde os tempos pré-cambriano (por exemplo java.beans.PropertyChangeSupport), está se tornando cada vez mais popular para os quadros Para utilizar a notificação evento orientado a anotação em seu lugar.

Por exemplo, veja JBossCache 2.2 . A classe ouvinte tem os seus métodos de ouvinte anotado, em vez de se conformarem a uma interface rígida. Isto é um pouco mais fácil de programa para, e mais fácil de ler, desde que você não tem que escrever implementações vazias de retornos de chamada de ouvinte que você não está interessado em (e sim, eu sei sobre superclasses adaptador ouvinte).

Aqui está uma amostra dos 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");
   }

}

O problema com isso, é que é muito mais de um processo envolvido escrevendo a estrutura para suportar esse tipo de coisa, devido à natureza anotação-reflexo dela.

Assim, antes de eu cobrar pela estrada de escrever uma estrutura genérica, eu estava esperando que alguém tinha feito isso já. Alguém se deparar com uma coisa dessas?

Foi útil?

Solução

Você já pode fazer isso hoje com EventBus .

Seguindo o exemplo é de EventBus Guia de Primeiros Passos . Barra de Estado que as atualizações com base em eventos publicados, e não há necessidade de registrar o controle de estado / widget como ouvinte de editor (s). Sem EventBus, Barra de Estado terá de ser adicionado como ouvinte para muitas classes. Barra de Estado também pode ser criado e destruído a qualquer momento.

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

Um projeto semelhante é ELF (Event Listener Framework) mas parece ser menos maduros.

Atualmente estou pesquisando sobre os quadros de notificação de eventos em Publish-Subscribe Evento programação orientada | Primavera do Kev vs Java EE Dev e os artigos de acompanhamento.

Outras dicas

Eu fiz http://neoevents.googlecode.com para lidar com este tipo de evento anotação com base manipulador.

        @actionPerformed
        private void onClick() {
                //do something
        }

        protected void initComponents() {
                JButton button = new JButton("Click me!!!");
                button.addActionListener(new ActionListener(this) );
        }

Parece tão simples como eu estava esperando que ele seja. Anotações estão disponíveis para cada ouvinte em J2SE.

Não confunda complicado para inteligente. Parece-me que este seria:

  1. Um pesadelo para depuração
  2. difícil seguir (a partir de uma perspectiva de manutenção, ou alguém tentando mudar algo 6 meses abaixo da linha)
  3. Cheio de if (event instanceof NodeCreatedEvent) como código. Por que isso é melhor do que subclasses um adapter Eu não tenho idéia!

O principal problema que vejo aqui são os parâmetros do método, que restringem quais métodos podem realmente ser usado para que os eventos, e não há nenhuma ajuda em tempo de compilação para isso.

Isto é o que faz com que as interfaces atraentes para me para implementações padrão Observer como o modelo de evento Java. Ferramentas como o eclipse pode Autogen stubs de métodos para que você não pode obter o errado assinaturas. No seu exemplo, é muito fácil de usar o tipo de parâmetro errado e nunca sabe-lo até que ocorre um evento (que pode ser um caso de erro de vários meses para baixo da linha)

Uma coisa que você pode tentar são as minhas anotações e processador para a implementação de observadores e implementações nulo objeto. Suponha que você tenha

package a.b.c;

public interface SomeListener {
    void fee();
    void fie();
    void fo();
    void fum();
}

e queria criar uma instância de ouvinte. Você poderia escrever

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
    }
}

Para criar uma fonte para esses eventos, você pode escrever

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/ anotações se você estiver interessado. Pode dar-lhe algumas outras idéias também.

Eu estive pensando sobre uma estrutura de eventos orientado a anotação genérica bem. Eu como os benefícios proporcionados por tipagem estática, mas o modelo de evento-driven interface atual é doloroso para uso (código feio). Seria possível usar um processador de anotação personalizado para fazer alguns testes em tempo de compilação? Essa ajuda poderia acrescentar alguns dos desaparecidos "segurança" que todos nós temos crescido acostumado.

Um monte de verificação de erro também pode ser feito no momento em que os ouvintes são "registrado" com os produtores de eventos. Assim, a aplicação seria um fracasso precoce (quando os ouvintes são registrados), possivelmente mesmo em na inicialização tempo.

Aqui está um exemplo do que a estrutura genérica Fui brincar com o olhar pode gostar:

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"));
    }
   }

O EventSupport usos produtores, que usa a reflexão para invocar os eventos. Como mencionado antes, EventSupport poderia preformas algumas verificações iniciais, quando os ouvintes de eventos são registrados.

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
  }
}

Aqui, EventSupport tem um método estático que usa reflexão para auto-registrar o ouvinte com o produtor do evento. Isso elimina a necessidade de registrar manualmente com a origem do evento. Um processador de anotação personalizado pode ser usado para validar que o anotação @HandlesEventFor refere-se a um campo real do ExampleListener. O processador de anotação poderia fazer outras verificações, bem como, tais como assegurar que o evento manipulador método de assinatura combina com um dos métodos de registro no ExampleProducer (basicamente, a mesma verificação que pode ser realizada no momento da inscrição em tempo).

O que você acha? É este vale a pena colocar algum tempo em desenvolver plenamente?

Aqui está um projeto semelhante chamado 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));
}
}

Eu acho que isso é muito fácil de usar.

Você também pode verificar MBassador É anotação impulsionado, muito leve e usa referências fracas ( portanto, fácil de integrar em ambientes onde objetos de gerenciamento de ciclo de vida é feito por uma estrutura como a primavera ou guice ou somethign).

Ele fornece um objeto mecanismo de filtragem (assim você pode se inscrever para NodeEvent e anexar alguns filtros para restringir a manipulação mensagem para um conjunto de apenas tipos específicos). Você também pode definir suas próprias anotações para ter declaração personalizada de seus manipuladores.

E é muito rápido e eficiente dos recursos. Confira este href="https://github.com/bennidi/eventbus-performance" rel="nofollow"> referência mostrando um gráfico de desempenho para diferentes cenários usando goiaba ou mbassador.

scroll top