ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Event 동작 원리 - 1
    Spring Framework 2023. 9. 11. 00:01
    728x90

    개요

    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

     

    댓글

Designed by Tistory.