ApplicationContext的事件机制是观察者设计模式的实现,通过 ApplicationEvent 类和 ApplicationListener 接口,可以实现 ApplicationContext 的事件处理。如果容器中有一个 ApplicationListener Bean 每当 ApplicationContext 发布 ApplicationEvent时,ApplicationListener Bean将自动触发。
Spring的事件框架有如下两个重要成员。
》ApplicationEvent: 容器事件,必须由 ApplicationContext发布。
》ApplicationListener: 监听器,可有容器中的任何监听器Bean担任
实际上,Spring 的事件机制与所有的事件机制都基本类似,他们都需要 事件源, 事件 和事件监听器 组成。只是此处的事件源是ApplicationContext。
下图简单示范了ApplicationContext事件
下面的程序僵尸翻生Spring容器的事件程序 。程序先定义了一个ApplicationContext类,其对象就是一个Spring容器事件。代码如下:
1 import org.springframework.context.ApplicationEvent; 2 3 public class EmailEvent extends ApplicationEvent{ 4 5 private static final long serialVersionUID = -7056841418193254583L; 6 7 private String address; 8 private String text; 9 public EmailEvent(Object source) { 10 super(source); 11 } 12 public String getAddress() { 13 return address; 14 } 15 public void setAddress(String address) { 16 this.address = address; 17 } 18 public String getText() { 19 return text; 20 } 21 public void setText(String text) { 22 this.text = text; 23 } 24 25 }
上面的EmailEvent类继承了ApplicationContext类,除此之外,它就是一个普通的Java类。
容器的监听器必须实现ApplicationListener接口,实现该接口必须实现如下方法:
》onApplicationEvent(ApplicationEvent event):每当容器内发生任何事件时,此方法都被触发。
本例所用的监听器代码如下:
1 import org.springframework.context.ApplicationEvent; 2 import org.springframework.context.ApplicationListener; 3 4 public class EmailNotifier implements ApplicationListener<ApplicationEvent>{ 5 6 //该方法会在容器发生事件时自动触发 7 public void onApplicationEvent(ApplicationEvent event) { 8 if(event instanceof EmailEvent){ 9 //只处理EmailEvent,发送Email通知... 10 EmailEvent emailEvent = (EmailEvent)event; 11 System.out.println("需要发送的邮件的接收地址:"+emailEvent.getAddress()); 12 System.out.println("需要发送邮件的邮件正文:"+emailEvent.getText()); 13 }else{ 14 //容器内置时间不做任何处理 15 System.out.println("容器本身事件: "+event); 16 } 17 } 18 19 }
将监听器配置在容器中,配置文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 7 <!-- 配置监听器 --> 8 <bean class="com.spring_example.event.EmailNotifier" /> 9 </beans>
从上面的配置文件中可以看出,为Spring容器注册事件监听器,不需要像AWT编程那样采用代码进行编程,只要进行简单的配置即可。当我们在Spring配置了一个实现ApplicationListener的Bean。Springl容器就会把这个Bean当初容器的监听器。
当系统创建Spring容器,加载Spring容器时会自动触发容器事件,容器事件监听器可以监听到这些事件。除此之外,程序也可调用ApplicationContext的publishEvent方法来主动触发容器事件。如下主程序使用ApplicationContext的publishEvent来触发事件。
1 ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext.xml"); 2 //创建一个ApplicationEvent对象 3 EmailEvent ele = new EmailEvent("Hello"); 4 ele.setAddress("123456@163.com"); 5 ele.setText("Hello World"); 6 //主动触发容器事件 7 ac.publishEvent(ele);
上面的代码通过ApplicationContext对象的publishEvent主动触发该事件。运行上面的程序将看到如下执行结果。
1 容器本身事件: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.support.ClassPathXmlApplicationContext@141b571: startup date [Fri Sep 13 10:16:01 CST 2013]; root of context hierarchy] 2 需要发送的邮件的接收地址:123456@163.com 3 需要发送邮件的邮件正文:Hello World
从上面的执行结果可以看出,监听器不仅监听到程序所触发的事件,也监听到容器内置的事件。实际上,如果开发者需要在Spring容器初始化,销毁时回调自定义方法,就可以通过上面的监听器来实现。
如果Bean想发布事件,则Bean必须获得其容器的引用。如果程序中没有直接获取容器的引用,则应该让Bean实现ApplicationContextAware 或 BeanFactoryAware接口,从而可以获得容器的引用。
Spring提供如下几个内置事件:
》ContextRefreshEvent: ApplicationContext容器初始化或刷新触发该事件。此处的初始化是指,所有Bean 被成功装载,后处理Bean被检测并激发,所有Singleton Bean被预实例化,ApplicationContext容器已就绪可用。
》ContextStartedEvent: 当使用ConfigurableApplicationContext(ApplicationContext的子接口)接口的start()方法启动ApplicationContext容器时触发该事件。容器管理生命周期的Bean实例将获得一个指定的启动信号,这在经常需要停止后重新启动的场合比较常见。
》ContextClosedEvent:当使用ConfigurableApplicationContext接口的close()方法关闭ApplicationContext容器时触发该事件。
》ContextStoppedEvent:当使用ConfigurableApplicationContext接口的stop()方法使ApplicationContext停止时触发该事件。此处的“停止”意味着容器管理生命周期的Bean实例将获得一个指定的停止信号,被停止的Spring容器可在此调用start()方法重新启动。
》RequestHandledEvent:Web相关的事件,只能运用于使用DispatcherServlet的Web运用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。
Spring的这种事件模型其实就是标准的观察者设计模式。