浅聊Spring的事件处理机制

Spring 事件处理基于 Java 观察者模式扩展。Spring 应用上下文中发布了各种事件,此外 Spring 还允许我们发送和处理自定义的事件,本篇将对 Spring 的事件机制使用及其实现进行详细介绍。

观察者模式

观察者模式作为设计模式的一种,当被观察者的状态发生变化,所有观察者都将得到通知。一个直观的例子如下,可视化界面中的按钮作为被观察者,为按钮添加观察者对象,当按钮被点击或按下时所有的观察者对象都将被通知到。

Java 自身对观察者模式具有两种实现,如下:

1、基于 Observable/Observer 对观察者模式的实现,Observable 作为被观察者,可以注册观察者 Observer,当 Observable 的状态发生变化时通知所有的 Observer,使用示例如下:

public class SpringEventDemo {

    public static void main(String[] args) {
        MyObservable observable = new MyObservable();
        observable.addObserver(new Observer() {
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("收到观察者状态改变的通知:" + arg);
            }
        });
        observable.setChanged();
        observable.notifyObservers("最新状态");
    }
}

class MyObservable extends Observable{

    @Override
    public synchronized void setChanged() {
        super.setChanged();
    }
}	

成功打印出:收到观察者状态改变的通知:最新状态

2、基于 EventObject/EventListener 对观察者模式的实现,EventObject 作为事件对象(被观察者状态),EventListener 作为事件监听器(观察者),当产生新的事件,事件监听器能够及时进行感知。Spring 的事件处理正是基于此进行扩展。

Spring 中的事件处理核心组件

先看 Spring 事件处理中的核心组件,这些核心组件是 Spring 对事件处理流程的抽象。

事件 ApplicationEvent

ApplicationEvent 是对 Java EventObject 的扩展,表示 Spring 中的事件,Spring 中的所有事件都要基于其进行扩展。其定义如下:

public abstract class ApplicationEvent extends EventObject {

    private final long timestamp;

    public ApplicationEvent(Object source) {
    	super(source);
    	this.timestamp = System.currentTimeMillis();
    }
    
    public final long getTimestamp() {
    	return this.timestamp;
    }

}

ApplicationEvent 只是记录了一个创建时的时间戳,Spring 中 ApplicationEvent 的主要实现如下:

Spring ApplicationEvent

上图中各 ApplicationEvent 主要应用场景如下:

  • ServletRequestHandledEvent:spring-web 模块的类,记录一些请求和响应的数据,当 Spring 处理完 HTTP 请求时发布此事件
  • PayloadApplicationEvent:spring-context 模块中的类,早期的 Spring 只支持发布 ApplicationEvent 类型的事件,自 Spring 4.2 开始可以发布非 ApplicationEvent 类型的事件时,此时事件参数将包装到 PayloadApplicationEvent ,然后再发布。
  • ApplicationContextEvent:Spring 应用上下文生命周期中发布的事件,对应不同的生命周期,其事件类型如下:
    • ContextStartedEvent:应用上下文启动。
    • ContextStoppedEvent:应用上下文停止。
    • ContextRefreshedEvent:应用上下文刷新。
    • ContextClosedEvent:应用上下文关闭。

事件监听器 ApplicationListener

ApplicationListener是Spring事件的监听器,可以用来接收上述中说明的 ApplicationEvent 事件,所有的监听器都必须实现该接口。该接口源码如下:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	
    // 处理事件
    void onApplicationEvent(E event);

}

有以下方式可以用于向 Spring 中添加 ApplicationListener

1、向 Spring 中注册 ApplicationListener 类型的 bean。示例如下:

@Component
public class MyListener implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("收到事件:" + event);
    }
}

2、使用 @EventListener 注解标注配置类中处理事件的方法,此时 Spring 将创建一个 ApplicationListener bean 对象,使用给定的方法处理事件。示例如下:

@Configuration
public class Config {

    @EventListener
    public void handleEvent(MyEvent event){
        System.out.println("收到事件:" + event);
    }
    
}

3、通过 ConfigurableApplicationContext#addApplicationListener 方法向 Spring 中手动添加事件处理器。示例如下。

public class App {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.addApplicationListener(new MyListener());
        context.refresh();
        context.close();
    }
    
}

事件发布器

事件发布器用于发布 ApplicationEvent 事件,发布后 ApplicationListener 才能监听到事件进行处理Spring 的事件发布器包括 ApplicationEventPublisher 和 ApplicationEventMulticaster 两种

  • 获取 ApplicationEventPublisher 可以通过 @Autowired 或 ApplicationEventPublisherAware 依赖注入。
  • ApplicationEventMulticaster 是 ApplicationEventPublisher 的底层实现,ApplicationEventMulticaster 的获取除了可以通过依赖注入,还可以通过依赖查找的方式。

通过 ApplicationEventPublisher 发布自定义事件的示例如下:

public class App {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.addApplicationListener(new MyListener());
        context.publishEvent(new MyEvent(context));
        context.refresh();
        context.close();
    }

}

由于 ApplicationContext 已经继承了 ApplicationEventPublisher ,因此可以直接使用发布事件。

Spring 应用上下文生命周期事件发布源码分析

前面提到 Spring 应用上下文的生命周期中会发布不同的 ApplicationContextEvent 事件,因此我们就从 Spring 应用上下文入手进行分析,后面的分析将会对 Spring 事件处理核心组件的使用进行解释。

Spring 应用上下文生命周期中涉及事件的部分代码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {

    @Override
    public void start() {
    	getLifecycleProcessor().start();
    	publishEvent(new ContextStartedEvent(this));
    }

    @Override
    public void refresh() throws BeansException, IllegalStateException {
    	synchronized (this.startupShutdownMonitor) {
    		// 这个方法会进行事件发布准备工作
    		prepareRefresh();
    	
    			... 省略部分代码
    			// Initialize event multicaster for this context.
    			initApplicationEventMulticaster();

    			... 省略部分代码
    			// Check for listener beans and register them.
    			registerListeners();

    			... 省略部分代码
    	}
    }

    @Override
    public void stop() {
    	getLifecycleProcessor().stop();
    	publishEvent(new ContextStoppedEvent(this));
    }

    @Override
    public void close() {
    	synchronized (this.startupShutdownMonitor) {
    		doClose();
    		... 省略部分代码
    	}
    }

    protected void doClose() {
    	... 省略部分代码
    		try {
    			// Publish shutdown event.
    			publishEvent(new ContextClosedEvent(this));
    		} catch (Throwable ex) {
    			logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
    		}

    		... 省略部分代码
    	}
    }
}

Spring生命周期中,AbstractApplicationContext的start、stop、close直接进行事件发布,而发布事件的准备工作放在了 refresh 方法。先把注意力放在 start、stop、close 方法调用的 publishEvent 方法,跟踪源码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    @Override
    public void publishEvent(ApplicationEvent event) {
    	publishEvent(event, null);
    }    
    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    	Assert.notNull(event, "Event must not be null");    
    	// 先将事件参数转换为 ApplicationEvent 
    	ApplicationEvent applicationEvent;
    	if (event instanceof ApplicationEvent) {
    		applicationEvent = (ApplicationEvent) event;
    	} else {
    		applicationEvent = new PayloadApplicationEvent<>(this, event);
    		if (eventType == null) {
    			eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
    		}
    	}    
    	if (this.earlyApplicationEvents != null) {
    		// 调用 refresh 初始化 earlyApplicationEvents 后,BeanFactoryPostProcessor 可能发布事件
    		this.earlyApplicationEvents.add(applicationEvent);
    	} else {
    		// 刷新准备工作完成,事件监听器已向 Spring 注册
    		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    	}    
    	// 使用父上下文继续发布事件
    	if (this.parent != null) {
    		if (this.parent instanceof AbstractApplicationContext) {
    			((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
    		} else {
    			this.parent.publishEvent(event);
    		}
    	}
    }
}

通过源码,我们发现 publishEvent 先将事件参数转换为 ApplicationEvent ,如果 earlyApplicationEvents 存在则直接向其添加 ApplicationEvent ,否则获取 ApplicationEventMulticaster 然后进行发布事件。此外当前 ApplicationContext 发布事件后还会通过父 ApplicationContext 发布事件。这里不仅产生一个疑问,earlyApplicationEvents 什么时候不为 null,而什么时候又转换为 null 了呢?它又表示什么含义?通过查询源码发现其初始化位置如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {

    // 添加到当前上下文的事件监听器,可能在刷新前或刷新后添加
    private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();    
    // 刷新当前应用上下文注册的事件监听器
    @Nullable
    private Set<ApplicationListener<?>> earlyApplicationListeners;    
    // 当前应用上下文刷新添加添加的事件
    @Nullable
    private Set<ApplicationEvent> earlyApplicationEvents;
    
    @Nullable
    private ApplicationEventMulticaster applicationEventMulticaster;
    
    protected void prepareRefresh() {
    	... 省略部分代码
    	if (this.earlyApplicationListeners == null) {
    		// 首次 fresh,把刷新前添加的监听器添加到 earlyApplicationListeners
    		this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    	} else {
    		// 再次 fresh,把之前添加的监听器添加到 applicationListeners
    		this.applicationListeners.clear();
    		this.applicationListeners.addAll(this.earlyApplicationListeners);
    	}
    	this.earlyApplicationEvents = new LinkedHashSet<>();
    }    
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
    	Assert.notNull(listener, "ApplicationListener must not be null");
    	if (this.applicationEventMulticaster != null) {
    		this.applicationEventMulticaster.addApplicationListener(listener);
    	}
    	this.applicationListeners.add(listener);
    }
}

earlyApplicationEvents 初始化位置正是处于 refresh 方法准备初始化时调用的 prepareRefresh 方法。此外,prepareRefresh 还对 applicationListeners 初始化。 由于 ConfigurableApplicationContext 提供了 addApplicationListener 方法,因此可以在上下文实例化后的任意时刻添加 ApplicationListener,earlyApplicationListeners 作用便是保存早期向上下文中添加的 ApplicationListener。首次 refresh 时 earlyApplicationListeners 为 null,prepareRefresh 方法会把 AbstractApplicationContext 持有的 applicationListeners 添加到 earlyApplicationListeners ,再次 refresh 时 Spring 又巧妙的把 earlyApplicationListeners 存放到了 applicationListeners 中。

prepareRefresh 方法调用结束后由于可能存在 BeanFactoryPostProcessor、BeanPostProcessor ,因此可能会在这些扩展点发布事件,此时 earlyApplicationEvents 不为 null,因此 publishEvent 方法发布事件就会直接向 earlyApplicationEvents 中添加 ApplicationEvent。

refresh 方法中,调用 prepareRefresh 方法完成刷新的准备工作后,然后就会初始化 ApplicationEventMulticaster,初始化源码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
		
    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 时,如果已经存在特定名称 ApplicationEventMulticaster 类型的 bean,则会直接初始化 AbstractApplicationContext 中的成员变量 applicationEventMulticaster ,否则使用 SimpleApplicationEventMulticaster 初始化,并且向 BeanFactory 中注册,这也是为什么能够 通过依赖查询 SimpleApplicationEventMulticaster 的原因。

初始化 ApplicationEventMulticaster 后,Spring 会对 ApplicationListener 进行注册,相关源码如下:

protected void registerListeners() {
    // 将当前上下文中保存的 ApplicationListener 添加到 ApplicationEventMulticaster
    for (ApplicationListener<?> listener : getApplicationListeners()) {
    	getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 将 Spring 中 ApplicationListener 类型的 bean 添加到 ApplicationListener 
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
    	getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // 发布早期的事件
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
    	for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
    		// 广播当前应用上下文刷新前发布的事件
    		getApplicationEventMulticaster().multicastEvent(earlyEvent);
    	}
    }
}

注册 ApplicationListener 其实就是分别将 AbstractApplicationContext 中保存的 ApplicationListener 和 ApplicationListener 类型的 bean 添加到 ApplicationEventMulticaster。这时 Spring 才能够正常发布事件,然后 Spring 将 earlyApplicationEvents 置为 null,将早期发布的事件进行发布,整个发布事件流程至此结束。

ApplicationEventMulticaster 事件发布源码分析

通过前面的分析,我们已经知道了事件的发布依托于 ApplicationEventMulticaster ,更确切的说默认情况下依托于 SimpleApplicationEventMulticaster,这节便进行分析。先看 ApplicationEventMulticaster 接口的定义,拿到它之后我们能做什么?

public interface ApplicationEventMulticaster {
	
    // 添加指定的事件监听器
    void addApplicationListener(ApplicationListener<?> listener);
    // 添加给定 bean 名称的事件监听器
    void addApplicationListenerBean(String listenerBeanName);    
    // 移除给定的事件监听器
    void removeApplicationListener(ApplicationListener<?> listener);
    // 移除给定 bean 名称的事件监听器
    void removeApplicationListenerBean(String listenerBeanName);    
    // 移除所有的事件监听器
    void removeAllListeners();    
    // 广播事件
    void multicastEvent(ApplicationEvent event);
    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

ApplicationEventMulticaster 接口中,方法的定义大致可以分为三类,事件监听器的添加、事件监听器的异常、广播事件。我们将重点放在事件的广播上,分析实现类 SimpleApplicationEventMulticaster 的广播方法。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

    @Nullable
    private Executor taskExecutor;
    
    @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);
    		}
    	}
    }
}

广播方法较为简洁,先取线程池,然后拿到所有的能处理给定事件类型的 ApplicationListener 调用监听器方法,获取 ApplicationListener 时使用了 Spring 处理泛型的 ResolvableType。这里线程池 Executor 的获取通过 SimpleApplicationEventMulticaster 提供的方法获取,由于和具体的实现耦合,因此不建议直接进行使用。再看事件处理方法调用的部分。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

    @Nullable
    private ErrorHandler errorHandler;
    
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    	ErrorHandler errorHandler = getErrorHandler();
    	if (errorHandler != null) {
    		try {
    			doInvokeListener(listener, event);
    		}
    		catch (Throwable err) {
    			errorHandler.handleError(err);
    		}
    	}
    	else {
    		doInvokeListener(listener, event);
    	}
    }
}

调用事件处理器方法时先获取错误处理器,这里也是通过编程设置,不建议直接使用。如果能够获取到则调用事件处理方法出现异常时通过错误处理器进行处理异常。至此,ApplicationEventMulticaster 事件发布分析完成。

@EventListener 实现源码分析

在前面的源码分析中,我们发现 Spring 通过 ApplicationEventMulticaster 管理 ApplicationListener,从而发送事件,其中并没有对 @EventListener 进行处理。事实上 Spring 使用 EventListenerMethodProcessor 对 @EventListener 注解进行处理。核心源码如下:

public class EventListenerMethodProcessor
		implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {

    @Override
    public void afterSingletonsInstantiated() {
    	ConfigurableListableBeanFactory beanFactory = this.beanFactory;
    	Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
    	String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
    	for (String beanName : beanNames) {
    		if (!ScopedProxyUtils.isScopedTarget(beanName)) {
    			Class<?> type = null;
    			try {
    				type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
    			}
    			catch (Throwable ex) {
    				... 省略部分代码
    			}
    			if (type != null) {
    				... 省略部分代码
    				processBean(beanName, type);	
    				... 省略部分代码
    			}
    		}
    	}
    }
}

EventListenerMethodProcessor 实现接口 SmartInitializingSingleton,Spring 应用上下文 refresh 最后的阶段,当单例 bean 加载完成,就会调用 SmartInitializingSingleton#afterSingletonsInstantiated 方法。EventListenerMethodProcessor 中,该方法获取所有 bean 的原始类型,然后调用 processBean 方法,继续进行跟踪:

public class EventListenerMethodProcessor
		implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {

	private void processBean(final String beanName, final Class<?> targetType) {
		if (!this.nonAnnotatedClasses.contains(targetType) &&
				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
				!isSpringContainerClass(targetType)) {
			// 选择 @EventListener 标注的方法
			Map<Method, EventListener> annotatedMethods = null;
			try {
				annotatedMethods = MethodIntrospector.selectMethods(targetType,
						(MethodIntrospector.MetadataLookup<EventListener>) method ->
								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
			}
			catch (Throwable ex) {
				... 省略部分代码
			}

			if (CollectionUtils.isEmpty(annotatedMethods)) {
				this.nonAnnotatedClasses.add(targetType);
				if (logger.isTraceEnabled()) {
					logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
				}
			}
			else {
				// Non-empty set of methods
				ConfigurableApplicationContext context = this.applicationContext;
				Assert.state(context != null, "No ApplicationContext set");
				List<EventListenerFactory> factories = this.eventListenerFactories;
				Assert.state(factories != null, "EventListenerFactory List not initialized");
				for (Method method : annotatedMethods.keySet()) {
					for (EventListenerFactory factory : factories) {
						if (factory.supportsMethod(method)) {
							// 如果 EventListenerFactory 支持给定的方法,则使用 EventListenerFactory  创建 ApplicationListener
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
							ApplicationListener<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
							// 将创建的 ApplicationListener 添加到上下文
							context.addApplicationListener(applicationListener);
							break;
						}
					}
				}
				... 省略部分代码
			}
		}
	}
}

processBean 方法从所有 bean 中选择标注了 @EventListener 注解的方法,然后使用 EventListenerFactory 创建 ApplicationListener,并将 ApplicationListener 添加到上下文中。

处理注解的处理器全部在 AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry, Object) 方法中进行注册,Spring AnnotatedBeanDefinitionReader 在读取注解信息时会进行调用,事件处理相关源码如下:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
		BeanDefinitionRegistry registry, @Nullable Object source) {

	...省略部分代码
	
	Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

	...省略部分代码

	// 注册 EventListenerMethodProcessor
	if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
		RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
		def.setSource(source);
		beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
	}

	// 注册 DefaultEventListenerFactory
	if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
		RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
		def.setSource(source);
		beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
	}

	return beanDefs;
}

Spring 同时对 EventListenerMethodProcessor 和 DefaultEventListenerFactory 进行了注册,从而可以处理 @EventListener 注解。

 

参考:

 

posted @ 2022-01-13 13:41  残城碎梦  阅读(522)  评论(0编辑  收藏  举报