写点什么

Spring 事件监听机制使用和原理解析

  • 2023-06-27
    福建
  • 本文字数:4889 字

    阅读完需:约 16 分钟

前言


好久没有更新 Spring 了,今天来分享一下 Spring 的事件监听机制,之前分享过一篇 Spring 监听机制的使用,今天从原理上进行解析,Spring 的监听机制基于观察者模式,就是就是我们所说的发布订阅模式,这种模式可以在一定程度上实现代码的解耦,如果想要实现系统层面的解耦,那么消息队列就是我们的不二选择,消息队列本身也是发布订阅模式,只是不同的消息队列的实现方式不一样。


使用


之前的文章我们使用了注解的方式,今天我们使用接口的方式来实现。


定义事件


如下定义了一个事件 AppEvent,它继承了 ApplicationEvent 类,如果我们要使用 Spring 的事件监听机制,那么我们定义的事件必须继承 ApplicationEvent ,否则就无法使用。


/** * 功能说明: 事件 * <p> * Original @Author: steakliu-刘牌, 2023-03-30  11:02 * <p> * Copyright (C)2020-2022  steakliu All rights reserved. */public class AppEvent extends ApplicationEvent {    private final String event;
public AppEvent(Object source, String event) { super(source); this.event = event; }
public String getEvent() { return event; }}
复制代码


定义事件监听器


事件监听器实现了 ApplicationLister 接口,其泛型为 ApplicationEvent,因为要监听事件,所以必须按照 Spring 的规则来,onApplicationEvent 方法就是监听到的事件,在这里我们可以进行我们的业务处理,我们可以看出 AppLister 我们加上了 @Component 注解,因为事件监听器需要加入 Spring IOC 容器中才能生效。


/** * 功能说明:事件监听器 * <p> * Original @Author: steakliu-刘牌, 2023-03-30  11:03 * <p> * Copyright (C)2020-2022  steakliu All rights reserved. */@Componentpublic class AppListener implements ApplicationListener<AppEvent> {    @Override    public void onApplicationEvent(AppEvent event) {        System.out.println("event:  "+event.getEvent());    }}
复制代码


事件发布器


有了事件监听器,就需要发布事件,所以就需要一个事件发布器,事件发布器使用的是 ApplicationEventPublisher,使用它的 publishEvent 方法进行事件发布。


/** * 功能说明:事件发布器 * <p> * Original @Author: steakliu-刘牌, 2023-06-11  13:55 * <p> * Copyright (C)2020-2022  steakliu All rights reserved. */@Componentpublic class AppPublisher {
@Resource private ApplicationEventPublisher applicationEventPublisher;
public void publish(){ applicationEventPublisher.publishEvent(new AppEvent(new AppListener(),"publish event")); }}
复制代码


测试为了方便,这里直接使用 SpringBoot 来进行测试,先获取 AppPublisher,然后调用 publish 发布事件。


@SpringBootApplicationpublic class Application {
public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); AppPublisher publisher = context.getBean(AppPublisher.class); publisher.publish(); }}
复制代码


上面整个事件发布的代码就写完了,我们可以看出其实还是比较简单的,里面最核心的三个组件分别为,事件(Event)监听器(Listener)发布器(Publisher),实际使用中我们可以根据自己的需求去实现。


原理


上面我们知道了 Spring 的事件监听机制的基本使用,那么整个事件在 Spring 中是怎么流转的呢,我们很有必要去弄清楚。


我们使用的是 SpringBoot 项目来进行测试,我们先找到 SpringBoot 对事件监听机制进行处理的入口,然后再进行分析,SpringBoot 对上下文进行处理的入口类是 AbstractApplicationContext,它是 Spring 的入口,其中我们主要关注的refresh()方法,因为 refresh 中的方法比较多,我们下面只保留了三个方法。


@Override	public void refresh() throws BeansException, IllegalStateException {		synchronized (this.startupShutdownMonitor) {    // Initialize event multicaster for this context.				initApplicationEventMulticaster();				// Check for listener beans and register them.				registerListeners();				// Last step: publish corresponding event.				finishRefresh();			}		}	}
复制代码


initApplicationEventMulticaster()


ApplicationEventMulticaster 是一个接口,它定义了如何将 ApplicationEvent 传递给事件监听者(event listener)。该接口有多个实现类,可以使用不同的策略将事件分派给不同的监听者。


ApplicationEventMulticaster 为 Spring 事件机制的核心之一,它支持在应用中传递事件,并且可以将事件广播给多个监听者。在 Spring 中,事件是由 ApplicationEvent 及其子类表示的,例如 ContextStartedEvent 和 ContextStoppedEvent 等。当某些事件发生时,Spring 容器将使用事件广播机制来通知感兴趣的监听者。


这个方法的作用是对 ApplicationEventMulticaster 进行赋值,Spring 在初始化的时候会将 ApplicationEventMulticaster 注册进 IOC 容器,这里就只是单纯从 IOC 容器中获取 ApplicationEventMulticaster 来进行赋值,以方便后续的使用。


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.isTraceEnabled()) {                logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");            }        } else {            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);            if (logger.isTraceEnabled()) {                logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +                        "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");            }        }    }
复制代码


registerListeners()


这个方法的作用主要就是注册监听器,它会从 IOC 容器获取到我们注册的监听器,然后将其加入到 Multicaster 中,在 AbstractApplicationEventMulticaster 中,使用一个 Set 集合来装监听器。


public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
复制代码


finishRefresh()


finishRefresh()的作用是发布事件,里面是一些发布事件的逻辑,但是由于我们还没有正式发布事件,所以这里并不会发布事件,当我们使用 applicationEventPublisher 的 publishEvent 方法发布事件时,才会真正的发布事件。


ApplicationEventPublisher 发布事件


上面示例中使用 ApplicationEventPublisher 的 publishEvent 发布事件,最终会进入 AbstractApplicationContext 类中进行事件发布,我们只关注最重要的方法 multicastEvent(),它是广播器 ApplicationEventMulticaster 的一个方法事件都是由广播器进行发布。


protected void publishEvent(Object event, @Nullable ResolvableType eventType) {  getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}
复制代码


ApplicationEventMulticaster 真正发布事件


ApplicationEventPublisher 并没有真正发布事件,它相当于只是抽象了事件的发布,为了让我们更加简单和方便使用,但是真正发布事件的是 ApplicationEventMulticaster,在 multicastEvent()方法中,如果我们配置了线程池,那么事件就会被加入线程池,从而异步执行,如果没有设置线程池,那么就同步执行,最终执行都是调用 invokeListener()方法。


public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));        Executor executor = getTaskExecutor();        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {            if (executor != null) {                executor.execute(() -> invokeListener(listener, event));            } else {                invokeListener(listener, event);            }        }    }
复制代码


默认是不会使用线程池的,如果我们需要事件异步执行,那么可以配置线程池,其核心就是给广播器 SimpleApplicationEventMulticaster 的成员变量 taskExecutor 设置


/** * 功能说明: 事件任务线程池 * <p> * Original @Author: steakliu-刘牌, 2023-06-11  13:17 * <p> * Copyright (C)2020-2022  steakliu All rights reserved. */@Configurationpublic class TaskExecutor {
@Bean("eventTaskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize(10); threadPoolTaskExecutor.setMaxPoolSize(20); threadPoolTaskExecutor.setKeepAliveSeconds(10); threadPoolTaskExecutor.setThreadNamePrefix("application-event-thread"); threadPoolTaskExecutor.setQueueCapacity(100); threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true); threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true); threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); threadPoolTaskExecutor.initialize(); return threadPoolTaskExecutor; }
@Bean public ApplicationEventMulticaster applicationEventMulticaster() { SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(); simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor()); return simpleApplicationEventMulticaster; }
}
复制代码


invokeListener


invokeListener 最终会通过传入的监听器去调用目标监听器,也就是我们自定义的监听器,主要代码如下,我们可以看到最终调用 onApplicationEvent 方法,就是我们上面示例 AppListener 监听器的 onApplicationEvent 方法。


private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {		 listener.onApplicationEvent(event);}    
复制代码


到这里,整个流程就完了,我们梳理一下重要的组件。


  • ApplicationEvent

  • ApplicationListener

  • ApplicationEventPublisher

  • ApplicationEventMulticaster


上面的四个组件基本上就是 Spring 事件监听机制的全部,ApplicationEvent 是事件的规范,ApplicationListener 是监听器,ApplicationEventPublisher 是发布器,ApplicationEventMulticaster 是广播器,其实 ApplicationEventMulticaster 和 ApplicationEventPublisher 本质是一样的,都能完成事件的发布,ApplicationEventPublisher 最终也是去调用 ApplicationEventMulticaster,只不过它只专注于事件发布,单独提出一个接口来,职责更加单一,这也是一种设计思想。


总结


上面对 Spring 事件监听机制的使用和原理进行了详细的介绍,并对其中涉及的组件进行解析,Spring 事件监听机制是一个很不错的功能,我们在进行业务开发的时候可以引入,在相关的开源框架中也是用它的身影,比如高性能网关 ShenYu 中就使用了 Spring 事件监听机制来发布网关的更新数据,它可以降低系统的耦合性,使系统的扩展性更好。


文章转载自:刘牌

原文链接:https://www.cnblogs.com/steakliu/p/17474050.html

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
Spring事件监听机制使用和原理解析_spring_不在线第一只蜗牛_InfoQ写作社区