iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >SpringEvent事件通知机制解读
  • 431
分享到

SpringEvent事件通知机制解读

Spring事件机制Spring事件通知机制Event事件通知机制 2023-02-14 15:02:35 431人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录spring事件通知原理异步响应Event自定义SimpleApplicationEventMulticaster@Async@Async原理总结Spring的事件通知机制是一项

Spring的事件通知机制是一项很有用的功能,使用事件机制我们可以将相互耦合的代码解耦,从而方便功能的修改与添加。本文我来学习并分析一下Spring中事件的原理。

举个例子,假设有一个添加评论的方法,在评论添加成功之后需要进行修改Redis缓存、给用户添加积分等等操作。当然可以在添加评论的代码后面假设这些操作,但是这样的代码违反了设计模式的多项原则:单一职责原则、迪米特法则、开闭原则。一句话说就是耦合性太大了,比如将来评论添加成功之后还需要有另外一个操作,这时候我们就需要去修改我们的添加评论代码了。

在以前的代码中,我使用观察者模式来解决这个问题。不过Spring中已经存在了一个升级版观察者模式的机制,这就是监听者模式。通过该机制我们就可以发送接收任意的事件并处理。

通过一个简单的demo来看看Spring事件通知的使用:

// 定义一个事件
public class EventDemo extends ApplicationEvent {
    private String message;


    public EventDemo(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

// 定义一个事件监听者
@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
    @Override
    public void onApplicationEvent(EventDemo event) {
        System.out.println("receiver " + event.getMessage());
    }
}

// 事件发布
@Component
public class EventDemoPublish {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void publish(String message) {
        EventDemo demo = new EventDemo(this, message);
        applicationEventPublisher.publishEvent(demo);
    }
}

调用EventDemoPublish.publish方法来发布消息,EventDemoListener监听器接收到消息后对消息进行处理,打印出消息的内容:

receiver hello

Spring事件通知原理

首先我们跟踪publishEvent方法,这个方法在AbstractApplicationContext类中。

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        // 如果event是ApplicationEvent对象
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        // 如果event不是ApplicationEvent对象,则将其包装成PayloadApplicationEvent事件,并获取对应的事件类型
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
        }
    }

    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        // 获取ApplicationEventMulticaster,调用`multicastEvent`方法广播事件
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // 如果当前命名空间还有父亲节点,也需要给父亲推送该消息
    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}

// 获取ApplicationEventMulticaster
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    if (this.applicationEventMulticaster == null) {
        throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
                "call 'refresh' before multicasting events via the context: " + this);
    }
    return this.applicationEventMulticaster;
}

经过上面的分析,我们看到事件是通过applicationEventMulticaster来广播出去的。

applicationEventMulticaster在Spring的启动过程中被建立,我们在之前的文章Spring启动过程分析1(overview)中分析过Spring的启动过程,在核心方法refresh中建立applicationEventMulticaster:

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
// 在Spring容器中初始化事件广播器,事件广播器用于事件的发布
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and reGISter them.
// 把Spring容器内的事件监听器和BeanFactory中的事件监听器都添加的事件广播器中。
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();

关注initApplicationEventMulticaster和registerListeners方法。

// 初始化事件广播器
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 如果用户手动新建了一个名为applicationEventMulticaster类型为ApplicationEventMulticaster的bean,则将这个bean作为事件广播器
    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 {
        // 否则新建一个SimpleApplicationEventMulticaster作为默认的事件广播器
        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() + "]");
        }
    }
}
// 注册监听器
protected void registerListeners() {
    // Register statically specified listeners first.
    // 把提前存储好的监听器添加到监听器容器中
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    // 获取类型是ApplicationListener的beanName集合,此处不会去实例化bean
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // Publish early application events now that we finally have a multicaster...
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    // 如果存在earlyEventsToProcess,提前处理这些事件
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

经过前面的分析,我们知道了事件广播器applicationEventMulticaster如何被构建,下面我们分析事件的广播过程。

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 根据event类型获取适合的监听器
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 获取SimpleApplicationEventMulticaster中的线程执行器,如果存在线程执行器则在新线程中异步执行,否则直接同步执行监听器中的方法
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    // 如果存在ErrorHandler,调用监听器方法如果抛出异常则调用ErrorHandler来处理异常。否则直接调用监听器方法
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        doInvokeListener(listener, event);
    }
}

经过上面的分析,我们知道了Spring如何发送并响应事件。下面我们来分析如何使Spring能够异步响应事件。

异步响应Event

默认情况下,Spring是同步执行Event的响应方法的。如果响应方法的执行时间很长会阻塞发送事件的方法,因此很多场景下,我们需要让事件的响应异步化。

为了更直观地说明Event的响应默认是同步的,我们修改一下EventDemoListener并增加一个EventDemoListener2:

@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
    Logger logger = LoggerFactory.getLogger(EventDemoListener.class);

    @Override
    public void onApplicationEvent(EventDemo event) {
        logger.info("receiver " + event.getMessage());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Component
public class EventDemoListener2 implements ApplicationListener<EventDemo> {
    Logger logger = LoggerFactory.getLogger(EventDemoListener2.class);

    @Override
    public void onApplicationEvent(EventDemo event) {
        logger.info("receiver 2 " + event.getMessage());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果如下:

在这里插入图片描述

执行结果显示:EventDemoListener2和EventDemoListener的执行间隔1秒,EventDemoListener2的执行和程序的结束也间隔1秒。

结果表示我们的响应程序是同步执行的,一个响应程序的执行会阻塞下一个响应程序的执行。

自定义SimpleApplicationEventMulticaster

通过前面的代码分析,我们发现如果SimpleApplicationEventMulticaster中的taskExecutor如果不为null,将在taskExecutor中异步执行响应程序。applicationEventMulticaster的新建在initApplicationEventMulticaster方法中,默认情况下它会新建一个SimpleApplicationEventMulticaster,其中的taskExecutor为null。

因此想要taskExecutor不为null,我们可以自己手动创建一个SimpleApplicationEventMulticaster然后设置一个taskExecutor。

修改Config类:

@Configuration
@ComponentScan("love.wangqi")
public class Config {
    @Bean
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(simpleAsyncTaskExecutor());
        return simpleApplicationEventMulticaster;
    }
}

此时再次执行程序,执行结果如下:

在这里插入图片描述

可以看到,EventDemoListener和EventDemoListener2是同时执行的,同时它们的执行没有阻塞主程序的执行。事件的响应做到了异步化。

@Async

前面我们看到,通过手动新建SimpleApplicationEventMulticaster并设置TaskExecutor可以使所有的事件响应程序都在另外的线程中执行,不阻塞主程序的执行。

不过这样也带来一个问题,那就是所有的事件响应程序都异步化了,某些场景下我们希望某些关系密切的响应程序可以同步执行另外一些响应程序异步执行。

这种场景下,我们就不能简单地新建SimpleApplicationEventMulticaster并设置TaskExecutor。

Spring中提供了一个@Async注解,可以将加上这个注解的方法在另外的线程中执行。通过这个注解我们可以将指定的事件响应程序异步化。

我们修改EventDemoListener,在onApplicationEvent中加上@Async注解;同时修改Config类:

@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
    Logger logger = LoggerFactory.getLogger(EventDemoListener.class);

    @Async
    @Override
    public void onApplicationEvent(EventDemo event) {
        logger.info("receiver " + event.getMessage());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Configuration
@ComponentScan("love.wangqi")
@EnableAsync
public class Config {
    @Bean
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
}

注意Config类中需要加上@EnableAsync注释,并定义TaskExecutor。

执行结果如下:

在这里插入图片描述

我们看到,EventDemoListener是在另外的线程中执行的,但是EventDemoListener2仍然在主线程中执行,因此EventDemoListener2阻塞了主线程的执行。

@Async原理

@Async注解可以将方法异步化,下面我们来看看它的原理是什么。

我们在Config类中添加了@EnableAsync注释。@EnableAsync注释引入AsyncConfigurationSelector类,AsyncConfigurationSelector类导入ProxyAsyncConfiguration类,ProxyAsyncConfiguration类新建过程中会新建AsyncAnnotationBeanPostProcessor。

AsyncAnnotationBeanPostProcessor类继承了BeanPostProcessor,当每个Bean新建完成后会调用AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法:

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (this.advisor == null || bean instanceof aopInfrastructureBean) {
        // Ignore AOP infrastructure such as scoped proxies.
        return bean;
    }

    if (bean instanceof Advised) {
        Advised advised = (Advised) bean;
        if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
            // Add our local Advisor to the existing proxy's Advisor chain...
            if (this.beforeExistingAdvisors) {
                advised.addAdvisor(0, this.advisor);
            }
            else {
                advised.addAdvisor(this.advisor);
            }
            return bean;
        }
    }

    if (isEligible(bean, beanName)) {
        ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
        if (!proxyFactory.isProxyTargetClass()) {
            evaluateProxyInterfaces(bean.getClass(), proxyFactory);
        }
        proxyFactory.addAdvisor(this.advisor);
        customizeProxyFactory(proxyFactory);
        return proxyFactory.getProxy(getProxyClassLoader());
    }

    // No proxy needed.
    return bean;
}

postProcessAfterInitialization方法判断bean是否符合要求(方法上是否加了@Async注释),如果符合要求则对bean加上代理,代理类为AnnotationAsyncExecutionInterceptor。

@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
    final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

    // 获取executor
    AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
    if (executor == null) {
        throw new IllegalStateException(
                "No executor specified and no default executor set on AsyncExecutionInterceptor either");
    }

    // 将我们真正的方法包装成一个`Callable`任务
    Callable<Object> task = () -> {
        try {
            Object result = invocation.proceed();
            if (result instanceof Future) {
                return ((Future<?>) result).get();
            }
        }
        catch (ExecutionException ex) {
            handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
        }
        catch (Throwable ex) {
            handleError(ex, userDeclaredMethod, invocation.getArguments());
        }
        return null;
    };
    
    // 将任务提交到`executor`中执行
    return doSubmit(task, executor, invocation.getMethod().getReturnType());
}

调用我们的方法时首先调用AnnotationAsyncExecutionInterceptor的invoke方法,invoke方法将我们真正的方法包装成一个Callable任务,将这个任务提交到executor中执行。

由此达到了将我们的方法异步化的目的。

总结

Spring的事件机制是一套相当灵活的机制,使用它可以简便地将我们的代码解耦从而优化我们的代码。经过前面的分析我们了解了其中的运行原理,这有助于我们更好地使用这套机制。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: SpringEvent事件通知机制解读

本文链接: https://www.lsjlt.com/news/196029.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SpringEvent事件通知机制解读
    目录Spring事件通知原理异步响应Event自定义SimpleApplicationEventMulticaster@Async@Async原理总结Spring的事件通知机制是一项...
    99+
    2023-02-14
    Spring事件机制 Spring事件通知机制 Event事件通知机制
  • Spring的事件机制知识点有哪些
    这篇文章主要讲解了“Spring的事件机制知识点有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring的事件机制知识点有哪些”吧!同步事件和异步事件同步事件: 在一个线程里,按顺序...
    99+
    2023-06-22
  • Spring的事件机制知识点详解及实例分析
    同步事件和异步事件 同步事件: 在一个线程里,按顺序执行业务,做完一件事再去做下一件事。 异步事件: 在一个线程里,做一个事的同事,可以另起一个新的线程...
    99+
    2024-04-02
  • React事件机制源码解析
    目录原理简述 源码浅析 委托事件绑定 listenToAllSupportedEventslistenToNativeEvent addTrappedEventListener 不需...
    99+
    2024-04-02
  • 深入理解JavaScript事件机制
    目录如何实现一个事件的发布订阅介绍下事件循环宏任务和微任务的区别如何实现一个事件的发布订阅 可以通过以下步骤实现 JavaScript 中的发布-订阅模式: 创建一个事件管理器对象。...
    99+
    2023-05-17
    JavaScript事件机制 JS事件机制
  • Vue实例揭秘:破解其事件处理和通信机制
    事件处理 Vue 实例是一个响应式的数据绑定对象,它处理来自 DOM 事件的事件。要为事件添加处理程序,可以使用 v-on 指令或者直接在 methods 对象中声明方法。 使用 v-on 指令 <button v-on:click...
    99+
    2024-02-18
    Vue.js 事件处理 组件通信 $emit $on $once
  • 通过AOP环绕通知如何实现事务控制
    目录通过AOP环绕通知实现事务控制1、导入相关的依赖2、配置连接池和开启AOP注解2、创建链接工具类3、AOP环绕事务类spring AOP 环绕通知的思路1、设计一个接口2、编写这...
    99+
    2024-04-02
  • 怎么理解Java线程间通信与等待/通知机制
    本篇内容主要讲解“怎么理解Java线程间通信与等待/通知机制”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解Java线程间通信与等待/通知机制”吧!一、概念简介1、线程通信在操作系统中,线...
    99+
    2023-06-02
  • SpringBoot通知机制的实现方式
    1. 快速创建maven管理的SpringBoot项目 1、访问 http://start.spring.io/ 2、 选择构建工具Maven Project、 Spring Boo...
    99+
    2024-04-02
  • 深入理解异步事件机制
    通过了解异步设计的由来,来深入理解异步事件机制。 代码地址 什么是异步 同步 并发(Concurrency) 线程(Thread) I/O多路复用 异步(Asynchronous) 回调(Callback) 参考文...
    99+
    2023-01-31
    机制 事件
  • Android事件分发机制的详解
    Android事件分发机制我们只考虑最重要的四个触摸事件,即:DOWN,MOVE,UP和CANCEL。一个手势(gesture)是一个事件列,以一个DOWN事件开始(当用户触摸屏幕时产生),后跟0个或多个MOVE事件(当用户四处移动手指时产...
    99+
    2023-05-30
    android 事件 分发
  • Android事件分发机制全面解析
    目录事件分发机制 ViewGroup.dispatchTouchEvent 源码分析 View.dispatchTouchEvent 和 View.onTouchEvent 源码分析...
    99+
    2024-04-02
  • Java的等待通知机制是什么
    这篇“Java的等待通知机制是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java的等待通知机制是什么”文章吧。Jav...
    99+
    2023-06-03
  • 事件冒泡是什么?深入解析事件冒泡机制
    事件冒泡是什么?深入解析事件冒泡机制 事件冒泡是Web开发中一个重要的概念,它定义了页面上事件传递的方式。当一个元素上的事件被触发时,事件将会从最内层的元素开始传递,逐级向外传递,直到...
    99+
    2024-02-22
    事件机制 深入解析 事件冒泡 点击事件
  • JavaScript事件流:深入理解事件处理和传播机制
    引言 JavaScript中的事件流是一种机制,用于描述和处理事件在DOM树中的传播过程。了解事件流的属性和工作原理对于编写高效的事件处理代码和实现复杂的交互功能至关重要。本文将详细介绍JavaSc...
    99+
    2023-09-14
    javascript 开发语言 ecmascript 原力计划
  • 如何正确的使用Java事件通知
    如何正确的使用Java事件通知,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。通过实现观察者模式来提供 Java 事件通知(Java event notification)似乎不...
    99+
    2023-06-17
  • 解析Spring事件发布与监听机制
    前言 Spring 提供了 ApplicationContext 事件机制,可以发布和监听事件,这个特性非常有用。 Spring 内置了一些事件和监听器,例如在 Spring 容器启...
    99+
    2024-04-02
  • vue.js的事件循环机制如何理解
    这篇文章主要介绍了vue.js的事件循环机制如何理解的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue.js的事件循环机制如何理解文章都会有所收获,下面我们一起来看看吧。一、事件循环机制介绍  &n...
    99+
    2023-06-29
  • JS的事件循环执行机制详解
    目录前言JS语言的特点JS中同步和异步的使用事件循环是什么?事件循环执行过程微任务和宏任务的区别JS执行/运行机制最后前言 在前端开发中,涉及到JS原生的使用原理是非常重要的知识点,...
    99+
    2023-05-19
    JS事件循环执行机制 JS事件循环 JS事件
  • 深入了解Node事件循环(EventLoop)机制
    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。下面本篇文章就来带大家掌握Node.js中的eventloop,希望对大家有所帮助!虽然js可以在浏览器...
    99+
    2023-05-14
    javascript Node.js 面试 前端
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作