Question

Bien que les infrastructures de notification d'événements pilotées par interface en Java existent depuis l'époque pré-cambrienne (par exemple, java.beans.PropertyChangeSupport), il est de plus en plus courant que les infrastructures utilisent plutôt la notification d'événements par annotation.

Pour un exemple, voir JBossCache 2.2 . La méthode d'écoute de la classe d'auditeur est annotée plutôt que conforme à une interface rigide. C'est assez facile à programmer et à lire, car vous n'avez pas à écrire des implémentations vides de rappels d'écouteurs qui ne vous intéressent pas (et oui, je connais les superclasses d'adaptateurs d'écouteur).

Voici un exemple de la documentation 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");
   }

}

Le problème, c’est qu’il s’agit bien plus d’un processus complexe pour l’écriture du cadre afin de prendre en charge ce genre de choses, en raison de sa nature annotation-réflexion.

Donc, avant de me lancer dans la rédaction d'un cadre générique, j'espérais que quelqu'un l'avait déjà fait. Quelqu'un at-il rencontré une telle chose?

Était-ce utile?

La solution

Vous pouvez déjà le faire aujourd'hui avec EventBus .

L'exemple suivant est tiré du Guide de mise en route d'EventBus . Statusbar qui se met à jour en fonction des événements publiés. Il n'est donc pas nécessaire d'enregistrer le contrôle / widget de la barre d'état en tant qu'écouteur des éditeurs. Sans EventBus, la barre d'état devra être ajoutée en tant qu'auditeur à de nombreuses classes. Statusbar peut également être créé et détruit à tout moment.

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

Un projet similaire est ELF (Framework Listener Event)) , mais il semble être moins mature.

Je suis actuellement à la recherche de cadres de notification d'événements sur Programmation événementielle avec publication / abonnement | Kev's Spring vs Java EE Dev et les articles qui suivent.

Autres conseils

J'ai fait http://neoevents.googlecode.com gérer ce type d'événement basé sur des annotations. gestionnaire.

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

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

Cela a l'air aussi simple que ce à quoi je m'attendais. Les annotations sont disponibles pour chaque auditeur dans J2SE.

Ne confondez pas compliqué avec intelligent. Il me semble que ce serait:

  1. Un cauchemar à déboguer
  2. Difficile à suivre (du point de vue de la maintenance ou de toute personne tentant de modifier quelque chose dans les 6 mois)
  3. Complet de si (instance d'instance de NodeCreatedEvent) code similaire. Pourquoi c'est mieux que de sous-classer un adaptateur je n'en ai aucune idée!

Le principal problème que je vois ici concerne les paramètres de méthode, qui limitent les méthodes pouvant être utilisées pour quels événements. Aucune aide à la compilation n’est disponible pour cela.

C’est ce qui me rend attractif les interfaces pour les implémentations de modèles d’observateur telles que le modèle d’événement Java. Des outils tels que eclipse peuvent créer des souches de méthode autogen afin que vous ne puissiez pas vous tromper de signature. Dans votre exemple, il est très facile d'utiliser un type de paramètre incorrect et de ne le savoir jamais avant qu'un événement ne se produise (ce qui peut être un cas d'erreur plusieurs mois plus tard)

Une chose que vous pourriez essayer sont mes annotations & amp; processeur pour l'implémentation d'observateurs et l'implémentation d'objets nuls. Supposons que vous ayez

package a.b.c;

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

et je voulais créer une instance de listener. Vous pourriez écrire

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

Pour créer une source pour ces événements, vous pouvez écrire

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
}

Voir http://code.google.com/p/javadude/wiki/ Annotations si cela vous intéresse. Cela pourrait aussi vous donner d’autres idées.

J'ai également pensé à un cadre d'événements générique piloté par des annotations. J'aime les avantages offerts par le typage statique, mais le modèle d'événement actuel basé sur une interface est difficile à utiliser (code laid). Serait-il possible d'utiliser un processeur d'annotation personnalisé pour effectuer une vérification à la compilation? Cela pourrait aider à ajouter certaines des informations manquantes "Sécurité". que nous avons tous habitués.

Une grande partie de la vérification des erreurs peut également être effectuée au moment où les écouteurs sont "enregistrés". avec les producteurs de l'événement. Ainsi, l’application échouerait tôt (lors de l’enregistrement des écouteurs), peut-être même au moment du démarrage.

Voici un exemple de ce à quoi pourrait ressembler le cadre générique que j'ai utilisé:

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

Le producteur utilise EventSupport , qui utilise la réflexion pour appeler les événements. Comme mentionné précédemment, EventSupport pourrait effectuer certaines vérifications initiales lors de l'enregistrement des écouteurs d'événements.

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

Ici, EventSupport a une méthode statique qui utilise la réflexion pour enregistrer automatiquement l'écouteur auprès du producteur d'événements. Cela évite d'avoir à s'inscrire manuellement auprès de la source d'événements. Un processeur d'annotation personnalisé peut être utilisé pour valider que l'annotation @HandlesEventFor fait référence à un champ réel de ExampleListener . Le processeur d'annotation peut également effectuer d'autres vérifications, comme par exemple s'assurer que la signature de la méthode du gestionnaire d'événements correspond à l'une des méthodes d'enregistrement du ExampleProducer (à la base, le même contrôle que celui qui a pu être effectué lors de l'enregistrement). temps).

Qu'en penses-tu? Cela vaut-il la peine de consacrer un peu de temps au plein développement?

Voici un projet similaire appelé 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));
}
}

Je pense que c'est très facile à utiliser.

Vous pouvez également consulter MBassador . Il est basé sur les annotations, très léger et utilise des références faibles ( donc facile à intégrer dans des environnements où la gestion du cycle de vie des objets est réalisée par un framework tel que spring ou guice ou quelque chose)

Il fournit un mécanisme de filtrage des objets (vous pouvez donc vous abonner à NodeEvent et associer des filtres pour limiter la gestion des messages à un ensemble de types spécifiques). Vous pouvez également définir vos propres annotations pour une déclaration personnalisée de vos gestionnaires.

Et c'est très rapide et efficace en ressources. Découvrez ce référence montrant un graphique de performances pour différents scénarios utilisant Guava ou mbassador.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top