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这种监听类。
百度知道