Spring 源码学习 14:initApplicationEventMulticaster、onRefresh 和 registerListeners

简介: 上一篇介绍了国际化的使用以及初始化消息源的源码,接下来接着往下阅读,将进入 initApplicationEventMulticaster 、onRefresh 和 registerListeners 的相关操作逻辑。这一部分主要是初始化事件广播器以及注册监听器。而 onRefresh 部分则需要子类去实现。 所以本文主要介绍以下几个部分:1. 什么是 Spring 事件?2. 监听器是如何使用的?

前言


上一篇介绍了国际化的使用以及初始化消息源的源码,接下来接着往下阅读,将进入 initApplicationEventMulticaster 、onRefresh 和 registerListeners 的相关操作逻辑。

这一部分主要是初始化事件广播器以及注册监听器。而 onRefresh 部分则需要子类去实现。 所以本文主要介绍以下几个部分:

  1. 什么是 Spring 事件?
  2. 监听器是如何使用的?


什么是 Spring 事件?

网络异常,图片无法展示
|

这块的介绍在官网 1.15.2. Standard and Custom Events 部分有介绍。

Spring 通过 ApplicationEvent 类和 ApplicationListener 接口提供 ApplicationContext 中的事件处理。如果将实现 ApplicationListener 接口的 bean 部署到上下文中,则每次将 ApplicationEvent 发布到 ApplicationContext 时,都会通知该 bean。本质上,这是标准的观察者设计模式。

归纳下来主要就是三个部分: 事件、事件发布者、事件监听器。

  1. 事件:ApplicationEvent,要自定义事件,则需要创建一个类继承 ApplicationEvent。
  2. 事件发布者:ApplicationEventPublisher 和 ApplicationEventMulticaster,因为 ApplicationContext 实现了 ApplicationEventPublisher,所以事件发布可以直接使用 ApplicationContext。
  3. 事件监听器:ApplicationListener,通过创建一个实现了 ApplicationListener 并注册为 Spring bean 的类来接收消息。

Spring 也提供了也有一些内置的监听器,可以在官网查看,这里就不做介绍了。


使用监听器

简单来说主要分为以下几个部分:

  1. 注册事件
  2. 注册监听器
  3. 发布事件

在接口调用发布事件时,监听器就会做出相应的操作。


1. 注册事件

创建 MyApplicationEvent 类并继承 ApplicationEvent

public class MyApplicationEvent extends ApplicationEvent {
  private static final long serialVersionUID = 5366526231219883438L;
  private String message;
  /**
   * Create a new {@code ApplicationEvent}.
   *
   * @param source the object on which the event initially occurred or with
   *               which the event is associated (never {@code null})
   */
  public MyApplicationEvent(Object source, String message) {
    super(source);
    this.message = message;
  }
  public String getMessage() {
    return message;
  }
}


2. 注册监听器

@Component
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
  @Override
  public void onApplicationEvent(MyApplicationEvent event) {
    System.out.println("MyApplicationListener 收到消息: " + event.getMessage());
  }
}


当然这里也可以使用注解 @EventListener 的方式来使用。

@Component
public class MyAnnotationApplicationListener {
  @EventListener(classes = MyApplicationEvent.class)
  public void myApplicationEventListener(MyApplicationEvent event) {
    System.out.println("使用注解的方式, 收到事件: " + event.getMessage());
  }
}


3. 使用

因为 AnnotationConfigApplicationContext 实现了 ApplicationContext , 而 ApplicationContext 实现了 ApplicationEventPublisher,所以这块传入当前 context 是没有问题的。

public class AnnotationConfigApplicationTest {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(JavaConfig.class);
    context.refresh();
    MyApplicationEvent myApplicationEvent = new MyApplicationEvent(context, "呼叫土豆,呼叫土豆!");
    context.publishEvent(myApplicationEvent);
  }
}

日志输出:

网络异常,图片无法展示
|


源码部分


initApplicationEventMulticaster

这块和上面初始化消息源类似,都是查找指定名称的 Bean ,如果找不到,则自己使用默认的。

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 是否包含 applicationEventMulticaster 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 创建一个 事件发布器
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        simpleApplicationEventMulticaster.setApplicationStartup(getApplicationStartup());
        this.applicationEventMulticaster = simpleApplicationEventMulticaster;
        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() + "]");
        }
    }
}


onRefresh

这块需要子类去实现,我这里通过断电,暂时没有进去。所以就不介绍了。


registerListeners

protected void registerListeners() {
    // 添加实现ApplicationListener作为侦听器的bean。
    // 不会影响其他侦听器,可以将它们添加为非bean。
    // 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!
    // 只是添加 并没有执行
    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...
    // 发布早期的时间,并且将 earlyApplicationEvents 设置为空
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}


总结


这篇文章主要内容是介绍 Spring 事件的使用,同时简单介绍了 initApplicationEventMulticaster 、onRefresh 和 registerListeners 部分的源码。

网络异常,图片无法展示
|


目录
相关文章
|
11天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
1天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
20 9
|
21天前
|
前端开发 Java 数据库
SpringBoot学习
【10月更文挑战第7天】Spring学习
33 9
|
22天前
|
XML Java 数据格式
Spring学习
【10月更文挑战第6天】Spring学习
19 1
|
27天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
27天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
26天前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
51 2
|
26天前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
47 1
|
26天前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
19 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
26天前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
21 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现