관련 글은 Spring Event의 동작을 한번 소스를 까면서 동작을 이해하는 과정을 다룹니다.
Spring Event 동작 흐름은 어떻게 될까?
ApplicationEventPulisher의 이벤트를 발행 -> ApplicationEventMulticaster에 의해 구독(모니터링)된 이벤트 리스너들을 호출하게 됩니다.
동작 흐름을 코드로 따라가 보자
1. ApplicationEventPulisher에 이벤트를 발행
2. ApplicationEventPulisher에서 옵저버 역할을 하는 ApplicationEventMulticaster에 일을 시킵니다.
3. ApplicationEventMulticaster (구현체 SimpleApplicationEventMulticaster)
getApplicationListeners를 통해 이벤트가 등록된 리스너들을 찾고 리스너를 수행하게 됩니다.
4. 이벤트 리스너들이 호출 되는 과정
- 4.1 에러 핸들러가 있으면 리스너의 에러가 발생 시 에러를 핸들링 없다면 리스너를 수행해줌
- 4.2 ApplicationListener.onApplicationEvent를 호출해 준다.
여기서 해당 이벤트를 받는 리스너들의 순서는 어떻게 결정될 까에 대해서 궁금해지게 됩니다.
해답은 3번에 설명했던 [ getApplicationListeners ] 메서드에 [ retrieveApplicationListeners ] 메서드가 정답이 있습니다.
이벤트 리스너의 순서
순서를 결정하는 retrieveApplicationListeners 메서드
AnnotationAwareOrderComparator.sort를 통해 순서를 정렬하게 됩니다.
내용을 보아하니 @Order 어노테이션을 이용하여 정렬함을 알 수가 있습니다.
검증해보기
- 리스너 선언
public class OrderedEventListener {
@Order(2)
@EventListener(OrderedEchoEvent.class)
public void twoEvent(OrderedEchoEvent event) {
log.info("두번째 이벤트 {}", event.getMessage());
}
@Order(1)
@EventListener(OrderedEchoEvent.class)
public void oneEvent(OrderedEchoEvent event) {
log.info("첫번째 이벤트 {}", event.getMessage());
}
@Order(3)
@EventListener(OrderedEchoEvent.class)
public void threeEvent(OrderedEchoEvent event) {
log.info("세번째 이벤트 {}", event.getMessage());
}
}
- 순서에 맞게 나옵니다.
@Order 어노테이션을 선언을 안 한다면?
- 작성 코드
public class OrderedEventListener {
@EventListener(OrderedEchoEvent.class)
public void twoEvent(OrderedEchoEvent event) {
log.info("두번째 이벤트 {}", event.getMessage());
}
@EventListener(OrderedEchoEvent.class)
public void oneEvent(OrderedEchoEvent event) {
log.info("첫번째 이벤트 {}", event.getMessage());
}
@EventListener(OrderedEchoEvent.class)
public void threeEvent(OrderedEchoEvent event) {
log.info("세번째 이벤트 {}", event.getMessage());
}
}
- 메서드 선언순으로 나오는 것을 확인이 가능합니다.
(번외) 순서 중에 지연이 되는 메서드(블록킹)된 부분이 있다면 어떻게 동작할까?
@EventListener(OrderedEchoEvent.class)
public void oneEvent(OrderedEchoEvent event) {
log.info("첫번째 이벤트 {}", event.getMessage());
}
@EventListener(OrderedEchoEvent.class)
public void twoEvent(OrderedEchoEvent event) throws InterruptedException {
//10초 지연
TimeUnit.SECONDS.sleep(10);
log.info("두번째 이벤트 {}", event.getMessage());
}
@EventListener(OrderedEchoEvent.class)
public void threeEvent(OrderedEchoEvent event) {
log.info("세번째 이벤트 {}", event.getMessage());
}
- 결과 (두 번째가 10초가 지연되면서 세 번째 이벤트는 대기 상태가 됨)
해당 결과는 다른 이벤트 리스너에 의해서 영향을 줄 수 있음을 의미합니다.
주의해서 사용이 필요해 보입니다.
느낀 점
정리하는데 시간은 오래 걸렸지만 실제로 코드를 직접 돌려면서 확인해 보니 세부 사항을 자세히 들여 볼 수 있는 계기가 되었습니다.
스프링 이벤트는 옵저버 패턴을 사용하는것으로 확인 되었고,
축약된 부분이 있긴 했지만 색 다른 경험이었고 같은 이벤트 한에서 비동기 설정을 안 하면 순서나 메서드 선언에 따라 동기적으로 호출되는 것을 알 수 있었습니다.
동기적인 작성은 앞에 이벤트 리스너의 영향(블록킹)의 영향을 받을 수 있다는 점을 알았고 주의해서 작성해야겠구나 생각이 들었습니다.
관련코드는 https://github.com/beng9re/spring-event에서 확인이 가능합니다.