-
Spring Event 동작 원리 - 1Spring Framework 2023. 9. 11. 00:01728x90
개요
Spring이 제공하는 EventListener를 사용하다가 문득 Spring Event는 어떻게 동작하는 건지 궁금했었습니다.
추후에 비동기를 위해서는 @Async를 활용하는 대신에 ApplicationEventMulticaster를 사용할수도 있다는 이야기를 듣고 찾아보다가 Spring Event의 동작원리를 다룬 글을 보고 이 기회에 이해해 보고자 정리해보려 합니다.
Application Context
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
Spring Event를 이야기 하는데? Application Context는 왜 나오는 거지..
ApplicationContext란 애플리케이션에 대한 구성정보를 제공하는 인터페이스입니다.
흔히 Spring 컨테이너에 빈들을 가져오기위해 사용해 본 경험이 있을 수 있습니다.
Application Context를 보면 ApplicationEventPublisher를 상속받고 있습니다.
ApplicationEventPublisher에 대한 Java Docs를 읽어보면 등록된 listener들에게 event를 publish 할 수 있다고 적혀있습니다.
그 말은 즉슨.. ApplicationEventPublisher 대신 ApplicationContext를 주입받아 publishEvent 메서드를 사용할 수 있습니다.
하지만 명시적으로 ApplicationEventPublisher를 주입받는 것이 더 명확하고, 동료 개발자들도 이해하기 쉬울 것 같습니다.
AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { @Override public void publishEvent(Object event) { publishEvent(event, null); } protected void publishEvent(Object event, @Nullable ResolvableType eventType) { .... .... getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); .... .... } }
ApplicationContext 인터페이스의 구현체로 publishEvent의 구현체가 존재하는 클래스입니다.
주어진 이벤트를 모든 리스너에게 publish합니다.
getApplicationEventMulticaster()로 얻어온 ApplicationEventMulticaster에게 event처리를 위임합니다.
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException { if (this.applicationEventMulticaster == null) { throw new IllegalStateException("ApplicationEventMulticaster not initialized - " + "call 'refresh' before multicasting events via the context: " + this); } return this.applicationEventMulticaster; }
this.applicationEventMulticaster를 가져오고 있으며 별다른 설정이 없으면 기본 전략인 SimpleApplicationEventMulticaster를 사용합니다.
SimpleApplicationEventMulticaster는 어떻게 가져올까?
@Override public void refresh() throws BeansException, IllegalStateException { ... // Initialize event multicaster for this context. initApplicationEventMulticaster(); ... }
AbstractApplicationContext 클래스의 refresh 메서드는 Application scan이후에 호출되는 메서드입니다.
내부에서 initApplicationEventMulticaster 메서드를 호출하여 SimpleApplicationEventMulticaster를 생성합니다.
protected void initApplicationEventMulticaster() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isTraceEnabled()) { logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); } } else { this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isTraceEnabled()) { logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " + "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]"); } } }
천천히 읽어보면 applicationEventMulticaster(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)이란 이름을 가진 빈이 존재하는 경우에는 해당 빈을 사용하고 그렇지 않은 경우에는 SimpleApplicationEventMulticaster를 사용합니다.
SimpleApplicationEventMulticaster
ApplicationEventMulticaster 인터페이스의 구현체입니다.
해당 클래스의 multicastEvent가 핵심 메서드입니다.
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } }
getTaskExecutor를 통하여 Executor를 가져오고 null인지에 따라 분기를 수행합니다.
기본 전략은 null이며 Executor를 통해 실행시키고 싶다면 SimpleApplicationEvenMulticaster Bean에 Executor를 주입해 사용하면 됩니다.
코드를 자세하게 보면 invokeListener를 호출하는 부분은 if-else와 동일하며 executor를 통해 실행하느냐 그렇지 않느냐의 차이입니다.
for문을 살펴보면 모든 ApplicationListener들을 모두 가져와 event를 뿌려주는 invokeListener 메서드를 호출합니다.
invokeListener 메서드 분석
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } }
메서드를 타고 들어가 보면 ApplicationListener 인터페이스로 이동됩니다.
디버깅 포인트를 찍어보고 실제로 호출해 보면 해당 인터페이스를 ApplicationListenerMethodAdapter가 구현하고 있습니다.
ApplicationListenerMethodAdapter에 대해 자세한 건 다음 포스팅으로..
참고자료
https://blog.naver.com/gngh0101/222020512119
'Spring Framework' 카테고리의 다른 글
Spring Event 동작 원리 -3 (0) 2023.09.13 Spring Event 동작 원리 - 2 (0) 2023.09.12 SpringApplicationRunner란? (+ 동작과정) (0) 2023.09.08 놓치기 쉽지만 중요한 @Transactional Rollback (1) 2023.09.07 ServletRequest 여러번 읽기 - ContentCachingRequestWrapper (0) 2023.09.06