之前的文章链接:https://blog.csdn.net/qq_41907991/article/details/88544777

我们要知道的是,Spring的publish-event使用的是监听者模式

监听者模式包含了一个监听者Listener与之对应的事件Event,还有一个事件发布者EventPublish,过程就是EventPublish发布一个事件,被监听者捕获到,然后执行事件相应的方法

监听者模式跟观察者模式的区别:

观察者模式是一对多的模式,一个被观察者Observable和多个观察者Observer,被观察者中存储了所有的观察者对象,当被观察者接收到一个外界的消息,就会遍历广播推算消息给所有的观察者

  1. 首先我们看下两个发布事件的方法,可以发现,最后都会调到void publishEvent(Object event)
public interface ApplicationEventPublisher {

   default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
} void publishEvent(Object event); }
  1. 继续跟踪代码:

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null"); // 如果是一个ApplicationEvent,就强转
    // 如果不是的话,包装成一个PayloadApplicationEvent
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
    applicationEvent = (ApplicationEvent) event;
    }
    else {
    applicationEvent = new PayloadApplicationEvent<>(this, event);
    if (eventType == null) {
    // 包装成一个PayloadApplicationEvent,并获取实际的带泛型的事件类型
    // 比如PayloadApplicationEvent<MyEvent>
    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 {
    // 1.获取一个applicationEventMulticaster
    // 2.广播事件
    getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    } // 如果当前容易存在父容器,父容器也要发布事件
    if (this.parent != null) {
    if (this.parent instanceof AbstractApplicationContext) {
    ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
    }
    else {
    this.parent.publishEvent(event);
    }
    }
    }
    1. 我们来看下ApplicationEventMulticaster的初始化
    protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 我们自定义了ApplicationEventMulticaster,就直接用我们自定义的
    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 {
    // 如果我们没定义ApplicationEventMulticaster,默认使用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() + "]");
    }
    }
    }

    ​ 通过这段代码,我们可以知道,我们如果申明了一个ApplicationEventMulticaster类,会覆盖默认的SimpleApplicationEventMulticaster。

    1. 注册监听者
    protected void registerListeners() {
    // 把提前存储好的监听器添加到监听器容器中
    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!
    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;
    if (earlyEventsToProcess != null) {
    for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
    getApplicationEventMulticaster().multicastEvent(earlyEvent);
    }
    }
    }
    1. 看完上面的代码,我相信大家多spring的publish-event机制已经有了一定的了解,现在我们再来探讨一个问题-------怎么实现异步机制

    我继续看getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType)这段代码:

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

    可以发现,在通知监听者的时候,会去检查ApplicationEventMulticaster对象中是否配置了Executor执行器,如果配置了的话,会将invokeListener(listener, event)封装成一个线程可执行任务,然后给执行器执行。

    这样的话,我们可以自定义一个ApplicationEventMulticaster:

    //需要注意的是,我们的名字一定要叫applicationEventMulticaster,否则不生效,回头看下源码就明白了
    @Component(&quot;applicationEventMulticaster&quot;)
    public class MyMulticaster extends SimpleApplicationEventMulticaster { @Override
    protected Executor getTaskExecutor() {
    // 这里可以按实际需求进行线程池的配置
    return new SimpleAsyncTaskExecutor();
    } /**
    * 这个方法主要配置错误处理,默认只会打一个警告级别的日志
    *
    * @return
    */
    @Override
    protected ErrorHandler getErrorHandler() {
    return (t -&gt; {
    System.out.println(&quot;出错了&quot;);
    });
    }
    }

    通过上面的配置后,我们所有的publish-event都会通过异步的机制去执行,但是我们想一个问题,有时候我们不想异步呢?一部分我们想同步,一部分想异步,这个时候怎么做呢?我们可以通过下面这种方式:

    • 配置类上加注解@EnableAsync

    • // 执行的方法上加@Async注解
      @EventListener
      @Async
      public void on(MySecondEvent event) throws Exception { }

    这样通过aop的方式,会将我们的方法采用异步的方式进行执行。我们没有配置线程池的时候可能会报错:

    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.core.task.TaskExecutor' available

    不过我们方法还是会异步执行,这主要是下面这段代码:

    protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
    if (beanFactory != null) {
    try {
    // 在这里会抛出一个NoSuchBeanDefinitionException
    return beanFactory.getBean(TaskExecutor.class);
    }
    catch (NoUniqueBeanDefinitionException ex) {
    logger.debug("Could not find unique TaskExecutor bean", ex);
    try {
    return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
    }
    catch (NoSuchBeanDefinitionException ex2) {
    if (logger.isInfoEnabled()) {
    logger.info("More than one TaskExecutor bean found within the context, and none is named " +
    "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " +
    "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
    }
    }
    }
    // 这里被捕获
    catch (NoSuchBeanDefinitionException ex) {
    logger.debug("Could not find default TaskExecutor bean", ex);
    try {
    // 我们也没有定义一个名字是DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor"
    // 的线程池
    return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
    }
    catch (NoSuchBeanDefinitionException ex2) {
    logger.info("No task executor bean found for async processing: " +
    "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
    }
    // Giving up -> either using local default executor or none at all...
    }
    }
    // 最后会返回一个null
    return null;
    }
    @Override
    @Nullable
    // 最后会返回一个SimpleAsyncTaskExecutor
    protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
    Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
    return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
    }

Spring 学习 之 再探publish-event机制的更多相关文章

  1. Spring学习之旅(四)Spring工作原理再探

    上篇博文对Spring的工作原理做了个大概的介绍,想看的同学请出门左转.今天详细说几点. (一)Spring IoC容器及其实例化与使用 Spring IoC容器负责Bean的实例化.配置和组装工作有 ...

  2. 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)

    再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...

  3. spring学习12 -Spring 框架模块以及面试常见问题注解等

    以下为spring常见面试问题: 1.Spring 框架中都用到了哪些设计模式? Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的: 代理模式—在AOP和remoting中被用的比较 ...

  4. Spring 学习记录8 初识XmlWebApplicationContext(2)

    主题 接上文Spring 学习记录7 初识XmlWebApplicationContext refresh方法 refresh方法是定义在父类AbstractApplicationContext中的. ...

  5. 【再探backbone 02】集合-Collection

    前言 昨天我们一起学习了backbone的model,我个人对backbone的熟悉程度提高了,但是也发现一个严重的问题!!! 我平时压根没有用到model这块的东西,事实上我只用到了view,所以昨 ...

  6. 不错的Spring学习笔记(转)

    Spring学习笔记(1)----简单的实例 ---------------------------------   首先需要准备Spring包,可从官方网站上下载.   下载解压后,必须的两个包是s ...

  7. spring学习 8-面试(事务,解决线程安全)

    1.介绍一下Spring的事物管理 参考:Spring 学习7 -事务 2.Spring如何处理线程并发问题    Spring使用ThreadLocal解决线程安全问题 参考:Spring学习11- ...

  8. Spring学习(一)初识Spring

    什么是Spring 定义:Spring 是一个轻量级的 DI / IoC 和 AOP 容器的开源框架,目的为了简化java开发. DI:注入 IOC:控制反转 AOP:面向切面编程 原理:利用了jav ...

  9. Spring学习笔记之二----基于XML的Spring AOP配置

    在Spring配置文件中,通常使用<aop:config>元素来设置AOP,其中应包括: <aop:aspect>指定aspect,aspect是一个POJO类,包含了很多的a ...

随机推荐

  1. 选择IT行业的自我心得,希望能帮助到各位!(五)

    相信很多小伙伴,在看完之前的一二三四,也是我一路走来,走走停停,走走停停,有快乐,也有伤悲,毕竟这就是人生嘛,人生不起起伏伏怎么才能体验刺激的快感,也让我从一个小男孩净化成清高浮躁的青少年,在从而让我 ...

  2. Mac 窗口管理软件 Spectacle

    我个人使用的窗口管理软件是 Magnet(本人在 Mac 下付费的首款软件,记得是 6 元~),今天为大家介绍一款类似的开源软件. 简介 Spectacle 是一款可以快速调整窗口大小与位置的开源软件 ...

  3. Labyrinth 树的直径加DFS

    The northern part of the Pyramid contains a very large and complicated labyrinth. The labyrinth is d ...

  4. 判断一个字符串是否是合法IP地址

    # -*- coding: utf-8 -*- """ @File:test06_判断ip地址是否合法.py @E-mail:364942727@qq.com @Time ...

  5. JavaScript函数作用域和声明提前(3.10.1 page.57)

    <h4>3.函数作用域和声明提前</h4> <p> <!--<script type="text/javascript">-- ...

  6. [转载]MySQL中int(11)最大长度是多少?

    原文地址:https://blog.csdn.net/allenjay11/article/details/76549503 今天在添加数据的时候,发现当数据类型为 int(11) 时,我当时让用户添 ...

  7. 通达OA任意用户登录 漏洞复现

    0x00 漏洞简介 通达OA国内常用的办公系统,使用群体,大小公司都可以,其此次安全更新修复的高危漏洞为任意用户登录漏洞.攻击者在远程且未经授权的情况下,通过利用此漏洞,可以直接以任意用户身份登录到系 ...

  8. BUU刷题01

    [安洵杯 2019]easy_serialize_php 直接给了源代码 <?php $function = @$_GET['f']; function filter($img){ $filte ...

  9. 散列表和JAVA中的hash

    引文 hello,今天写的数据结构是散列表(hash表),也算是一种基础数据结构了吧.学过计算机的人大概都能说出来这是个以空间换时间的东西,那么具体怎么实现的是今天要讨论的问题. 为什么需要它?主要还 ...

  10. 必须先理解的RocketMQ入门手册,才能再次深入解读

    RocketMQ入门手册 RocketMQ是一个分布式.队列模型的开源消息中间件,前身是MetaQ,是阿里研发的一个队列模型的消息中间件,后开源给apache基金会成为了apache的顶级开源项目,具 ...