事件机制-Spring 源码系列(4)

目录:

Ioc容器beanDefinition-Spring 源码(1)

Ioc容器依赖注入-Spring 源码(2)

Ioc容器BeanPostProcessor-Spring 源码(3)

事件机制-Spring 源码(4)

AOP执行增强-Spring 源码系列(5)

ApplicationEvent 事件抽象类
ApplicationListener 监听器接口
ApplicationContext 事件源
事件源触发事件后,将事件通知给监听器,监听器执行相应逻辑的过程
 
使用简单的实现:
事件:
public class EatEvent extends ApplicationEvent {

    private String status;

    public String getStatus() {
return status;
} public void setStatus(String status) {
this.status = status;
} public EatEvent(Object source) {
super(source);
}
}

监听器:

public class MeListener implements ApplicationListener<EatEvent> {

    public void onApplicationEvent(EatEvent event) {
System.out.println("收到通知,可以去吃饭了");
}
}

触发事件:

public class TestDo implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public void doTest(){
applicationContext.publishEvent(new EatEvent(this));
} public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

以上代码是实际spring项目中经常会用到的,利用spring的事件机制,可以解耦各个具体监听器,在变化的需求中通过增减监听器来减少具体实现的改动。

spring核心是管理bean,而对于这种事件机制,天然有了比较好的实现基础,可以想象这些事件bean在初始化时已经被管理器加入到某个注册表里了,然后事件触发时,就要找容器触发。

网上找的完整的相关类图:

源码实现部分:

首先我们在创建一个Listener的时候,需要把这个bean交给容器管理,由EventMulticaster来管理,从applicationContext.publishEvent(new EatEvent("”))为入口来看源码。

public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
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);
} // Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<Object>(this, event);
if (eventType == null) {
eventType = ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass());
}
} // Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 获取ApplicationEventMulticaster
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
} // Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

getApplicationEventMulticaster拿预备好的事件广播器,可以使用自己实现的事件广播器,初始化是在AbstractApplicationContext.refresh方法触发initApplicationEventMulticaster():

protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 取applicationEventMulticaster名的bean,如果没有,就用框架的SimpleApplicationEventMulticaster,也就是说我们可以自定义一个bean来扩展
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 + "]");
}
}
}

SimpleApplicationEventMulticaster的multicastEvent(applicationEvent, eventType);方法:

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

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

retrieveApplicationListeners需要从容器中过滤出对应的监听者的bean:

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);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, eventType)) {
// 就是这行代码从容器中获取
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListenerBeans.add(listenerBeanName);
}
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}
事实上,容器预先将监听者的bean全部注册到了defaultRetriever.applicationListeners,每次出发publish时,来遍历过滤出后缓存起来。这个注册的操作也是在AbstractApplicationContext.refresh方法中的registerListeners();
最下面的AnnotationAwareOrderComparator.sort用来排序监听者的执行顺序。继承Ordered即可。
这里我们可以回顾一下这个refresh方法的具体代码。
以上已经基本看完了怎么讲监听器的获取,再来看一下执行方法的触发,回到SimpleApplicationEventMulticaster的multicastEvent(applicationEvent, eventType);

这里牵涉到同步执行或异步执行这些监听器的问题,默认spring是同步执行的,那么在实际场景中我们会因为监听者执行影响住流程,采用异步的方式,如果没有阅读过源码,采取的方式可能会使用在publish的时候进行异步化。
但是这里注意到,如果是publish的时候进行异步操作,异步线程在执行多个监听者时依然需要一个个去执行。
我们想到多个监听器如果可以并行执行,是可以提高执行效率的,那么这里就有个扩展的入口了那就是通过继承AbstractApplicationEventMulticaster自定义一个广播器。
public class AsyncApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(); public void setTaskExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = (taskExecutor != null ? taskExecutor : new SimpleAsyncTaskExecutor());
} protected TaskExecutor getTaskExecutor() {
return this.taskExecutor;
} @SuppressWarnings("unchecked")
public void multicastEvent(final ApplicationEvent event) {
for (Iterator<ApplicationListener> it = getApplicationListeners().iterator(); it.hasNext();) {
final ApplicationListener listener = it.next();
getTaskExecutor().execute(new Runnable() {
public void run() {
listener.onApplicationEvent(event);
}
});
}
}
}

实现的时候也可以通过继承SimpleApplicationEventMulticaster的方式来完成,例子如下:

public class AsyncApplicationEventMulticaster extends SimpleApplicationEventMulticaster {
private TaskExecutor taskExecutor = new TaskExecutor() {
ExecutorService exeserv = Executors.newCachedThreadPool();
public void execute(Runnable task) {
exeserv.execute(task);
}
}; protected TaskExecutor getTaskExecutor() {
return this.taskExecutor;
}
}

上面提到的扩展点就是自己定义一个id=applicationEventMulticaster的bean,就可以自定义广播器了。

<bean id="applicationEventMulticaster" class="com.x.y.z.AsyncApplicationEventMulticaster" />

再补充一个,我们看到这个applicationEventMulticaster bean的意味着spring容器中定义的所有监听器都会被自定义的广播器来广播,单纯实现异步并不是好的实现,如果有不能异步执行的呢,所以在自定义的广播器里的实现代码有必要配合监听器的信息进行一些筛选的工作。

invokeListener来执行onApplicationEvent方法:

protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
listener.onApplicationEvent(event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
listener.onApplicationEvent(event);
}
}

到这里,就执行到了onApplicationEvent方法。

另外,回到最前面的例子中,注意EatEvent中那个source属性,代表来源的意思,再调用publish方法时将this传入,那么在筛选监听者的时候,就可以判断是哪个来源的bean发起的通知,再进行一次筛选是否执行的逻辑,如此就是监听者可以过滤事件源了。

事件机制-Spring 源码系列(4)的更多相关文章

  1. Ioc容器依赖注入-Spring 源码系列(2)

    Ioc容器依赖注入-Spring 源码系列(2) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostPr ...

  2. Ioc容器BeanPostProcessor-Spring 源码系列(3)

    Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...

  3. AOP执行增强-Spring 源码系列(5)

    AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...

  4. Ioc容器beanDefinition-Spring 源码系列(1)

    Ioc容器beanDefinition-Spring 源码系列(1) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器 ...

  5. Spring源码系列 — BeanDefinition

    一.前言 回顾 在Spring源码系列第二篇中介绍了Environment组件,后续又介绍Spring中Resource的抽象,但是对于上下文的启动过程详解并未继续.经过一个星期的准备,梳理了Spri ...

  6. Spring源码系列 — 注解原理

    前言 前文中主要介绍了Spring中处理BeanDefinition的扩展点,其中着重介绍BeanDefinitionParser方式的扩展.本篇文章承接该内容,详解Spring中如何利用BeanDe ...

  7. Spring源码系列 — Bean生命周期

    前言 上篇文章中介绍了Spring容器的扩展点,这个是在Bean的创建过程之前执行的逻辑.承接扩展点之后,就是Spring容器的另一个核心:Bean的生命周期过程.这个生命周期过程大致经历了一下的几个 ...

  8. Spring源码系列(二)--bean组件的源码分析

    简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...

  9. Spring源码系列(三)--spring-aop的基础组件、架构和使用

    简介 前面已经讲完 spring-bean( 详见Spring ),这篇博客开始攻克 Spring 的另一个重要模块--spring-aop. spring-aop 可以实现动态代理(底层是使用 JD ...

随机推荐

  1. iOS开发——UIAlertController

    iOS8之后,UIAlertView和UIActionSheet被干掉了,取而代之的是UIAlertController和UIAlertAction. UIAlertController有两种样式,一 ...

  2. BCB实现BMP图片的RGB分解(转)

    源:BCB实现BMP图片的RGB分解 1.打开BMP图片文件,在Image控件中显示: if(dlgOpen1->Execute()) { edt1->Text=dlgOpen1-> ...

  3. Memcached源码分析之内存管理

    先再说明一下,我本次分析的memcached版本是1.4.20,有些旧的版本关于内存管理的机制和数据结构与1.4.20有一定的差异(本文中会提到). 一)模型分析在开始解剖memcached关于内存管 ...

  4. IOS开发中使用CNContact\CNMutableContact 对通讯录增删改查

    IOS开发中使用CNContact\CNMutableContact 对通讯录增删改查 首先当然是把CNcontact包含在工程中: @import Contacts; 1.下面是增加联系人的程序段: ...

  5. bzoj 1036

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 11858  Solved: 4803[Submit ...

  6. (转载)HTML、CSS、JavaScript、PHP、MySQL 的学习顺序是什么?

    文章转载自 鸟巢 - 技术分享的社区 http://t.runoob.com/question/13 1.HTML.CSS.JavaScript 前端学习三部曲,照着这个顺序依次学习 HTML教程.C ...

  7. fold change(ratio)

    fold change 英文简称 : FC 中文全称 : 倍性变化 所属分类 : 生物科学 词条简介 : 一种用于描述两个用于相比的对象数量差异的方法.例如,第一个样本和第二个样本的量是50/10,那 ...

  8. FMS4中的P2P功能

    在fms4以前Adobe只允许在stratus中才能使用p2p功能.令人高兴的是,在最新发布的fms4中,p2p功能已经集成进来了,这将给实时视频类的应用带来更高的效率,adobe这次很给力! 为了使 ...

  9. Clojure发音

    Clojure的发音和单词closure是一样的.Clojure之父是这样解释Clojure名字来历的 “我想把这就几个元素包含在里面: C (C#), L (Lisp) and J (Java). ...

  10. Word常用实用知识3

    纯手打,可能有错别字,使用的版本是office Word 2013 转载请注明出处 http://www.cnblogs.com/hnnydxgjj/p/6322813.html,谢谢. 分页符分页 ...