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

简介: 今天来分享一下Spring的事件监听机制,之前分享过一篇Spring监听机制的使用,今天从原理上进行解析,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.
 */
@Component
public 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.
 */
@Component
public class AppPublisher {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    public void publish(){
        applicationEventPublisher.publishEvent(new AppEvent(new AppListener(),"publish event"));
    }
}


测试


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


@SpringBootApplication
public 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.
 */
@Configuration
public 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事件监听机制来发布网关的更新数据,它可以降低系统的耦合性,使系统的扩展性更好。


今天的分享就到这里,感谢你的观看,我们下期见,如果文中有说得不合理或者不对的地方,希望你能指出,我们进行交流!


目录
相关文章
|
10天前
|
存储 机器学习/深度学习 搜索推荐
深入解析矢量数据库的数据模型与索引机制
【4月更文挑战第30天】本文深入探讨了矢量数据库的数据模型和索引机制。向量数据库以高维向量表示数据,采用稀疏或密集向量形式,并通过数据编码和组织优化存储与检索。索引机制包括基于树的(如KD-Tree和Ball Tree)、基于哈希的(LSH)和近似方法(PQ),加速相似性搜索。理解这些原理有助于利用矢量数据库处理大规模高维数据,应用于推荐系统、图像搜索等领域。随着技术发展,矢量数据库将扮演更重要角色。
|
10天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
21 0
|
6天前
|
Java 开发者 微服务
Spring Cloud原理详解
【5月更文挑战第4天】Spring Cloud是Spring生态系统中的微服务框架,包含配置管理、服务发现、断路器、API网关等工具,简化分布式系统开发。核心组件如Eureka(服务发现)、Config Server(配置中心)、Ribbon(负载均衡)、Hystrix(断路器)、Zuul(API网关)等。本文讨论了Spring Cloud的基本概念、核心组件、常见问题及解决策略,并提供代码示例,帮助开发者更好地理解和实践微服务架构。此外,还涵盖了服务通信方式、安全性、性能优化、自动化部署、服务网格和无服务器架构的融合等话题,揭示了微服务架构的未来趋势。
31 6
|
9天前
|
缓存 NoSQL Java
17:缓存机制-Java Spring
17:缓存机制-Java Spring
22 5
|
10天前
|
PHP 开发者
深入解析PHP的命名空间与自动加载机制
【4月更文挑战第30天】 在现代PHP开发实践中,命名空间和自动加载机制是模块化和代码复用的关键。本文旨在提供一个全面的视角来理解这两个概念如何协同工作以优化项目结构。我们将探讨命名空间解决代码冲突的方式,以及自动加载机制如何智能地按需加载类,从而减少内存占用和提升性能。
|
11天前
|
算法 安全 Linux
深度解析:Linux内核内存管理机制
【4月更文挑战第30天】 在操作系统领域,内存管理是核心功能之一,尤其对于多任务操作系统来说更是如此。本文将深入探讨Linux操作系统的内核内存管理机制,包括物理内存的分配与回收、虚拟内存的映射以及页面替换算法等关键技术。通过对这些技术的详细剖析,我们不仅能够理解操作系统如何高效地利用有限的硬件资源,还能领会到系统设计中的性能与复杂度之间的权衡。
|
11天前
|
负载均衡 Java 开发者
Spring Cloud:一文读懂其原理与架构
Spring Cloud 是一套微服务解决方案,它整合了Netflix公司的多个开源框架,简化了分布式系统开发。Spring Cloud 提供了服务注册与发现、配置中心、消息总线、负载均衡、熔断机制等工具,让开发者可以快速地构建一些常见的微服务架构。
|
11天前
|
安全 Java 测试技术
利用Java反射机制提高Spring Boot的代码质量:概念与实战
【4月更文挑战第29天】Java反射机制提供了一种强大的方法来在运行时检查或修改类和对象的行为。在Spring Boot应用中,合理利用反射可以提高代码的灵活性和可维护性。本篇博客将探讨Java反射的核心概念,并展示如何通过反射提高Spring Boot项目的代码质量。
28 0
|
15天前
|
缓存 Java Python
Python 弱引用全解析:深入探讨对象引用机制!
Python 弱引用全解析:深入探讨对象引用机制!
21 3
|
15天前
|
canal 缓存 关系型数据库
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战
Spring Boot整合canal实现数据一致性解决方案解析-部署+实战

推荐镜像

更多