Spring Framework

Spring Event 동작 원리 - 1

Junuuu 2023. 9. 11. 00:01
반응형

개요

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