Spring Event 동작 원리 - 1
개요
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