해당 게시물은 백기선님의 스프링 프레임워크 핵심 기술 강좌를 정리한 내용 입니다.
ApplicationEventPublisher
- ApplicationEventPublisher는 옵저버 패턴의 구현체로 이벤트 프로그래밍에 필요한 기능을 제공한다.
- ApplicationContext는 ApplicationEventPublisher를 상속 받는다.
Spring 4.2 이전 버전에서 이벤트 발생 및 처리
1) ApplicationEvent를 상속받은 이벤트 클래스 작성
Spring 4.2 이전 버전에서는 ApplicationEvent를 상속받은 이벤트 클래스를 작성해야 합니다.
그리고 이벤트는 빈으로 등록하지 않습니다.
아래 코드에서 이벤트 MyEvent는 이벤트를 발생시킨 source와 정수 데이터를 담아서 전달하도록 하였습니다.
public class MyEvent extends ApplicationEvent {
private int data;
public MyEvent(Object source) {
super(source);
}
public MyEvent(Object source, int data){
super(source);
this.data = data;
}
public int getData(){
return data;
}
}
2) 이벤트 발생 시키기
@Autowired로 ApplicationEventPublisher를 주입 받은 다음 publishEvent() 메서드를 호출하여 이벤트를 발생 시킵니다.
ApplicationEventPublisher의 publishEvent() : 이벤트를 발생 시킵니다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher publishEvent;
@Override
public void run(ApplicationArguments args) throws Exception {
publishEvent.publishEvent(new MyEvent(this, 100));
}
}
3) 이벤트 핸들러 작성
앞서 만든 이벤트를 받아서 처리하는 "이벤트 핸들러"는 클래스로 작성한 다음, 빈으로 등록해야 한다.
그리고 Spring 4.2 이전 버전에서 이벤트 핸들러는 ApplicationListener를 구현해야 한다.
그 다음 onApplicationEvent() 안에 이벤트 발생 시, 처리할 코드를 작성하면 된다.
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) {
System.out.println("이벤트 받았다. 데이터는" + event.getData());
}
}
※ 애플리케이션 실행 순서
① 스프링 부트 애플리케이션이 구동된다.
② 그리고 AppRunner가 실행 되면서 publishEvent()로 이벤트를 발생 시킨다.
③ 등록 되어 있는 Bean 중에서 이벤트 핸들러(MyEventHandler)가 발생된 이벤트를 처리 한다.
Spring 4.2 이후 버전에서 이벤트 발생 및 처리
1) 이벤트 클래스 작성
Spring 4.2부터는 이벤트 클래스는 ApplicationEvent를 상속받을 필요가 없습니다.
그래서 코드를 아래와 같이 변경 하였습니다.
public class MyEvent{
private int data;
private Object source;
public MyEvent(Object source, int data) {
this.source = source;
this.data = data;
}
public int getData() {
return data;
}
public Object getSource() {
return source;
}
}
위의 코드는 스프링 코드가 노출되지 않는 POJO 기반의 프로그래밍이며 이는 개발자가 테스트 및 유지보수 하기 쉽게 만들어 준다.
비침투성(non-invasive) : 자신의 코드에 스프링 코드가 노출되지 않는 것을 말한다.
즉, POJO를 그대로 유지하면서도 스프링의 기능을 제공하는 것을 말한다.
POJO(Plain Old Java Object) : 오래된 방식의 간단한 자바 오브젝트라는 말로서
객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로
설계된 오브젝트를 말한다.
2) 이벤트 발생 시키기
3) 이벤트 핸들러 작성
Spring 4.2 이후 버전에서 이벤트 핸들러는 ApplicationListener를 구현할 필요가 없으며 빈으로 등록 되어야 한다.
그리고 @EventListener 애노테이션만 붙여주면 됩니다.
(이벤트 핸들러를 빈으로 등록 해야 하는 이유는 스프링이 누구에게 이벤트를 전달해야 하는지 알아야 하기 때문이다.)
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) { // 메서드명은 사용자 마음대로 결정 가능
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
여러 개 존재하는 이벤트 핸들러
이벤트 핸들러가 여러 개인 경우, 하나의 thread에서 순차적으로 실행된다.
* 여기서 "순차적" 이라는 것은 하나의 핸들러가 실행되고 나서 다른 핸들러가 실행된다는 것을 의미 한다.
1) 새로운 핸들러를 작성한다.
그리고 하나의 thread에서 순차적으로 실행되는지 확인 하기 위해 이벤트 핸들러에 Thread 출력 코드를 추가 합니다.
@Component
public class AnotherHandler {
@EventListener
public void handle(MyEvent myEvent){
System.out.println(Thread.currentThread().toString());
System.out.println("Another " + myEvent.getData());
}
}
2) 기존 핸들러(MyEventHandler)의 코드를 변경한다.
@Component
public class MyEventHandler{
@EventListener
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
실행 결과를 보면 둘 다, main 스레드가 출력되는 것을 확인 할 수 있다.
[실행 결과]

이벤트 핸들러의 처리 순서 정하기 - @Order
@Order : 이벤트 핸들러의 처리 순서를 결정 한다.
@Order 애노테이션에 설정하는 값이 클수록 우선순위가 낮아집니다.
Ordered.HIGHEST_PRECEDENCE로 설정하면 가장 높은 우선순위를 갖게 됩니다. (가장 먼저 실행 됨)
@Component
public class MyEventHandler{
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE)
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
AnotherHandler에는 @Order(Ordered.HIGHEST_PRECEDENCE + 2)를 붙여준다.
그러면 MyEventHandler가 먼저 이벤트를 처리하고 AnotherEventHandler가 나중에 처리하게 된다
@Component
public class AnotherHandler {
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
public void handle(MyEvent myEvent){
System.out.println(Thread.currentThread().toString());
System.out.println("Another" + myEvent.getData());
}
}
[실행 결과]

이벤트 핸들러의 비동기적 실행 - @Async
이벤트 핸들러는 기본적으로 동기적으로(synchronized) 실행 된다.
비동기적으로 실행 하려면 @EventListener 애노테이션이 붙어 있는 메서드에 @Async를 사용한다.
@Component
public class MyEventHandler{
@EventListener
@Async
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
@Component
public class AnotherHandler {
@EventListener
@Async
public void handle(MyEvent myEvent){
System.out.println(Thread.currentThread().toString());
System.out.println("Another" + myEvent.getData());
}
}
그리고 스프링 부트 애플리케이션이 실행되는 main 진입점에 해당하는 @SpringBootApplication 클래스에
@EnableAsync을 붙여 준다.
@SpringBootApplication
@EnableAsync
public class Demospring51Application {
public static void main(String[] args) {
SpringApplication.run(Demospring51Application.class, args);
}
}
[실행결과]

스프링이 제공하는 기본 이벤트
ContextRefreshedEvent : ApplicationContext를 초기화 하거나 리프레쉬 했을 때 발생.
ContextStartedEvent : ApplicationContext를 start()하여 라이프사이클 빈들이 시작 신호를 받은 시점에 발생.
ContextStoppedEvent : ApplicationContext를 stop()하여 라이프사이클 빈들이 정지 신호를 받은 시점에 발생.
ContextClosedEvent : ApplicationContext를 close()하여 싱글톤 빈 소멸되는 시점에 발생.
RequestHandledEvent : HTTP 요청을 처리했을 때 발생.
@Component
public class MyEventHandler{
@EventListener
@Async
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
@EventListener
@Async
public void handle(ContextRefreshedEvent event){
System.out.println(Thread.currentThread().toString());
System.out.println("ContextRefreshedEvent");
}
@EventListener
@Async
public void handle(ContextClosedEvent event){
System.out.println(Thread.currentThread().toString());
System.out.println("ContextClosedEvent");
}
}
'Spring > Spring Core' 카테고리의 다른 글
[Section 2] Resource 추상화 (0) | 2020.09.13 |
---|---|
[Section 1] IoC 컨테이너 9부: ResourceLoader (0) | 2020.09.06 |
[Section 1] IoC 컨테이너 7부: MessageSource (0) | 2020.09.06 |
[Section 1] IoC 컨테이너 6부: Environment 2부. 프로퍼티 (0) | 2020.09.03 |
[Section 1] IoC 컨테이너 6부: Environment 1부. 프로파일 (0) | 2020.09.03 |
댓글