Domanda

Mentre i framework di notifica degli eventi semplici e basati su interfaccia in Java sono in circolazione sin dai tempi pre-cambriani (ad esempio java.beans.PropertyChangeSupport), sta diventando sempre più popolare per i framework utilizzare invece la notifica degli eventi basata sulle annotazioni.

Per un esempio, vedi JBossCache 2.2 . La classe listener ha i suoi metodi listener annotati, piuttosto che conformi a un'interfaccia rigida. Questo è piuttosto più facile da programmare e più facile da leggere, dal momento che non è necessario scrivere implementazioni vuote di callback listener che non ti interessano (e sì, so di superclassi dell'adattatore listener).

Ecco un esempio dai documenti di 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");
   }

}

Il problema con questo è che è molto più un processo coinvolto che scrive il framework per supportare questo tipo di cose, a causa della sua natura annotazione-riflessione.

Quindi, prima di iniziare a scrivere un framework generico, speravo che qualcuno lo avesse già fatto. Qualcuno ha mai visto una cosa del genere?

È stato utile?

Soluzione

Puoi già farlo oggi con EventBus .

L'esempio seguente è tratto dalla Guida introduttiva a EventBus . Barra di stato che si aggiorna in base agli eventi pubblicati e non è necessario registrare il controllo / widget della barra di stato come listener di editori. Senza EventBus, la barra di stato dovrà essere aggiunta come listener a molte classi. La barra di stato può anche essere creata e distrutta in qualsiasi momento.

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

Un progetto simile è ELF (Event Listener Framework) ma sembra essere meno maturo.

Attualmente sto effettuando ricerche sui framework di notifica degli eventi su Pubblica-Abbonati Programmazione guidata da eventi | Kev's Spring vs Java EE Dev e gli articoli successivi.

Altri suggerimenti

Ho creato http://neoevents.googlecode.com per gestire questo tipo di evento basato su annotazioni handler.

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

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

Sembra semplice come me lo aspettavo. Le annotazioni sono disponibili per ogni singolo ascoltatore in J2SE.

Non confondere complicato con intelligenza. Mi sembra che questo sarebbe:

  1. Un incubo da debug
  2. Difficile da seguire (dal punto di vista della manutenzione o qualcuno che tenta di cambiare qualcosa 6 mesi dopo la fine)
  3. Pieno di if (evento dell'istanza di NodeCreatedEvent) come il codice. Perché questo è meglio della sottoclasse di un adattatore non ne ho idea!

Il problema principale che vedo qui sono i parametri del metodo, che limitano quali metodi possono essere effettivamente utilizzati per quali eventi, e non c'è aiuto in fase di compilazione per quello.

Questo è ciò che rende le interfacce interessanti per me per implementazioni di modelli di osservatori come il modello di eventi Java. Strumenti come eclipse sono in grado di autogenizzare gli stub del metodo in modo da non poter sbagliare le firme. Nel tuo esempio, è molto facile usare il tipo di parametro sbagliato e non conoscerlo mai fino a quando non si verifica un evento (che potrebbe essere un caso di errore diversi mesi dopo la fine)

Una cosa che potresti provare sono le mie annotazioni e amp; processore per l'implementazione di osservatori e implementazioni di oggetti nulli. Supponi di avere

package a.b.c;

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

e volevo creare un'istanza di ascolto. Puoi scrivere

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

Per creare una fonte per questi eventi, puoi scrivere

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
}

Vedi http://code.google.com/p/javadude/wiki/ Annotazioni se sei interessato. Potrebbe darti anche altre idee.

Ho pensato anche a un generico framework di eventi basato su annotazioni. Mi piacciono i vantaggi offerti dalla tipizzazione statica, ma l'attuale modello di eventi basato sull'interfaccia è doloroso da usare (codice brutto). Sarebbe possibile utilizzare un processore di annotazione personalizzato per eseguire un controllo in fase di compilazione? Ciò potrebbe aiutare ad aggiungere parte della "sicurezza" mancante a cui ci siamo tutti abituati.

Gran parte del controllo degli errori può essere effettuato anche nel momento in cui gli ascoltatori sono "registrati". con i produttori dell'evento. Pertanto, l'applicazione fallirebbe presto (quando gli ascoltatori sono registrati), possibilmente anche al momento dell'avvio.

Ecco un esempio di come potrebbe apparire il framework generico con cui ho giocato:

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

Il produttore utilizza EventSupport , che utilizza la riflessione per invocare gli eventi. Come accennato in precedenza, EventSupport potrebbe preformare alcuni controlli iniziali quando gli ascoltatori di eventi sono registrati.

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

Qui EventSupport ha un metodo statico che utilizza la riflessione per registrare automaticamente l'ascoltatore con il produttore dell'evento. Ciò elimina la necessità di registrarsi manualmente con l'origine evento. È possibile utilizzare un processore di annotazione personalizzato per convalidare che l'annotazione @HandlesEventFor si riferisce a un campo effettivo di ExampleListener . Il processore delle annotazioni potrebbe fare anche altri controlli, ad esempio assicurando che la firma del metodo del gestore eventi corrisponda a uno dei metodi di registrazione su ExampleProducer (in pratica, lo stesso controllo che potrebbe essere eseguito al momento della registrazione- tempo).

Cosa ne pensi? Vale la pena dedicare del tempo allo sviluppo completo?

Ecco un progetto simile chiamato 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));
}
}

Penso che sia molto facile da usare.

Puoi anche dare un'occhiata a MBassador È guidato dalle annotazioni, molto leggero e usa riferimenti deboli ( quindi facile da integrare in ambienti in cui la gestione del ciclo di vita degli oggetti viene eseguita da un framework come spring o guice o somethign).

Fornisce un meccanismo di filtraggio degli oggetti (quindi è possibile abbonarsi a NodeEvent e allegare alcuni filtri per limitare la gestione dei messaggi solo a un set di tipi specifici). Puoi anche definire le tue annotazioni per avere una dichiarazione personalizzata dei tuoi gestori.

Ed è molto veloce ed efficiente in termini di risorse. Dai un'occhiata a questo benchmark che mostra un grafico delle prestazioni per diversi scenari utilizzando Guava o mbassador.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top