Spring Event
Spring Framework에서 제공하는 이벤트(Event) 객체가 있습니다.
이 객체는 `ApplicationContext`가 제공하는 기능의 일부인데요, Spring에 내장된 이벤트들을 이용해 특정 상황에서 원하는 동작을 수행하도록 개발할 수 있는 기능입니다.
Spring Event 동작 원리
동작 과정
이벤트는 하나의 쓰레드에서 동작합니다. 즉, 동기적으로 동작합니다.
한 쓰레드 내부에서 발행자가 이벤트를 발행(publish)하면 수신자가 이벤트를 수신(listen)하여 이벤트를 처리합니다.
이벤트 발행자(Event Publisher)
a. ApplicationEventPublisher 에서 이벤트를 발행한다.
실제로 이벤트를 발행하는 코드를 작성할 땐 `ApplicationEventPublisher`를 주입해 사용합니다.
`ApplicationEventPublisher`는 함수형 인터페이스로 아래 코드로 되어있는 것을 확인할 수 있습니다.
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
void publishEvent(Object event);
}
b. AbstractApplicationContext 도 이벤트를 발행할 수 있다.
`ApplicationEventPublisher` 인터페이스를 상속받는 `ApplicationContext` 인터페이스에 의해 이벤트를 발행할 수도 있습니다.
`ApplicationContext`의 구현체는 위의 이미지처럼 `AbstractApplicationContext`이며, `publishEvent()` 메서드 내부에는 `applicationEventMulticaster`에게 이벤트 발행을 위임합니다.
c. 구현체: SimpleApplicationEventMulticaster
별다른 설정이 없으면 `applicationEventMulticaster`에 실제 주입되는 구현체는 `SimpleApplicationEventMulticaster`입니다.
`multicastEvent()`는 위의 이미지에서 `for loop` 부분을 보면 모든 수신자들에게 이벤트를 전달합니다.
`for loop`가 동작하기 전 `getTaskExcutor()`에 의해 가져오는 excutor가 `null`이면 현재 실행 중인 쓰레드에서 동작하는 것을 확인할 수 있습니다. (동기적으로 수행)
따라서 `SimpleApplicationEventMulticaster`에 원하는 `TaskExecutor`를 주입하면 비동기로 동작할 수도 있습니다.
`for loop` 내부에서 `invokeListener()`를 통해 리스너에게 이벤트를 처리하도록 하는 것을 확인했습니다.
`invokeListener()`는 다시 내부에서 `doInvokeListener()`를 호출합니다.
이벤트 수신자(Event Listener)
a. ListenerRetreiver가 이벤트 수신자를 관리한다.
발행자(event publisher)는 수신자(event listener)들을 어떻게 가져오는 걸까요?
`SimpleApplicationEventMulticaster`는 `AbstractApplicationEventMulticaster`를 상속했습니다.
`AbstractApplicationEventMulticaster`는 이벤트 수신자들을 찾는 것을 `ListenerRetreiver`에게 위임합니다.
아래 이미지에서 ConcurrentHashMap에 저장되어 있는 `CachedListenerRetreiver`를 봐주세요.
`CachedListenerRetreiver`가 이벤트 수신자들을 갖고있는 핵심입니다.
반복되는 이벤트들을 발행할 때, 이벤트 수신자들을 매번 찾으면 비효율적입니다.
그래서 `ConcurrentHashMap` 자료 구조로 이벤트 수신자들을 캐싱합니다. (정확하게는 `CachedListenerRetriever`가 관리)
만약 자료 구조에 `CachedListenerRetriever`가 존재하지 않으면 `CachedListenerRetriever`를 먼저 생성합니다.
이미 캐시되어 있으면 바로 이벤트 수신자들을 가져옵니다.
b. ListenerRetriever는 Bean Factory로부터 이벤트 수신자들을 가져온다.
`CachedListenerRetriever`가 이벤트 수신자를 가져오는 부분입니다.
반복문 안에서 `BeanFactory`로부터 이벤트 수신자 Bean들을 가져와 `ArrayList`에 담는 것을 확인할 수 있습니다.
추가로, `AnnotationAwareOrderComparator.sort(allListeners);` 부분에서 이벤트 수신자가 여러 개 있을 때 `@Order()` 애너테이션에 의해 순서대로 수행할 수 있도록 정렬을 수행하는 것도 확인할 수 있습니다.
c. 이벤트 수신자(Event Linstener) 등록하는 방법
Spring 4.2 버전부터 `@EventListener` 애너테이션을 이용해 메서드를 이벤트 수신자로 등록할 수 있습니다.
`AbstractApplicationContext`에 의해 Bean들이 등록되는 과정에서 `DefaultEventListenerFactory`의 `createApplicationListener()`를 통해 `ApplicationListener(ApplicationListenerMethodAdapter)`로 변환되어 `ListenerRetreiver`에 의해 관리됩니다.
'Spring' 카테고리의 다른 글
[Spring] MockMvc 사용 시 Page 인터페이스의 직렬화 문제 (0) | 2024.12.31 |
---|---|
[Spring] 회원탈퇴 시 Kakao OAuth2 연결끊기: REST API로 연결끊기 (OpenFeign) (2) | 2024.10.30 |
[Spring] 직렬화/역직렬화 시 'is' prefix 가 안붙는 이유 (1) | 2024.10.30 |
[Spring Batch] ItemWriter 가 List<T> 를 전달받으려면? (Spring Batch 5) (0) | 2024.07.01 |
[Spring] Response DTO 직렬화 문제 (0) | 2024.06.13 |