Spring Event是什么鬼

原创
2016/03/31 18:46
阅读数 947

一、什么是程序世界里的事件?

最常见的点击操作是一种事件,刷新操作是一种事件,类似的还有很多,在程序的世界里,事件的起源是输入设备的一个信号(比如中断响应),进而将事件(或者说信号及所带的信息)一步步地传递给需要响应的监听器(JS的事件机制就比较典型)。可以看出来,事件具有几个要素:起源、事件信息、可传递、可被响应。

起源:事实上大部分的应用里讲的事件,已经是被OS,被所在APP层层传递和包装后的事件

事件信息:事件传递到不同的应用层再往下继续传递的信息可能会有所不同,APP会根据自己的需要做相应的处理

可传递:比如:JS的事件冒泡

可被响应 :这是具体业务应该关心的点,触发了的事件该做什么事

理解了事件本质再来理解 spring event 就变得非常容易了,继续往下看

二、观察者模式

有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

当我们聚焦到软件系统内部时,同样可以找到很多“事件”的缩影,观察者模式为解决这类问题提供了一个方案。

三、Spring Event

Spring Event 同样遵循事件几个特性,有事件的起源,可被传递,可被响应等,使用Spring Event的好处是可以被Spring容器管理,可以使用容器资源。

起源:在 Spring Event 中,事件的起源可能是一段业务代码,当业务变更时,调用 Event publisher post 一个Event(事件信息)

//...需要触发事件的业务方法
ExampleEvent exampleEvent = new ExampleEvent();
//exampleEvent.set...
examplePublisher.publishEvent(event);

@Component("examplePublisher")
public class ExamplePublisherImpl implements ApplicationContextAware, ExamplePublisher {

    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }


    @Override
    public void publishEvent(ExampleEvent event) {
        this.context.publishEvent(event);
    }
}

那么,Spring Event 内部是如何将事件 Publish 出去的呢?

//AbstractApplicationContext.java
	/**
	 * Publish the given event to all listeners.
	 * <p>Note: Listeners get initialized after the MessageSource, to be able
	 * to access it within listener implementations. Thus, MessageSource
	 * implementations cannot publish events.
	 * @param event the event to publish (may be application-specific or a
	 * standard framework event)
	 */
	public void publishEvent(ApplicationEvent event) {
		Assert.notNull(event, "Event must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Publishing event in " + getDisplayName() + ": " + event);
		}
		getApplicationEventMulticaster().multicastEvent(event);
		if (this.parent != null) {
			this.parent.publishEvent(event);
		}
	}

	/**
	 * Return the internal ApplicationEventMulticaster used by the context.
	 * @return the internal ApplicationEventMulticaster (never {@code null})
	 * @throws IllegalStateException if the context has not been initialized yet
	 */
	private 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;
	}

事件通过 ApplicationContext 通知给相应的监听,实际上是调用 ApplicationEventMulticaster 接口实现的

//AbstractApplicationEventMulticaster.java
	public void multicastEvent(final ApplicationEvent event) {
		for (final ApplicationListener listener : getApplicationListeners(event)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					public void run() {
						listener.onApplicationEvent(event);
					}
				});
			}
			else {
				listener.onApplicationEvent(event);
			}
		}
	}

突然想到一个问题,ApplicationContext并不是只处理一个事件,ApplicationEevent也并不是只有一种,不同的事件有自己的实现,那 multicasterEvent 时如何准确将事件 publish ?

/**
	 * Return a Collection of ApplicationListeners matching the given
	 * event type. Non-matching listeners get excluded early.
	 * @param event the event to be propagated. Allows for excluding
	 * non-matching listeners early, based on cached matching information.
	 * @return a Collection of ApplicationListeners
	 * @see org.springframework.context.ApplicationListener
	 */
	protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
		Class<? extends ApplicationEvent> eventType = event.getClass();
		Class sourceType = event.getSource().getClass();
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);  //这里定义了一个类用来做为缓存key,实际是重写了equal,比较的是eventType&sourceType
		ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
		if (retriever != null) {
			return retriever.getApplicationListeners();
		}
		else {
			retriever = new ListenerRetriever(true);
			LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
			Set<ApplicationListener> listeners;
			Set<String> listenerBeans;
			synchronized (this.defaultRetriever) {  //此处参考正确使用Java事件通知
				listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
				listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
			}
			for (ApplicationListener listener : listeners) {
				if (supportsEvent(listener, eventType, sourceType)) { //通过反射判断Listener实例的参数是不是与提供的event一致
					retriever.applicationListeners.add(listener);
					allListeners.add(listener);
				}
			}
			if (!listenerBeans.isEmpty()) {
				BeanFactory beanFactory = getBeanFactory();
				for (String listenerBeanName : listenerBeans) {
					ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
					if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
						retriever.applicationListenerBeans.add(listenerBeanName);
						allListeners.add(listener);
					}
				}
			}
			OrderComparator.sort(allListeners);
			this.retrieverCache.put(cacheKey, retriever);
			return allListeners;
		}
	}

至此,Spring Event 已经可以实现事件触发,前半部分已经结束了,并且我们已经知道 listener 中的方法是如何被调用了,但是这些 listener 是如何被添加到 Multicaster 的广播列表中的呢?

//AbstractApplicationContext.java

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
		...
		// Initialize event multicaster for this context.
		initApplicationEventMulticaster();
		...
		// Check for listener beans and register them.
		registerListeners();		
		...
		}
	}
	
	/**
	 * Add beans that implement ApplicationListener as listeners.
	 * Doesn't affect other listeners, which can be added without being beans.
	 */
	protected void registerListeners() {
		// Register statically specified listeners first. 
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}
		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let post-processors apply to them!
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String lisName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(lisName);
		}
	}

ApplicationContext 在启动时初始化各种 Bean,同时也会注册监听的事件(所有ApplicationListener接口的实现),这里讲的注册就是把监听类实例(只限那些特定的静态的监听实例)或者类名称放入 Multicaster 的监听队列中,只是放名称的目的是让ApplicationContext初始化完后再做注入。

与整个过程相关的还有两个对象,一个是 ApplicationListener 接口,所有Listener都要实现这个接口,否则 ApplicationContext 启动时无法注册监听,另一个是 ApplicationEvent -> EventObject,事件信息的传递需要 ApplicationEvent,事件信息需要继承 ApplicationEvent,另外ApplicationEvent本身继承自JDK的EventObject,其中 source 对象为同一个事件类,在不同的情况下,使用不同监听处理提供了可能。

另外,事件还可以设定为异步执行,只要往容器注入相应的Executor。

四、Guava EventBus

Event是事件的另一种优秀实现,相对来讲,比 Spring event 更容易上手,也更容易使用。EventBus的设计思想可参考:http://beneo.iteye.com/blog/1984072

五、相关文章

操作系统中断方式与轮询方式:http://lionwq.spaces.eepw.com.cn/articles/article/item/18936

javascript事件机制底层实现原理:http://www.cnblogs.com/yexiaochai/p/3477715.html

观察者模式:http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html

正确使用Java事件通知:http://www.importnew.com/15446.html


展开阅读全文
打赏
1
12 收藏
分享
加载中
更多评论
打赏
0 评论
12 收藏
1
分享
返回顶部
顶部