Spring事件监听机制源码解析

1 什么是事件监听机制

前一篇文章简述了观察者模式,事件监听机制是对观察者模式的另一种实现,本质上均是多个对象(观察者/监听器)依赖于一个对象(被观察者/事件)的状态的关系。
观察者模式中主要有角色:观察者(Observer)、被观察者(Subject,Obserable).
事件监听机制中的主要角色:事件(Event),事件源(Source),事件发布器(EventPublisher),监听器(Listener)


两者主要区别是什么?
观察者模式中:被观察者(Subject)需要主动维护观察者的引用集合,并通知观察者状态变化
事件监听机制中:事件发布器承担了维护观察者的引用集合,并通知观察者状态变化的功能。

观察者模式事件监听机制
观察者(Observer)监听器(Listener)
被观察者(Subject,Obserable)事件(Event),事件源(Source)
被观察者(Subject,Obserable)中负责维护主题与观察者映射关系以及在自身状态改变时通知观察者的职责事件发布器(EventPublisher)

事件源(Source):产生事件的源头,也即发生事件的组件。例如按钮(事件源)和点击(事件)的关系
事件(Event):对一个组件的某种同类型操作动作的集合.
监听器(Listener):首先,监听组件,观察组件有没有发生某类事件;其次如果监听的组件发生了某类事件,则调用对应的动作处理方法立刻处理这个事件。
这些角色/组件之间的关系如图所示(网图侵删):
在这里插入图片描述
事件发布器(EventPublisher)组件在图中主要表现为:将监听器注册到事件源上,在必要的时候调用事件所对应的监听器处理事件。

2 JDK的事件监听机制

jdk中为实现事件监听机制提供了两个基础类:EventObject和EventListener,均是java.util包的类。

2.1 EventObject(事件)

EventObject:表示事件基类,该EventObject定义了事件对象,其中source表示事件源(或者任意其它对象),自定义事件扩展该类,源码如下:

package java.util;
/**
 * <p>
 * The root class from which all event state objects shall be derived.
 * <p>
 * All Events are constructed with a reference to the object, the "source",
 * that is logically deemed to be the object upon which the Event in question
 * initially occurred upon.
 *
 * @since JDK1.1
 */

public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    /**
     * 表示事件最初发生的地方(事件源)
     * The object on which the Event initially occurred.
     */
    protected transient Object  source;

    /**
     * Constructs a prototypical Event.
     *
     * @param    source    The object on which the Event initially occurred.
     * @exception  IllegalArgumentException  if source is null.
     */
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }

    /**
     * The object on which the Event initially occurred.
     *
     * @return   The object on which the Event initially occurred.
     */
    public Object getSource() {
        return source;
    }

    /**
     * Returns a String representation of this EventObject.
     *
     * @return  A a String representation of this EventObject.
     */
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}

2.2 EventListener(事件监听器)

EventListener:事件监听器,是一个标记接口,自定义监听器扩展该监听器,源码如下:

package java.util;

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

2.3 EventPublisher(事件发布器)

该类jdk没有提供相应的实现,需要我们自己手动编写。

2.4 基于jdk的事件机制示例

该示例以过红绿灯为例(类似前文的观察者模式)

2.4.1 事件源(红绿灯)

//事件源
public class Source {
    public String color = "red";
}

2.4.1 事件

public class SwitchLightEvent extends EventObject {

    public SwitchLightEvent(Object source) {
        super(source);
    }
}

2.4.2 事件监听器

自定义事件监听器接口

public interface ObjectListener extends EventListener {
    public void deal(SwitchLightEvent event);
}

自定义监听器实例

public class CarListener implements ObjectListener{

    /**
     * 监听器接收到事件对象也可以通过getSource获得事件源对象
     * 这样可以根据事件及事件源来执行相应的处理逻辑
     * @param event 事件对象
     */
    public void deal(SwitchLightEvent event) {
        System.out.println(event.getSource()+"  Car Run");
    }
}
public class ManListener implements ObjectListener {

    public void deal(SwitchLightEvent event) {
        System.out.println(event.getSource()+"  Man Run");
    }
}

2.4.3 事件发布器

public class SwitchLightEventPublisher {
    //观察者对象引用的集合
    private List<ObjectListener> listeners = new ArrayList<ObjectListener>();

    //注册监听器
    public void attach(ObjectListener listener) {
        if (listener == null) {
            return;
        }
        listeners.add(listener);
    }

    //删除监视器
    public void detach(ObjectListener listener) {
        Iterator<ObjectListener> iterator = listeners.iterator();
        while (iterator.hasNext()) {
            ObjectListener next = iterator.next();
            if (listener.equals(next)) {
                iterator.remove();
            }
        }
    }

    //发布红绿灯切换事件
    public void publishEvent(SwitchLightEvent event) {
        for (ObjectListener observer : listeners) {
            observer.deal(event);
        }
    }
}

2.4.4 测试代码

public class Client {
    public static void main(String[] args) {
        //事件源
        Source source = new Source();
        //红绿灯切换事件
        SwitchLightEvent switchLightEvent = new SwitchLightEvent(source);
        //事件监听器
        ObjectListener carListener = new CarListener();
        ObjectListener manListener = new ManListener();

        //事件发布器
        SwitchLightEventPublisher publisher = new SwitchLightEventPublisher();
        publisher.attach(carListener);
        publisher.attach(manListener);
		//发布事件
        publisher.publishEvent(switchLightEvent);
    }
}

3 Spring的事件监听机制

Spring的事件监听机制基于JDK实现,大大简化了开发难度。具体

3.1 Spring事件(ApplicationEvent)

spring事件继承自EventObject
在这里插入图片描述

public abstract class ApplicationEvent extends EventObject {

	/** use serialVersionUID from Spring 1.2 for interoperability */
	private static final long serialVersionUID = 7099057708183571937L;

	/** System time when the event happened */
	private final long timestamp;
	/**
	 * Create a new ApplicationEvent.
	 * @param source the object on which the event initially occurred (never {@code null})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
	/**
	 * Return the system time in milliseconds when the event happened.
	 */
	public final long getTimestamp() {
		return this.timestamp;
	}

}

3.2 Spring事件监听器(ApplicationListener)

在这里插入图片描述
Spring事件监听器相较于JDK监听器多了一个泛型的约定,可以指定处理特定的事件类型。

3.3 Spring事件发布器(ApplicationEventPublisher )

Spring定义了时间发布其超类接口ApplicationEventPublisher ,我们熟知的ApplicationContext就实现了改接口,即ApplicationContext是一个事件发布器。具体的如何发布事件我们可以跟一下其中的源码进行了解。

/**
 * ApplicationContext的一个超类接口
 */
public interface ApplicationEventPublisher {

	/**
	 * 通知某个事件匹配的事件监听器
	 */
	void publishEvent(ApplicationEvent event);

	/**
	 * 通知某个事件匹配的事件监听器
	 */
	void publishEvent(Object event);
}

3.4 Spring事件机制小结

通过前面事件机制及示例和Spring基于JDK事件的扩展,我们大致了解到Spring事件的几个核心组件,ApplicationEvent,ApplicationListener,ApplicationEventPublisher 。接下来我们带着问题去学习Spring事件机制是怎么回事。

4 Spring事件监听机制源码分析

ApplicationEventPublisher 事件发布器是Spring 事件监听机制的核心,其中ApplicationContext作为其子接口,应该作为我们研究源码的入口。ApplicationContext的抽象实现类AbstractApplicationContext是所有Spring容器的基础。

4.1 AbstractApplicationContext如何注册事件监听器?

AbstractApplicationContext类的源码中有方法registerListeners(),这个注册的动作是在方法refresh()中被调用的,也就是在初始化容器的时候调用自动调用的。

	/**
	 * 注册监听器
	 */
	protected void registerListeners() {
		for (ApplicationListener<?> listener : getApplicationListeners()) {
		//注册监听器对象的实例,真正的注册事件监听器的对象是ApplicationEventMulticaster
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		//注册监听器对象的名称,真正的注册事件监听器的对象是ApplicationEventMulticaster
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// 发布某些事件earlyEventsToProcess 包含的事件
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (earlyEventsToProcess != null) {
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}

其中,getApplicationListeners()获得容器中所有的ApplicationListener对象

	public Collection<ApplicationListener<?>> getApplicationListeners() {
		return this.applicationListeners;
	}

其中,this.applicationListeners字段的write过程是在方法addApplicationListener()中。其中涉及到对象applicationEventMulticaster,该对象是作为ApplicationContext实现事件发布器的委托对象,真正的监听器注册,发送事件都是由该对象实现。

@Override
	public void addApplicationListener(ApplicationListener<?> listener) {
		Assert.notNull(listener, "ApplicationListener must not be null");
		if (this.applicationEventMulticaster != null) {
			this.applicationEventMulticaster.addApplicationListener(listener);
		}
		else {
			this.applicationListeners.add(listener);
		}
	}

继续跟代码可以查到方法addApplicationListener()被ApplicationListenerDetector中的postProcessAfterInitialization()方法调用。

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.applicationContext != null && bean instanceof ApplicationListener) {
			// potentially not detected as a listener by getBeanNamesForType retrieval
			Boolean flag = this.singletonNames.get(beanName);
			if (Boolean.TRUE.equals(flag)) {
				//添加监听器
				this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
			}
			else if (Boolean.FALSE.equals(flag)) {
				if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
					// inner bean with other scope - can't reliably process events
					logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
							"but is not reachable for event multicasting by its containing ApplicationContext " +
							"because it does not have singleton scope. Only top-level listener beans are allowed " +
							"to be of non-singleton scope.");
				}
				this.singletonNames.remove(beanName);
			}
		}
		return bean;
	}

ApplicationListenerDetector实现了BeanPostProcessor接口,可以在容器级别对所有bean的生命周期过程进行增强。这里主要是为了能够在初始化所有bean后识别出所有的事件监听器bean并将其注册到事件发布器中。具体可以参看refresh()方法中的prepareBeanFactory(beanFactory)过程,其中就注册ApplicationListenerDetector对象。

4.2 Spring如何发布事件?

AbstractApplicationContext类的方法中发布事件的过程源码:

protected void publishEvent(Object event, ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Publishing event in " + getDisplayName() + ": " + event);
		}

		// 获得事件对象
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<Object>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
			}
		}

		// 事件懒加载,等一段事件(以后)再发布
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
		//真正发布事件的是ApplicationEventMulticaster对象
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// 将事件对象传播到父类进行发布操作
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

4.3 ApplicationEventMulticaster

从以上的源码中我们看到不论是监视器注册,还是事件的发布均由ApplicationEventMulticaster作为ApplicationContext的委托方进行实际的操作,接下来,我们分析一下ApplicationEventMulticaster就是什么,什么时候加载的,如何发布注册监听器,如何发布事件的?

4.3.1 ApplicationEventMulticaster的加载时机

AbstractApplicationContext的字段中有如下定义:

/** Helper class used in event publishing */
	private ApplicationEventMulticaster applicationEventMulticaster;

我们跟一下该字段被Write的时机,发现是在initApplicationEventMulticaster()方法中,而该方法会在Spring容器初始化的时候被调用。

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.isDebugEnabled()) {
				logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isDebugEnabled()) {
				logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
						APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
						"': using default [" + this.applicationEventMulticaster + "]");
			}
		}
	}

逻辑很简单:
1.如果容器中存在ApplicationEventMulticaster该对象则直接获得该对象
2.若不存在ApplicationEventMulticaster对象则创建一个子类实例,
SimpleApplicationEventMulticaster。

4.3.2 ApplicationEventMulticaster注册事件监听器

当AbstractApplicationContext中调用registerListeners()方法时,或者注册事件监听器时,底层会使用ApplicationEventMulticaster的addApplicationListener()方法来完成注册事件监听器的工作,方法源码如下

	@Override
	public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.retrievalMutex) {
			// Explicitly remove target for a proxy, if registered already,
			// in order to avoid double invocations of the same listener.
			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
			if (singletonTarget instanceof ApplicationListener) {
				this.defaultRetriever.applicationListeners.remove(singletonTarget);
			}

//注册事件监听器			
			this.defaultRetriever.applicationListeners.add(listener);
			this.retrieverCache.clear();
		}
	}

把事件监听器保存在了ListenerRetriever类的applicationListeners集合中

4.3.3 ApplicationEventMulticaster发布事件

当AbstractApplicationContext中调用publishEvent()方法时或者其他重载的发布事件的方法时,底层会调用的ApplicationEventMulticaster的multicastEvent()方法,源码如下:

@Override
	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

逻辑也比较简单:
1.获得事件类型,(eventType != null ? eventType : resolveDefaultEventType(event))
2.根据事件类型找到对应的监视器,getApplicationListeners(event, type),这一步是通过事件类型找到对应事件监听器的关键
3.如果存在单独的执行器(线程池)则采用异步的方式调用的监听器的方法,否则同步执行对象的方法。invokeListener(listener, event);

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);
		}
	}

invokeListener的处理逻辑:
1.判断是否存在错误处理器,若存在错误处理器,当发生异常的时候调用对应的错误处理方法;
2.不存在错误错误处理器,直接调用doInvokeListener(listener, event);方法

	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
		//调用事件监视器中的onApplicationEvent方法,完成事件发布工作
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception and just log a debug message.
				Log logger = LogFactory.getLog(getClass());
				if (logger.isDebugEnabled()) {
					logger.debug("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

4.4 Spring如何根据发布的事件找到对应的事件监听器?

在发布事件的过程中,存在一个疑问我们怎么知道事件和实践监听器的对应关系呢?在4.3.3 ApplicationEventMulticaster发布事件的过程中提到了2.根据事件类型找到对应的监视器,name具体这一步是如何做到的呢?
通过事件找事件监听器的基本思想很简单:
1.首先,Spring的事件监听器中是通过泛型定义的,在具体使用的时候会指定实际类型,通过反射可得实际监听的事件类型
2.其次,Spring发布事件时会将事件作为参数传递给事件发布器;
3.从而,事件发布器有了事件信息和事件监听器所监听的事件类型,就可以通过事件找到对应的时间监听器了。
具体而言在类AbstractApplicationEventMulticaster的方法getApplicationListeners()中实现的

protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {

		Object source = event.getSource();
		Class<?> sourceType = (source != null ? source.getClass() : null);
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

		// Quick check for existing entry on ConcurrentHashMap...
		ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
		if (retriever != null) {
			return retriever.getApplicationListeners();
		}

		if (this.beanClassLoader == null ||
				(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
						(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
			// Fully synchronized building and caching of a ListenerRetriever
			synchronized (this.retrievalMutex) {
				retriever = this.retrieverCache.get(cacheKey);
				if (retriever != null) {
					return retriever.getApplicationListeners();
				}
				retriever = new ListenerRetriever(true);
				Collection<ApplicationListener<?>> listeners =
						retrieveApplicationListeners(eventType, sourceType, retriever);
				this.retrieverCache.put(cacheKey, retriever);
				return listeners;
			}
		}
		else {
			// No ListenerRetriever caching -> no synchronization necessary
			return retrieveApplicationListeners(eventType, sourceType, null);
		}
	}

基本逻辑:
1.通过事件源类型和事件类型构成ListenerCacheKey
2.从缓存中查找,存在直接方法,不存在走下一步
3.缓存中不存在则,Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
4.将查到的结果缓存起来

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {

		LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
		Set<ApplicationListener<?>> listeners;
		Set<String> listenerBeans;
		synchronized (this.retrievalMutex) {
			listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
			listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
		}
		for (ApplicationListener<?> listener : listeners) {
		//找到指定类型的监听器
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					retriever.applicationListeners.add(listener);
				}
				
				allListeners.add(listener);
			}
		}
		略略略。。。。。。。
		//返回结果
		return allListeners;
	}
protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
		GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
				(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
		return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
	}

处理逻辑:
1.当前Listener是GenericApplicationListener 类型,直接强转,否则通过适配器GenericApplicationListenerAdapter获得对象GenericApplicationListener
2.对于获得GenericApplicationListener 对象直接调用其中 的supportsEventType和supportsSourceType方法即可判断该监视器是否支持该事件类型

5.总结

本文从事件监听机制的基本概念,再到jdk事件机制代码示例,最后到Spring对事件监听机制的支持,及源码分析希望能够帮助您对事件监听机制有一个深入的理解和把握,本文对Spring监听机制做了源码上的分析,后续有机会,再起一篇重点讲监听器在Spring中的应用场景。


版权声明:本文为CTPeng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。