Spring Framework 4.2에서 애플리케이션 이벤트

애플리케이션 이벤트는 느슨하게 결합 된 구성 요소 사이의 정보를 교환하는 수단으로서 Spring Framework의 맨 처음부터 사용할 수 있었습니다. 애플리케이션 이벤트의 가장 잘 알려진 사용법 중 하나는 다음과 같습니다.

1
2
3
4
5
6
7
8
@Component
public class MyListener
implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
...
}
}

이렇게하면 컨텍스트가 refresh 될때 MyListener가 통보되고 응용 프로그램 컨텍스트가 완전히 시작될 때 코드를 실행할 수 있습니다.

Spring Framework 4.2에서는 세가지 주요 영역에서 이벤트 인프라를 다시 살펴 보았습니다.

Generic 지원

다음과 같이 이벤트 타입에 중첩 된 Generic 정보로 ApplicationListener 구현을 정의 할 수 있습니다.

1
2
public class MyListener
implements ApplicationListener<MyEvent<Order>> { ... }

이벤트를 전달할 때 Listener의 Signature를 사용하여 들어오는 이벤트와 일치하는지 확인합니다.

MyOrderEvent extends MyEvent<Order>와 같이 타입 소거(Type erasure)로 인해 필터링 할 Generic 파라미터를 해결하는 이벤트를 publish 해야합니다.
다른 해결 방법이있을 수 있고, 커뮤니티에서 가치 있다고 생각하는 경우 Signature 일치 알고리즘(matching algorithm)을 다시 재검토할 수 있습니다.

Annotation 기반 이벤트 Listener

가장 큰 새로운 기능은 Spring Framework 4.1의 JMS 및 AMQP endpoint에 대한 최근 작업과 유사한 Annotation 기반 이벤트 Listener의 지원입니다. 메소드의 Signature와 일치하는 ApplicationListener를 자동으로 등록하기 위해 관리 Bean의 메소드에 @EventListener를 사용하여 Annotation을 붙일 수 있습니다. 위의 예는 다음과 같이 다시 작성할 수 있습니다.

1
2
3
4
5
6
7
8
@Component
public class MyListener {
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
...
}
}

@EventListener@Autowired 및 다른것과 비슷한 방식으로 처리되는 핵심 Annotation입니다. Java 구성에서는 추가 구성이 필요하지 않으며 기존 <context:annotation-driven/> 요소로 완벽하게 지원할 수 있습니다.

메서드 Signature는 관심있는 이벤트 타입을 정의합니다. 이벤트를 처리하기 위해 SpEL 표현식을 정의 할 수도 있습니다. 예를 들어, 다음 이벤트를 생각해 보겠습니다.

1
2
3
4
5
6
7
public class OrderCreatedEvent implements CreationEvent<Order> { ... }
private boolean awesome;
public boolean isAwesome() { return this.awesome; }
....
}

다음 예제는 awesomeCreationEvent Order (즉,awesome 플래그가 true)에 대해서만 호출 될 이벤트 Listener를 보여줍니다.

1
2
3
4
5
6
7
8
9
@Component
public class MyComponent {
@EventListener(condition = "#creationEvent.awesome")
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
...
}
}

위의 예제에서 볼 수 있듯이 메서드 파라미터는 해당 정보를 발견 할 수있는 이름을 통해 표시됩니다. 조건식은 raw ApplicationEvent(#root.event) 및 실제 메서드 파라미터 (#root.args)가 있는 “root” 변수도 표시합니다.

Event Publish 하기

@EventListener로 Annotation이 붙은 모든 메소드에 대해 리턴 타입이 void가 아닌 타입을 정의 할 수 있습니다. 특정 이벤트를 처리 한 결과로 null값을 반환하지 않으면 해당 결과가 새로운 이벤트로 전송됩니다

여러분은 OrderCreatedEventApplicationEvent를 상속하지 않는다는 것을 알아 차렸을 것입니다. Spring Framework는 임의의 이벤트를 공개하고 ApplicationEvent를 상속하도록 강요하지 않는 유연성을 줄 때가되었다고 느꼈습니다. ApplicationEventPublisher 인터페이스가 확장되어 모든 객체를 Publish 할 수 있습니다. 객체가 ApplicationEvent가 아닌 경우 PayloadApplicationEvent로 래핑합니다. 일반적인 ApplicationListener 구현을 사용하여 이러한 임의의 이벤트를 수신하려는 경우 이 내용을 기억해야 합니다.

다음 예제는 ApplicationEventPublisher를 사용하여 OrderCreatedEvent를 보내는 방법을 보여줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class MyComponent {
private final ApplicationEventPublisher publisher;
@Autowired
public MyComponent(ApplicationEventPublisher publisher) { ... }
public void createOrder(Order order) {
// ....
this.publisher.publishEvent(new OrderCreatedEvent(order));
}
}

Transaction bound events

또 다른 인기있는 개선사항은 이벤트 Listener를 트랜잭션 단계에 바인드하는 기능입니다. 일반적인 예는 트랜잭션이 성공적으로 완료되면 이벤트를 처리하는 것입니다. 이렇게하면 현재 트랜잭션의 결과가 Listener에게 중요한 경우 이벤트를 보다 유연하게 사용할 수 있습니다.

Spring Framework는 컨텍스트가 트랜잭션 지원을 인식하지 못하도록하는 방식으로 구조화되어 있으며, 우리는 분명히 그 원칙에서 벗어나고 싶지 않았습니다. 그래서 추가 구성 요소를 등록하고 이벤트 리스너가 생성되는 방식의 개방형 인프라를 구축했습니다.

트랜잭션 모듈은 새로운 @TransactionalEventListener Annotation을 찾는 EventListenerFactory를 구현합니다. 이 값이 있으면 트랜잭션을 인식하는 확장 이벤트 리스너가 기본값 대신 등록됩니다.

위 예제를 다시 사용하여 Producer가 실행중인 트랜잭션이 성공적으로 완료 될 때만 주문 생성 이벤트를 처리되도록 다음과 같이 다시 작성해 보겠습니다.

1
2
3
4
5
6
7
8
9
@Component
public class MyComponent {
@TransactionalEventListener(condition = "#creationEvent.awesome")
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
...
}
}

@TransactionalEventListener는 일반 @EventListener이며, 기본값은 AFTER_COMMITTransactionPhase를 나타냅니다. 트랜잭션의 다른 단계 (BEFORE_COMMIT, AFTER_ROLLBACK, AFTER_COMMIT, AFTER_ROLLBACK의 별명 인AFTER_COMPLETION)를 연결할 수도 있습니다.

기본적으로 트랜잭션이 실행되고 있지 않으면 요청한 단계를 지키지 못하기 때문에 이벤트가 전혀 전송되지 않습니다. 그러나 @TransactionalEventListenerfallbackExecution 속성이 있습니다. 이 어트리뷰트는 트랜잭션이 없으면 즉시 리스너를 호출하도록 Spring에 지시합니다.

작성해보기

4.2의 첫 번째 마일스톤 릴리스 전에 이 기능을 사용하려면 스냅 샷 저장소를 통해 야간 SNAPSHOT 빌드를 작성하십시오. 최신 스프링 부트 스냅 샷 빌드를 사용하여 start.spring.io를 사용하여 샘플 프로젝트를 만들 수도 있습니다. 또는 매우 게으른 경우 셸에서 이 프로젝트를 복사/붙여 넣기 할 수 있습니다.

1
2
$ curl https://start.spring.io/starter.tgz -d artifactId=events-demo \
-d baseDir=events-demo -d bootVersion=1.2.2.BUILD-SNAPSHOT | tar -xzvf -

Spring Framework 4.2.0.BUILD-SNAPSHOT을 사용하도록 프로젝트를 업데이트하십시오.

1
2
3
4
<properties>
...
<spring.version>4.2.0.BUILD-SNAPSHOT</spring.version>
</properties>

언제나처럼, 우리는 커뮤니티 의견을 환영합니다. 이 기능을 사용해보고 문제가 생기면 알려주십시오.


이 내용은 나중에 참고하기 위해 제가 공부하며 정리한 내용입니다.
의역, 오역, 직역이 있을 수 있음을 알려드립니다.
This post is a translation of this original article [https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2]

공유하기