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

来源:互联网 发布:虚拟社交网络正方攻辩 编辑:程序博客网 时间:2024/06/18 13:30

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 集合中所有监听器的事件方法。标记1if (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的事件处理句柄为onApplicationEventif(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 集合中所有监听器的事件方法。标记1if (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这种监听类。
百度知道













0 0
原创粉丝点击