Spring笔记——通过源码理解Spring 中事件发布

Spring 中ApplicationContext 容器在BeanFactory 基础上增加了许多功能,现在单独记录下其中事件发布功能是如何实现的。


一、Spring 中与事件概览

关于Java 中事件机制这篇文章中自己已经整理:     。

弄清楚Java 中的事件原理,无非要弄清楚三个角色:事件源(source)、事件对象(EventObject)、事件监听器(EventListener)。

注:Java 中的java.util 包中的EventObject 与EventListener 是所有事件对象与监听器的最基础的类。


Spring 中是如何规划这三个角色的呢?

角色一:事件对象:

基础类为org.springframework.context.ApplicationEvent

源码:

public abstract class ApplicationEvent extends EventObject {

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

事件对象继承关系:

说明:其中ApplicationContextEvent 事件传入ApplicationContext 作为事件源,适合于所有Java 应用;RequestHandleEvent 中传入Object 对象作为事件源,且构造函数中附带了SessionID、请求地址、主机地址等信息,可见是专用于 Web 工程的。

角色二:事件源:

从上面可知,对于不同应用事件源是不同的。此处主要学习Spring ,所以只讨论子类ApplicationContextEvent 事件对象的事件源:ApplicationContext 。

ApplicationContext 与事件有关的继承关系:

说明:ApplicationContext 直接实现了ApplicationEventPublisher 接口,该接口中定义了publishEvent 方法,图上所示的类是全部实现了该接口方法的,而另外有些直接或间接实现了接口ApplicationContext 的类并没有实现publishEvent 方法,只是被动的继承了该方法。publishEvent 的具体实现位置在图中标记的AbstractApplicationContext 抽象类中。

监听器是在事件源中被调用的,那么事件源ApplicationContext 是怎么存放这些监听器实例的?在ApplicationContext 直接子类中,Spring 定义了所有监听器实例为一个集合:

private Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>();

Java 中事件监听器是在事件源中被调用执行,此处即ApplicationContext 的子类对象中。ApplicationContext 的publishEvent 方法的功能简单的理解就是,通过执行所有监听器实例的事件方法,判断该事件满足哪个监听器的执行条件,然后相关代码块。具体的过程便是在publishEvent 方法中完成的。此方法中关键在于标记1 处,后面仔细分析。

publishEvent 实现的源码:

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);//即执行applicationListeners 集合中所有监听器的事件方法。标记1
		if (this.parent != null) {
			this.parent.publishEvent(event);
		}
	}

角色三:监听器类:

基础类为:org.springframework.context.ApplicationListener

继承关系:



二、Spring 事件发布实现

上面已经知道,ApplicationContext 对象作为事件源,ApplicationEvent 作为事件对象基础类,ApplicationListener 作为事件监听器基础类。

所以,可以做一个简单的事件发布步骤为:

1、定义一个事件对象类:

class MyApplicationEvent extends ApplicationEvent {
	public MyApplicationEvent(Object source){super(source);}
}


2、定义一个处理上面MyApplicationEvent 事件的监听器类:

class MyApplicationListener implements ApplicationListener{

	public void onApplicationEvent(ApplicationEvent e){	//ApplicationEvent的事件处理句柄为onApplicationEvent
		if(e instanceof MyApplicationEvent){
			System.out.println("收到了MyApplicationEvent 事件,事件源为:"+e.getSource());
		}
	}
}


3、定义一个事件发布类,将ApplicationContext 作为事件源:

class Publisher implements ApplicationContextAware{
	private ApplicationContext appCtx=null;
	
	public void setApplicationContext(ApplicationContext appCtx) throws BeansException{
		this.appCtx=appCtx;
	}
	public void publishMyApplicationEvent(){	
		/*以String 对象作为事件源*/
		String strSource=new String("我是事件源");
		/*这就是要实现ApplicationContextAware 接口的原因,因为需要当前应用的ApplicationContext 对象*/
		MyApplicationEvent event=new MyApplication(strSource);	
		/*使用ApplicationContext 对象的publishEvent 方法发布事件*/
		this.appCtx.publishEvent(event);
	}
}

说明:ApplicationConextAware 接口的方法是ApplicationContext实例自动检测调用的,具体怎么调用没有细究,还有其他几个***Aware 接口也是如此。


4、配置myconfig.xml:省略头尾后

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="listener" class="com.milan.MyApplicationListener" />
<bean id="publisher" class="com.milan.Publisher"/>
</beans>

5、测试类。加载xml文件获得ApplicationContext 对象,通过getBean获得publisher 对象,执行其publishMyApplicationEvent 即可。

public class Main{
	public static void main(String[] args){
		ApplicationContext appContext=new ClassPathXmlApplicationContext("com/milan/myconfig.xml");
		Publisher publisher=(Publisher) appContext.getBean("publisher");
		publisher.publishMyApplicationEvent();
	}
}
注:演示的事件对象只是实现了最基础的ApplicationEvent 类,所以可以是任何Object对象作为事件源,但如果是实现ApplicationContextEvent  接口,此时必须以ApplicationContext 作为事件源,又该如何发布事件呢?应用场景又如何?后面讨论。

6、结果:



三、publishEvent 调用监听器细节

上面演示的只是简单的调用了publishEvent,而没有关注publishEvent 实现细节。回到前面:

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);//即执行applicationListeners 集合中所有监听器的事件方法。标记1
	if (this.parent != null) {
		this.parent.publishEvent(event);
	}
}
通过查看 标记1 处的函数细节,可发现ApplicationContext 容器管理所有监听器类是通过 ApplicationEventMulticaster 类实现的。该接口定义如下:

public interface ApplicationEventMulticaster {

	void addApplicationListener(ApplicationListener listener);
	void addApplicationListenerBean(String listenerBeanName);
	void removeApplicationListener(ApplicationListener listener);
	void removeApplicationListenerBean(String listenerBeanName);
	void removeAllListeners();
	void multicastEvent(ApplicationEvent event);

}
标记1 处的 getApplicationEventMulticaster().multicastEvent(event)    就是调用ApplicationEventMulticaseter 分发类的multicastEvent 实现执行ApplicationContext 实例中的成员变量applicationListeners 集合中的所有监听器的onApplicationEvent 方法,达到事件通知的目的。具体过程如下:

 1、创建ApplicationContext 实例时便初始化监听器管理接口:

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 + "]");
		}
	}
}
2、将监听器用bean标签声明在xml中,即完成了监听器的添加。

3、publishEvent 时,最终在监听器管理接口的实现类中的 multicastEvent 方法中实现事件通知:

public void multicastEvent(final ApplicationEvent event) {
	for (final ApplicationListener listener : getApplicationListeners(event)) {   	//遍历监听器
		Executor executor = getTaskExecutor();
		if (executor != null) {							//如果有帮手,找帮手完成
			executor.execute(new Runnable() {
				@SuppressWarnings("unchecked")
				public void run() {
					listener.onApplicationEvent(event);
				}
			});
		}
		else {									//只能老老实实自己完成
			listener.onApplicationEvent(event);
		}
	}
}	
知识点:使用Executor 类开启子线程处理耗时任务。


四、执行过程中的异常及解决办法

抛出异常:
Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/aop/support/AopUtils


原因分析:

网友解答:由于你涉及到面向切面编程,所以你需要AOP相关的jar包,不仅需要aop包,还需要aspectj相关的包,因为你用到ApplicationListener这种监听类。
百度知道













  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值