神刀安全网

说说 Spring 的容器事件体系

Spring 的 ApplicationContext 能够发布事件并且允许注册相应的事件监听器,它拥有一套完善的事件发布和监听机制。

在事件体系中有这些概念:

  • 事件: java.util.EventObject。
  • 监听器:java.util.EventListener。
  • 事件源:产生事件,任何一个事件,都必须拥有一个事件源。
  • 事件监听器注册表:用于保存监听器。当事件源产生事件时,就会通知注册表中的监听器。
  • 事件广播器:它负责向监听器通知事件。
说说 Spring 的容器事件体系

有时候,事件源、监听器注册表和事件广播这三个角色可以由同一个对象承担,比如 java.swing 中的 JButton。

事件体系,其实是观察者模式的一种具体实现方式。

1 Spring 事件类体系

1.1 事件类

说说 Spring 的容器事件体系

ApplicationEvent 的唯一一个构造函数是 ApplicationEvent(Object source),通过 source 来指定事件源,它有两个子类:

  • ApplicationContextEvent – 容器事件,它有四个子类,分别表示停止、刷新、关闭和启动容器事件。
  • RequestHandledEvent – 与 Web 应用相关的事件,当处理一个 HTTP 请求后会产生该事件(前提是在 web.xml 中定义了 DispatcherServlet)。

1.2 事件监听器接口

说说 Spring 的容器事件体系

ApplicationListener 接口只定义了 onApplicationEvent(E event) 方法,它接收 ApplicationEvent 事件对象,由实现类在此编写事件的响应处理逻辑。

SmartApplicationListener 是 Spring3.0 新增的接口,它定义两个方法:

方法 说明
boolean supportsEventType(Class<? extends ApplicationEvent> eventType) 指定支持哪些类型的容器事件。
boolean supportsSourceType(Class<?> sourceType) 指定对何种数据源做出响应。

GenericApplicationListener 是 Spring4.2 新增的接口,它增强了对泛型的支持,supportsEventType() 方法的参数采用的是可解析类型 ResolvableType。这是 Spring4 提供的泛型操作支持类,通过它可以很容易地获得泛型的实际类型信息,比如类级、字段级等等泛型信息。在 Spring4 的框架中,很多核心类内部涉及的泛型操作大都使用 ResolvableType 类进行处理。

GenericApplicationListener 定义两个方法,与 SmartApplicationListener 的方法功能相同。

1.3 事件广播器

说说 Spring 的容器事件体系

Spring 为事件广播器定义了一个接口、一个抽象类和一个简单的实现类。

2 分析

ApplicationContext 接口的抽象实现类 AbstractApplicationContext 搭建了事件体系。它包含 ApplicationEventMulticaster 变量,这个变量提供了容器监听器的注册表。而 refresh() 通过以下步骤搭建了事件的基础设施:

  1. 初始化应用上下文的事件广播器 – initApplicationEventMulticaster();
  2. 注册事件监听器 – registerListeners()
  3. 发布容器刷新事件 – finishRefresh()

我们可以在配置文件中自定义一个容器的事件广播器(实现了 ApplicationEventMulticaster),Spring 会利用反射机制将其注册为容器的事件广播器。Spring 如果没有找到定义的事件广播器,将会使用默认的事件广播器(SimpleApplicationEventMulticaster)。

Spring 会利用反射机制,从 BeanDefinitionRegistry 中找出所有实现了 ApplicationListener 的 Bean,将它们都注册为容器的事件监听器。

3 示例

假设我们需要一个短信发送器,它向目的号码发送短信时,会产生一个事件,然后通知给注册了该事件的监听器,进行后续的业务逻辑操作。

SmsSendEvent 事件:

public class SmsSendEvent extends ApplicationContextEvent {      /**      * 目的号码      */     private String target;      /**      * Create a new ContextStartedEvent.      *      * @param source the {@code ApplicationContext} that the event is raised for      *               (must not be {@code null})      * @param target      */     public SmsSendEvent(ApplicationContext source, String target) {         super(source);         this.target = target;     }      public String getTarget() {         return target;     } } 

SmsSendEvent 扩展了 ApplicationContextEvent,加入了一个目的号码 target。

SmsSendListener 监听器会监听 SmsSendEvent 事件:

public class SmsSendListener implements ApplicationListener<SmsSendEvent> {     public void onApplicationEvent(SmsSendEvent event) {         System.out.println("向 "+event.getTarget()+" 发送短信");     } } 

SmsSender 实现了 ApplicationContextAware 接口,因此就具有了发布事件的能力:

public class SmsSender implements ApplicationContextAware {      private ApplicationContext context;      /**      * 容器启动时,注入容器实例      *      * @param applicationContext      * @throws BeansException      */     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {         this.context = applicationContext;     }      /**      * 发送短信      * @param target      */     public void send(String target) {         System.out.println("准备发送短信");         SmsSendEvent event = new SmsSendEvent(context, target);         context.publishEvent(event);//向容器中所有注册了该事件的监听器发送该事件     } } 

这里通过 ApplicationContext 对象来发送事件,通知相应的事件监听器。

bean 配置:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">      <!-- 事件监听器-->     <bean class="net.deniro.spring4.event.SmsSendListener"/>      <bean id="SmsSender" class="net.deniro.spring4.event.SmsSender"/>  </beans> 

单元测试:

ApplicationContext context;  @BeforeMethod public void setUp() throws Exception {     context = new ClassPathXmlApplicationContext("beans.xml"); }  @Test public void test() {     SmsSender sender= (SmsSender) context.getBean("SmsSender");     sender.send("18929293832"); } 

容器启动后,就会根据配置文件自动注册 SmsSendListener 监听器啦。

输出结果:

准备发送短信
向 18929293832 发送短信

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 说说 Spring 的容器事件体系

分享到:更多 ()