当前位置:   article > 正文

Spring事件监听机制使用和原理解析_analysiseventlistener 的invoke 底层实现原理详解

analysiseventlistener 的invoke 底层实现原理详解

前言

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

使用

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

定义事件

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

  1. java复制代码/**
  2. * 功能说明: 事件
  3. * <p>
  4. * Original @Author: steakliu-刘牌, 2023-03-30 11:02
  5. * <p>
  6. * Copyright (C)2020-2022 steakliu All rights reserved.
  7. */
  8. public class AppEvent extends ApplicationEvent {
  9. private final String event;
  10. public AppEvent(Object source, String event) {
  11. super(source);
  12. this.event = event;
  13. }
  14. public String getEvent() {
  15. return event;
  16. }
  17. }

定义事件监听器

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

  1. java复制代码/**
  2. * 功能说明:事件监听器
  3. * <p>
  4. * Original @Author: steakliu-刘牌, 2023-03-30 11:03
  5. * <p>
  6. * Copyright (C)2020-2022 steakliu All rights reserved.
  7. */
  8. @Component
  9. public class AppListener implements ApplicationListener<AppEvent> {
  10. @Override
  11. public void onApplicationEvent(AppEvent event) {
  12. System.out.println("event: "+event.getEvent());
  13. }
  14. }

事件发布器

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

  1. java复制代码/**
  2. * 功能说明:事件发布器
  3. * <p>
  4. * Original @Author: steakliu-刘牌, 2023-06-11 13:55
  5. * <p>
  6. * Copyright (C)2020-2022 steakliu All rights reserved.
  7. */
  8. @Component
  9. public class AppPublisher {
  10. @Resource
  11. private ApplicationEventPublisher applicationEventPublisher;
  12. public void publish(){
  13. applicationEventPublisher.publishEvent(new AppEvent(new AppListener(),"publish event"));
  14. }
  15. }

测试

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

  1. java复制代码@SpringBootApplication
  2. public class Application {
  3. public static void main(String[] args) {
  4. ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
  5. AppPublisher publisher = context.getBean(AppPublisher.class);
  6. publisher.publish();
  7. }
  8. }

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

原理

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

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

  1. java复制代码@Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. synchronized (this.startupShutdownMonitor) {
  4. // Initialize event multicaster for this context.
  5. initApplicationEventMulticaster();
  6. // Check for listener beans and register them.
  7. registerListeners();
  8. // Last step: publish corresponding event.
  9. finishRefresh();
  10. }
  11. }
  12. }

initApplicationEventMulticaster()

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

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

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

  1. java复制代码protected void initApplicationEventMulticaster() {
  2. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  3. if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
  4. this.applicationEventMulticaster =
  5. beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
  6. if (logger.isTraceEnabled()) {
  7. logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
  8. }
  9. } else {
  10. this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
  11. beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
  12. if (logger.isTraceEnabled()) {
  13. logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
  14. "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
  15. }
  16. }
  17. }

registerListeners()

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

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

finishRefresh()

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

ApplicationEventPublisher发布事件

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

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

ApplicationEventMulticaster真正发布事件

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

  1. java复制代码public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
  2. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  3. Executor executor = getTaskExecutor();
  4. for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  5. if (executor != null) {
  6. executor.execute(() -> invokeListener(listener, event));
  7. } else {
  8. invokeListener(listener, event);
  9. }
  10. }
  11. }

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

  1. java复制代码/**
  2. * 功能说明: 事件任务线程池
  3. * <p>
  4. * Original @Author: steakliu-刘牌, 2023-06-11 13:17
  5. * <p>
  6. * Copyright (C)2020-2022 steakliu All rights reserved.
  7. */
  8. @Configuration
  9. public class TaskExecutor {
  10. @Bean("eventTaskExecutor")
  11. public Executor taskExecutor() {
  12. ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
  13. threadPoolTaskExecutor.setCorePoolSize(10);
  14. threadPoolTaskExecutor.setMaxPoolSize(20);
  15. threadPoolTaskExecutor.setKeepAliveSeconds(10);
  16. threadPoolTaskExecutor.setThreadNamePrefix("application-event-thread");
  17. threadPoolTaskExecutor.setQueueCapacity(100);
  18. threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
  19. threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
  20. threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
  21. threadPoolTaskExecutor.initialize();
  22. return threadPoolTaskExecutor;
  23. }
  24. @Bean
  25. public ApplicationEventMulticaster applicationEventMulticaster() {
  26. SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
  27. simpleApplicationEventMulticaster.setTaskExecutor(taskExecutor());
  28. return simpleApplicationEventMulticaster;
  29. }
  30. }

invokeListener

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

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

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

  • ApplicationEvent
  • ApplicationListener
  • ApplicationEventPublisher
  • ApplicationEventMulticaster

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

总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/799322
推荐阅读
相关标签
  

闽ICP备14008679号