[스프링 프레임워크 핵심기술] ApplicationEventPublisher

Dayeon myeong·2021년 3월 24일
0

ApplicationEventPublisher

스프링은 옵저버 패턴의 구현체로써, 이벤트 프로그래밍에 필요한 인터페이스 ApplicationEventPublisher를 제공한다.

옵저버 패턴 : 어떠한 상태를 가지는 객체와 이 객체의 상태를 관찰하는 객체를 가진 패턴을 옵저버 패턴이라고 한다.

ApplicationContext

ApplicationContext는 ApplicationEventPublisher를 상속 받고, publishEvent를 통해 이벤트를 발생시킬 수 있다.

  • ApplicationContext extends ApplicationEventPublisher
    - publishEvent(ApplicationEvent event) : 이벤트 발생 함수

이벤트 기반 프로그래밍 방법

스크린샷 2021-03-24 오후 9 45 29

스프링 4.2 이전에는 ApplicationEvent를 상속받아 이벤트를 만들수 있었다.

스크린샷 2021-03-24 오후 9 48 43

이벤트를 발생시키기 위해선 ApplicationContext나 ApplicationEventPublisher의 publishEvent를 통해 이벤트를 발생시킨다.

스크린샷 2021-03-24 오후 9 49 34

발생시킨 이벤트를 처리하기 위해서는 ApplicationListener<이벤트>를 구현한 핸들러 클래스를 만들어 빈으로 등록해야한다.

스크린샷 2021-03-24 오후 9 50 11

어플리케이션을 실행시키면, publishEvent(..)가 실행되면서 MyEvent를 발생시키고,
등록되어있는 빈 중에서 MyEventHandler를 받아 onApplicationEvent가 실행되면서 이벤트에 있는 데이터를 가져온다.

  • 스프링 4.2 이후부터는 POJO 구조로, Spring의 코드 없이 이벤트 프로그래밍을 할 수 있다. 이벤트 객체는 ApplicationEvent를 상속받을 필요가 없고, MyEventHandler 객체는 ApplicationListener를 구현할 필요가 없어진다. 이벤트 핸들러 객체는 대신 @EventListener 어노테이션을 사용한다. 이렇게 하면, 코드에 Spring 코드가 들어가지 않게 되고, 이는 스프링의 목적 중 하나인 비침투성을 나타낸다. 깔끔한 POJO의 형태가 되어 테스트 및 유지보수가 더욱 쉬워진다.

스프링의 목적 중 하나 : 비침투성 ( 기술에 적용 사실이 코드에 직접 반영되지 않음)

스크린샷 2021-03-24 오후 10 01 02 스크린샷 2021-03-24 오후 10 01 14

다중 이벤트 핸들러

스프링에서는 MyEventHandler 클래스 처럼 또 다른 이벤트 핸들러를 등록해서 사용할 수 있다.

스크린샷 2021-03-24 오후 10 06 53 스크린샷 2021-03-24 오후 10 07 06 스크린샷 2021-03-24 오후 10 08 06

MyEventHandler 외에 AnotherHandler를 만들어 실행 시켜보면,
어떤 Handler가 먼저 실행되는 건지는 알 수 없으나 순차적으로 이벤트를 처리하는 것을 알 수 있다.
또한, 두 Handler가 서로 다른 스레드에서 이벤드를 처리하는 것이 아니라 두개 다 Main Thread에서 다뤄진다.

이때, 순서를 임의로 정해주고 싶다면 @Order를 사용하면 된다.

  • @Order(Ordered.HIGHEST_PRECEDENCE) : 가장 높은 우선 순위
스크린샷 2021-03-24 오후 10 14 43 스크린샷 2021-03-24 오후 10 14 53 스크린샷 2021-03-24 오후 10 15 14

위에서는 another 핸들러가 먼저 발생되었는데
이번에는 @Order 어노테이션으로 우선 순위를 설정하여 main이 먼저 실행된 것을 알 수 있다.

이벤트 핸들러 비동기적으로 사용

이벤트 핸들러를 비동기 적으로 사용하고 싶다면 이벤트 핸들러에 @Async 어노테이션을 선언하여 사용할 수 있다.

이런 경우 서로 다른 스레드풀에 따로 돌기 때문에 @Order 어노테이션이 더이상 의미가 없어지게 된다.

실제로는 이벤트 핸들러에 @Async 어노테이션만 추가해준다고해서 비동기적으로 동작하는 것은 아니다. 아래와 같이 main 함수를 가지고 있는 클래스에 @EnableAsnc 어노테이션을 추가해야 한다.

스크린샷 2021-03-24 오후 10 19 48 스크린샷 2021-03-24 오후 10 20 04 스크린샷 2021-03-24 오후 10 20 20 스크린샷 2021-03-24 오후 10 20 52

비동기적으로 실행되면서 두개의 다른 스레드에서 동작하는 것을 확인 할 수 있다.

스프링에서 기본적으로 제공하는 기본 이벤트

스프링에서 기본적으로 제공하는 ApplicationContext관련 이벤트가 있다.

  • ContextRefreshedEvent : ApplicationContext를 초기화 했거나 리프레시 했을 때 발생.

  • ContextStartedEvent : ApplicationContext를 start()하여 라이프사이클 빈들이 시작 신호를 받은 시점에 발생.

  • ContextStoppedEvent : ApplicationContext를 stop()하여 라이프사이클 빈들이 정지 신호를 받은 시점에 발생.

  • ContextClosedEvent : ApplicationContext를 close()하여 싱글톤 빈 소멸되는 시점에 발생.

  • RequestHandledEvent : HTTP 요청을 처리했을 때 발생.

@Component
public class MyEventHandler {

    @EventListener
    @Async
    public void occurEvent(MyEvent myEvent) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("My Event, data = "+ myEvent.getData());
    }

    @EventListener
    @Async
    public void occurEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("ContextRefreshedEvent");
    }

    @EventListener
    @Async
    public void occurEvent(ContextClosedEvent contextClosedEvent) {
        System.out.println(Thread.currentThread().toString());
        System.out.println("ContextClosedEvent");
    }
}

위와 같이 수정하면 ContextRefreshedEvent 발생하고 ApplicationContext가 종료될때 ContextClosedEvent 이벤트가 발생하는 것을 확인할 수 있다.

profile
부족함을 당당히 마주하는 용기

0개의 댓글