Pregunta

Si bien los marcos de notificación de eventos simples y controlados por interfaz en Java han existido desde tiempos anteriores al Cámbrico (por ejemplo, java.beans.PropertyChangeSupport), es cada vez más popular para los marcos el uso de notificaciones de eventos basados ??en anotaciones.

Para ver un ejemplo, consulte JBossCache 2.2 . La clase de oyente tiene sus métodos de escucha anotados, en lugar de ajustarse a una interfaz rígida. Esto es más fácil de programar y más fácil de leer, ya que no tiene que escribir implementaciones vacías de devoluciones de llamada de escucha en las que no está interesado (y sí, sé acerca de las superclases de adaptador de escucha).

Aquí hay una muestra de los documentos de 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");
   }

}

El problema con esto, es que es mucho más un proceso involucrado que escribe el marco para soportar este tipo de cosas, debido a la naturaleza de la anotación-reflexión.

Entonces, antes de comenzar a escribir un marco genérico, esperaba que alguien ya lo hubiera hecho. ¿Alguien ha encontrado algo así?

¿Fue útil?

Solución

Ya puedes hacer esto hoy con EventBus .

El siguiente ejemplo es de Guía de introducción a EventBus . Barra de estado que se actualiza según los eventos publicados, y no es necesario registrar el control / widget de la barra de estado como oyente de los editores. Sin EventBus, la barra de estado deberá agregarse como escucha a muchas clases. La barra de estado también se puede crear y destruir en cualquier momento.

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

Un proyecto similar es ELF (Event Listener Framework) pero parece ser menos maduro.

Actualmente estoy investigando sobre marcos de notificación de eventos en Programación dirigida por eventos de publicación-suscripción | Kev's Spring vs Java EE Dev y los artículos de seguimiento.

Otros consejos

Hice http://neoevents.googlecode.com para manejar este tipo de evento basado en anotaciones controlador.

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

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

Se ve tan simple como esperaba. Las anotaciones están disponibles para cada oyente en J2SE.

No confundas complicado por inteligente. Me parece que esto sería:

  1. Una pesadilla para depurar
  2. Es difícil de seguir (desde una perspectiva de mantenimiento o alguien que intenta cambiar algo 6 meses después)
  3. Lleno de if (event instanceof NodeCreatedEvent) como código. Por qué esto es mejor que subclasificar un adaptador ¡No tengo idea!

El principal problema que veo aquí son los parámetros de los métodos, que restringen los métodos que pueden usarse para los eventos y no hay ayuda de compilación para eso.

Esto es lo que hace que las interfaces sean más atractivas para las implementaciones de patrones de observador, como el modelo de eventos de Java. Herramientas como eclipse pueden autogenerar apéndices de métodos para que no pueda obtener las firmas incorrectas. En su ejemplo, es muy fácil usar el tipo de parámetro incorrecto y nunca lo sabrá hasta que ocurra un evento (lo que podría ser un caso de error varios meses después)

Una cosa que podrías probar son mis anotaciones & amp; Procesador para la implementación de observadores e implementaciones de objetos nulos. Supongamos que tienes

package a.b.c;

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

y quería crear una instancia de escucha. Podrías escribir

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 crear una fuente para estos eventos, puedes escribir

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
}

Consulte http://code.google.com/p/javadude/wiki/ Anotaciones si estás interesado. También podría darte otras ideas.

También he estado pensando en un marco de eventos genérico basado en anotaciones. Me gustan los beneficios proporcionados por la escritura estática, pero el modelo de eventos basado en interfaz actual es doloroso de usar (código feo). ¿Sería posible usar un procesador de anotaciones personalizado para hacer algunas comprobaciones en tiempo de compilación? Eso podría ayudar a agregar algunos de los " seguridad " desaparecidos " a lo que todos nos hemos acostumbrado.

Gran parte de la comprobación de errores también se puede hacer en el momento en que los oyentes están " registrados " Con los productores del evento. Por lo tanto, la aplicación fallaría antes (cuando los oyentes estén registrados), posiblemente incluso en el momento de inicio.

Este es un ejemplo de cómo podría verse el marco genérico con el que he estado jugando:

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

El productor utiliza EventSupport , que utiliza la reflexión para invocar los eventos. Como se mencionó anteriormente, EventSupport podría realizar algunas verificaciones iniciales cuando los oyentes de eventos estén 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
  }
}

Aquí, EventSupport tiene un método estático que utiliza la reflexión para registrar automáticamente al oyente con el productor del evento. Esto elimina la necesidad de registrarse manualmente con el origen del evento. Se podría usar un procesador de anotaciones personalizado para validar que la anotación @HandlesEventFor se refiere a un campo real de ExampleListener . El procesador de anotaciones también podría realizar otras comprobaciones, como asegurarse de que la firma del método del manejador de eventos coincida con uno de los métodos de registro en ExampleProducer (básicamente, la misma comprobación que se podría realizar en el registro: tiempo).

¿Qué piensas? ¿Vale la pena dedicar algo de tiempo a un desarrollo completo?

Aquí hay un proyecto similar llamado 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));
}
}

Creo que esto es muy fácil de usar.

También puede consultar MBassador Es una anotación, es muy liviana y utiliza referencias débiles ( por lo tanto, es fácil de integrar en entornos donde la gestión del ciclo de vida de los objetos se realiza mediante un marco como Spring o Guice o Somethign).

Proporciona un mecanismo de filtrado de objetos (por lo tanto, puede suscribirse a NodeEvent y adjuntar algunos filtros para restringir el manejo de mensajes a un conjunto de tipos específicos solamente). También puede definir sus propias anotaciones para tener una declaración personalizada de sus manejadores.

Y es muy rápido y eficiente en recursos. Echa un vistazo a este benchmark que muestra un gráfico de rendimiento para diferentes escenarios utilizando Guava o mbassador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top