ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Event 동작 원리 - 2
    Spring Framework 2023. 9. 12. 00:01

    [1] Spring Event 동작 원리 - 1

    이전 포스팅에 이어 invokeListener에 대해 알아보고자 합니다.

     

    InvokeListener 메서드

    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    	try {
    		listener.onApplicationEvent(event);
    	}
    }

    publishEvent가 발생한 후 메서드를 타고 들어가면 doInvokeListener 메서드가 호출되고 해당 메서드는 onApplicationEvent 메서드를 호출합니다.

     

    어떻게 수많은 Entity 중 특정 Entity를 Consume 할 때만 실행될까?

     

    onApplicationEvent 메서드를 타고들어가보면 ApplicationListener 인터페이스로 이동됩니다.

     

    ApplicationListener 인터페이스

    @FunctionalInterface
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
    	/**
    	 * Handle an application event.
    	 * @param event the event to respond to
    	 */
    	void onApplicationEvent(E event);
    
    
    	/**
    	 * Create a new {@code ApplicationListener} for the given payload consumer.
    	 * @param consumer the event payload consumer
    	 * @param <T> the type of the event payload
    	 * @return a corresponding {@code ApplicationListener} instance
    	 * @since 5.3
    	 * @see PayloadApplicationEvent
    	 */
    	static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
    		return event -> consumer.accept(event.getPayload());
    	}
    
    }

    디버깅 포인트를 찍고 실제로 호출해보면 해당 인터페이스를 ApplicationListenerMethodAdapter가 구현하고 있습니다.

     

    ApplicationListenerMethodAdapter 클래스

    public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
    	@Override
    	public void onApplicationEvent(ApplicationEvent event) {
    		processEvent(event);
    	}
        
    	public void processEvent(ApplicationEvent event) {
    		Object[] args = resolveArguments(event);
    		if (shouldHandle(event, args)) {
    			Object result = doInvoke(args);
    			if (result != null) {
    				handleResult(result);
    			}
    			else {
    				logger.trace("No result object given - no result to handle");
    			}
    		}
    	}    
    }

    onApplicationEvent 메서드를 구현하고 있습니다.

    내부적으로 processEvent를 호출하고 있습니다.

     

    resloveArguments 메서드

    @Nullable
    protected Object[] resolveArguments(ApplicationEvent event) {
    	ResolvableType declaredEventType = getResolvableType(event);
    	if (declaredEventType == null) {
    		return null;
    	}
    	if (this.method.getParameterCount() == 0) {
    		return new Object[0];
    	}
    	Class<?> declaredEventClass = declaredEventType.toClass();
    	if (!ApplicationEvent.class.isAssignableFrom(declaredEventClass) &&
    		event instanceof PayloadApplicationEvent<?> payloadEvent) {
    		Object payload = payloadEvent.getPayload();
    		if (declaredEventClass.isInstance(payload)) {
    			return new Object[] {payload};
    		}
    	}
    	return new Object[] {event};
    }

    지정된 ApplicationEvent를 consume 할 메서드들을 확인합니다.

    적절한 argument를 찾을 수 없는 경우에 null을 반환합니다. (declaredEventType이 없는 경우!)

     

    getResolvableType 메서드

    @Nullable
    private ResolvableType getResolvableType(ApplicationEvent event) {
    	ResolvableType payloadType = null;
    	if (event instanceof PayloadApplicationEvent<?> payloadEvent) {
    		ResolvableType eventType = payloadEvent.getResolvableType();
    		if (eventType != null) {
    			payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
    		}
    	}
    	for (ResolvableType declaredEventType : this.declaredEventTypes) {
    		Class<?> eventClass = declaredEventType.toClass();
    		if (!ApplicationEvent.class.isAssignableFrom(eventClass) &&
    			payloadType != null && declaredEventType.isAssignableFrom(payloadType)) {
    				return declaredEventType;
    		}
    		if (eventClass.isInstance(event)) {
    			return declaredEventType;
    		}
    	}
    	return null;
    }

    전달된 event의 타입을 가져오고, declaredEventTypes을 for문으로 순회하며 이미 정의된 메서드의 파라미터 타입과 일치하는 것인지 찾고 아닌 경우에는 null을 반환합니다.

     

    그러면 declaredEventTypes는 어떻게 가져올까?

    public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
    		this.beanName = beanName;
    		this.method = BridgeMethodResolver.findBridgedMethod(method);
    		this.targetMethod = (!Proxy.isProxyClass(targetClass) ?
    				AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
    		this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
    
    		EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
    		this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
    		this.condition = (ann != null ? ann.condition() : null);
    		this.order = resolveOrder(this.targetMethod);
    		String id = (ann != null ? ann.id() : "");
    		this.listenerId = (!id.isEmpty() ? id : null);
    	}

    ApplicationListenerMethodAdapter 생성자 부분을 보면 declearedEventTypes를 받아오는데 디버깅을 찍고 올라가 보면 EventListenerMethodProcesser에서 processBean을 수행하는 과정 중에 호출되며 세팅이 일어납니다.

     

    EventListenerMethodProcesser 클래스는 EventListener들을 등록해주는 클래스이며, 하나의 EventListener 빈을 등록하며 이때 사용되는 ApplicationEvent 인자를 declaredEventTypes를 등록해 줍니다.

     

    다음 포스팅으로는 EventListener가 어떻게 등록되는지 알아보겠습니다.

     

     

    참고자료

    https://blog.naver.com/gngh0101/222040857815

     

    댓글

Designed by Tistory.