Spring之事件机制详解

Posted 敲代码的小小酥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring之事件机制详解相关的知识,希望对你有一定的参考价值。

机制详解

Spring提供了事件机制,其本质是JDK提供的事件机制的应用,利用的是观察者设计模式,具体请看设计模式之观察者模式(Observer Pattern)
这里我们来分析Spring事件机制的原理。
先上UML图,不熟悉UML规则的可以看UML类图的制作规则

下面我们对上图中涉及到的几个类进行讲解。
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请求结束触发该事件。在一些特殊场景里,可能会应用到该事件。

以上是关于Spring之事件机制详解的主要内容,如果未能解决你的问题,请参考以下文章

Spring高级之注解@DependsOn详解(超详细)

180609-Spring之事件驱动机制的简单使用

观察者模式之spring事件机制

详解 JavaScript 中的 EventtLoop(事件循环)机制

Spring事件监听机制源码解析

十Spring之事件监听