Spring之事件机制详解

简介: Spring之事件机制详解

机制详解


Spring提供了事件机制,其本质是JDK提供的事件机制的应用,利用的是观察者设计模式,具体请看设计模式之观察者模式(Observer Pattern)。

这里我们来分析Spring事件机制的原理。

先上UML图,不熟悉UML规则的可以看UML类图的制作规则。


55.jpg


下面我们对上图中涉及到的几个类进行讲解。

ApplicationEvent:

抽象类,继承了JDK的EventObject接口,起到包装事件源的作用。

ApplicationListener:

实现了JDK的EventListener接口,起到监听器的作用。


ApplicationEventMulticaster类:

在观察者模式中,一定要有一个管理维护监听者列表的功能。在Spring的事件机制中,将维护监听者列表的功能单独定义了一个接口,即ApplicationEventMulticaster接口。这也体现了单一责任原则的设计思想。我们看其源码:


public interface ApplicationEventMulticaster {
 /**
  * Add a listener to be notified of all events.
  * @param listener the listener to add
  */
 void addApplicationListener(ApplicationListener<?> listener);
 /**
  * Add a listener bean to be notified of all events.
  * @param listenerBeanName the name of the listener bean to add
  */
 void addApplicationListenerBean(String listenerBeanName);
 /**
  * Remove a listener from the notification list.
  * @param listener the listener to remove
  */
 void removeApplicationListener(ApplicationListener<?> listener);
 /**
  * Remove a listener bean from the notification list.
  * @param listenerBeanName the name of the listener bean to remove
  */
 void removeApplicationListenerBean(String listenerBeanName);
 /**
  * Remove all listeners registered with this multicaster.
  * <p>After a remove call, the multicaster will perform no action
  * on event notification until new listeners are registered.
  */
 void removeAllListeners();
 /**
  * Multicast the given application event to appropriate listeners.
  * <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
  * if possible as it provides better support for generics-based events.
  * @param event the event to multicast
  */
 void multicastEvent(ApplicationEvent event);
 /**
  * Multicast the given application event to appropriate listeners.
  * <p>If the {@code eventType} is {@code null}, a default type is built
  * based on the {@code event} instance.
  * @param event the event to multicast
  * @param eventType the type of event (can be {@code null})
  * @since 4.2
  */
 void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}


可以看到,这个接口定义了增删查监听者的方法,所以,监听者列表的维护通过这个接口实现。需要注意的是这个接口还定义了multicastEvent方法。通过这个方法,将事件传给监听器。所以这个类,将事件和监听器,连接在一起。这里采用的是中介者模式,这个接口就是中介者角色。

ApplicationEventPublisher:

Spring设计的事件发布类,我们看其源码:


public interface ApplicationEventPublisher {
 /**
  * Notify all <strong>matching</strong> listeners registered with this
  * application of an application event. Events may be framework events
  * (such as ContextRefreshedEvent) or application-specific events.
  * <p>Such an event publication step is effectively a hand-off to the
  * multicaster and does not imply synchronous/asynchronous execution
  * or even immediate execution at all. Event listeners are encouraged
  * to be as efficient as possible, individually using asynchronous
  * execution for longer-running and potentially blocking operations.
  * @param event the event to publish
  * @see #publishEvent(Object)
  * @see org.springframework.context.event.ContextRefreshedEvent
  * @see org.springframework.context.event.ContextClosedEvent
  */
 default void publishEvent(ApplicationEvent event) {
  publishEvent((Object) event);
 }
 /**
  * Notify all <strong>matching</strong> listeners registered with this
  * application of an event.
  * <p>If the specified {@code event} is not an {@link ApplicationEvent},
  * it is wrapped in a {@link PayloadApplicationEvent}.
  * <p>Such an event publication step is effectively a hand-off to the
  * multicaster and does not imply synchronous/asynchronous execution
  * or even immediate execution at all. Event listeners are encouraged
  * to be as efficient as possible, individually using asynchronous
  * execution for longer-running and potentially blocking operations.
  * @param event the event to publish
  * @since 4.2
  * @see #publishEvent(ApplicationEvent)
  * @see PayloadApplicationEvent
  */
 void publishEvent(Object event);
}


里面定义了publishEvent方法,进行事件的发布。但是事件不是直接发布到listener中,而是发布在ApplicationEventMulticaster类中,所以在ApplicationEventPublisher类中,一定会有ApplicationEventMulticaster对象,将事件发布到ApplicationEventMulticaster中。


事件流程总结:

通过上面几个类的描述,我们总结一下spring事件机制的流程:

流程的核心,就是PublishEvent。Event对象以参数的形式传入PublishEvent对象。然后将Event事件传入ApplicationEventMulticaster类中,由ApplicationEventMulticaster类将事件传给其维护的监听者,执行监听者方法。


领悟

由上面Spring设计事件模式思路我们可以感受到,Spring把单一的功能,都拎出来形成了一套接口规范,然后多个接口规范组合,去完成一件事情。所以我们在阅读源码时会感觉很乱。只要我们分析清楚每个对象的设计思路和作用是什么,再分析他们之间的组合完成了什么事情,就很容易理解其设计理念了。


应用

上面分析了Spring事件机制的运行原理,那么对我们实际开发中,有何帮助呢?

这就需要我们结合源码进行解读了。

笔者找到一篇写的很好的博文,大家可以参考:浅谈Spring事件监听。


我们可以自定义事件源,如下:


@Component
public class MyEventSource {
    public void ccc(){
        System.out.println("事件源方法");
    }
}

然后定义Event对象,包装事件源:


@Component
public class MyEvent extends ApplicationEvent {
    public MyEvent(MyEventSource source) {
        super(source);
    }
    public void eventMethod(){
        System.out.println("事件自定义方法");
    }
}


这里我们需要注意,有参构造中,传入事件源,我们还可以在事件类中定义其他的方法,在监听者中调用。因为监听者监听的是事件类,所以可以直接调到事件类的自定义方法。

下面,我们定义监听者:


@Component
public class MyListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if(applicationEvent instanceof MyEvent) {
            ((MyEvent)applicationEvent).eventMethod();
            MyEventSource eventSource = (MyEventSource) applicationEvent.getSource();
            eventSource.ccc();
            System.out.println("监听者发生一些改变");
        }
    }
}


需要注意的是,如果我们让Spring自带的事件发布类去发布事件,上面我们提到,会发布到ApplicationEventMulticaster 中,而Spring默认的ApplicationEventMulticaster 类会把所有的事件都发布给监听者,它并不会去做过滤,什么事件发给什么监听者。所以我们只能在每个监听者方法中去做判断,事件类型属于某个类型时,才做处理。

如果我们想特定的事件发布给特定的监听者,那我们只能自己实现Spring的发布类和ApplicationEventMulticaster类,自己定义事件的发布机制。


更正:

ApplicationListener类是支持泛型的,在类后定义泛型,可以过滤掉其他的事件对象,只接收泛型类事件。


最后,也是最关键的步骤,万事俱备只欠东风了,事件的发布,是需要我们在业务代码中自行进行发布的,这里我们用ApplicationContext作为发布者,进行事件的发布,代码如下:


public class MyTest {
    @Test
    public void test1() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Object o=applicationContext.getBean("myEvent");
        applicationContext.publishEvent(applicationContext.getBean("myEvent"));
    }
}


总结

Spring事件机制其实就是发布-订阅思想的一个体现,只不过,Spring中的订阅发布只限于在一个Spring容器中完成。而我们平时使用的订阅发布,都是分布式环境下的,一般采用MQ的方式完成。在实际的应用开发中,一个容器中进行订阅发布的需求并不是很多,当有些特殊需求可以转化成容器内的订阅发布时,希望大家可以想到应用Spring提供的事件机制。

在单机环境中,我们要实现代码的解耦,可以采用事件机制。例如:在前面讲观察者模式时,我们把监听者的放在事件类中维护的方式,就是高耦合的。而Spring将其进行了解耦。


补充:

Spring为我们提供了一些事件类。我们可以利用这些现成的事件,自定义监听者,来做一些业务。下面我们介绍常用的事件。

ContextRefreshedEvent(上下文更新完成事件):在spring容器初始化流程完成后,触发事件。事件的触发(即调用publishEvent方法)是Spring自己调用的,我们只需定义Listener监听者处理业务即可。

如之前一个项目,在项目启动的时候有一个while(true){…}死循环,造成了项目无法正常启动。当时的解决方案是将死循环另起了一个线程解决的。现在可以采用ContextRefreshedEvent事件解决。当容器加载完成后,再执行死循环业务。代码如下:


@Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
      while (true){
          System.out.println("执行业务");
      }
    }
}


RequestHandledEvent:在web应用中,一个http请求结束触发该事件。在一些特殊场景里,可能会应用到该事件。

目录
相关文章
|
14天前
|
存储 安全 Java
事件的力量:探索Spring框架中的事件处理机制
事件的力量:探索Spring框架中的事件处理机制
28 0
|
1月前
|
Java 开发者 UED
Spring Boot的全局异常处理机制
【2月更文挑战第13天】
60 0
|
2月前
|
XML Java 数据格式
编织Spring魔法:解读核心容器中的Beans机制【beans 一】
编织Spring魔法:解读核心容器中的Beans机制【beans 一】
42 0
|
1月前
|
人工智能 JSON 前端开发
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
|
3月前
|
XML Java 数据格式
聊聊Spring事件及其应用
在 JDK 中已经提供相应的自定义事件发布功能的基础类: - `java.util.EventObject`类 :自定义**事件**类型 - `java.util.EventListener`接口:事件的**监听器**
27 1
聊聊Spring事件及其应用
|
4月前
|
Java 数据库 Spring
Spring 声明式事务机制
Spring 声明式事务机制
32 0
|
6天前
|
存储 安全 Java
第3章 Spring Security 的用户认证机制(2024 最新版)(下)
第3章 Spring Security 的用户认证机制(2024 最新版)
29 0
|
1月前
|
Java Spring
除了spring自带的事件,你还可以这样使用事件
除了spring自带的事件,你还可以这样使用事件
14 3
|
1月前
|
存储 安全 Java
全面探索Spring框架中的事件处理机制
在现代应用程序中,各个组件之间的通信是至关重要的。想象一下,你的应用程序中的各个模块像是一个巨大的交响乐团,每个模块都是一位音乐家,而Spring事件机制就像是指挥家,将所有音乐家协调得天衣无缝。这种松耦合的通信方式使你的应用程序更加灵活、可维护,而且能够轻松应对变化。现在,让我们进入这个令人兴奋的音乐厅,探索Spring事件的世界。
|
1月前
|
消息中间件 存储 Cloud Native
【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程
【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程