문제

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

}

이 문제는 주석 반사 특성으로 인해 이런 종류의 일을 지원하기 위해 프레임 워크를 작성하는 관련 프로세스가 훨씬 더 많다는 것입니다.

그래서 일반 프레임 워크를 작성하는 길을 청구하기 전에 누군가가 이미 그것을했기를 바라고있었습니다. 누구든지 그런 일을 본 적이 있습니까?

도움이 되었습니까?

해결책

오늘 이미이 작업을 수행 할 수 있습니다 이벤트 버스.

다음 예제입니다 EventBus 시작 가이드. 게시 된 이벤트를 기반으로 업데이트되는 상태 바, Publisher (S)의 청취자로서 Statersbar Control/Widget을 등록 할 필요가 없습니다. EventBus가 없으면 StatusBar는 많은 클래스의 리스너로 추가해야합니다. StatusBar는 언제든지 생성 및 파괴 될 수 있습니다.

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

비슷한 프로젝트입니다 ELF (이벤트 청취자 프레임 워크) 그러나 그것은 덜 성숙한 것 같습니다.

현재 이벤트 알림 프레임 워크에 대해 연구하고 있습니다 게시-구독 이벤트 중심 프로그래밍 | Kev 's Spring vs Java Ee Dev 그리고 후속 기사.

다른 팁

나는 만들었다 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의 모든 청취자에게 주석이 있습니다.

영리하게 복잡하게 착각하지 마십시오. 이것이 나에게 이것이 될 것 같습니다.

  1. 디버그하는 악몽
  2. 따라 가기 어렵다 (유지 보수 관점에서 또는 6 개월 동안 무언가를 바꾸려고 시도하는 사람)
  3. 가득한 if (event instanceof NodeCreatedEvent) 코드처럼. 이것이 서브 클래싱보다 더 나은 이유 adapter 나는 전혀 모른다!

내가 여기에서 볼 수있는 주요 문제는 메소드 매개 변수로, 어떤 이벤트에 실제로 사용할 수 있는지 제한하는 메소드 매개 변수이며, 이에 대한 컴파일 타임 도움이 없습니다.

이것이 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 관심이 있다면. 다른 아이디어도 줄 수도 있습니다.

나는 일반적인 주석 중심의 이벤트 프레임 워크에 대해서도 생각하고 있습니다. 정적 타이핑이 제공하는 이점을 좋아하지만 현재 인터페이스 중심 이벤트 모델은 사용하기가 고통 스럽습니다 (Ugly Code). 맞춤형 주석 프로세서를 사용하여 컴파일 타임 확인을 수행 할 수 있습니까? 그것은 우리 모두가 자란 누락 된 "안전성"을 추가하는 데 도움이 될 수 있습니다.

청취자가 이벤트 제작자와 "등록"할 때 많은 오류 확인을 수행 할 수도 있습니다. 따라서 신청서는 조기에 실패합니다 (청취자가 등록 될 때) 아마도 시작 시간에도 가능합니다.

다음은 제가 놀랐던 일반 프레임 워크가 어떻게 보일지에 대한 예입니다.

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 (기본적으로 등록 시간에 수행 할 수있는 동일한 점검).

어떻게 생각해? 이것이 완전히 발전하는 데 시간을 할애 할 가치가 있습니까?

여기에 비슷한 프로젝트가 있습니다 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));
}
}

나는 이것이 매우 쉽게 사용하기 쉽다고 생각합니다.

체크 아웃 할 수도 있습니다 MBASSADOR 주석 구동, 매우 가벼운 가중이며 약한 참조를 사용합니다 (따라서 물체 라이프 사이클 관리가 Spring 또는 Guice 또는 Somethign과 같은 프레임 워크에 의해 수행되는 환경에서 쉽게 통합 할 수 있습니다).

객체 필터링 메커니즘을 제공합니다 (따라서 Nodeevent를 구독하고 일부 필터를 연결하여 메시지 처리를 특정 유형 세트로만 제한 할 수 있습니다). 또한 자신의 주석을 정의하여 핸들러의 맞춤형 선언을 할 수도 있습니다.

그리고 매우 빠르고 자원 효율적입니다. 이것을 확인하십시오 기준 Guava 또는 Mbassador를 사용하여 다양한 시나리오에 대한 성능 그래프를 보여줍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top