Spring 源码学习(七)扩展功能 下篇(二)

简介: 在前面的 Spring 源码学习系列的文章中,深入分析和学习了 BeanFactoryPostProcessor ,主体是 BeanFactory 的后处理器,这次来学习主体是 Bean 的后处理器:BeanPostProcessor。

初始化消息资源

根据书中的内容介绍,这个消息资源 messageSource 是跟 Spring 国际化相关。

例如中美之间的中英文差别,在不同地区显示不同的资源。对于有国际化需求的系统,要为每种提供一套相应的资源文件,并以规范化命名的形式保存在特定的目录中,由系统自动根据客户端的语言或者配置选择合适的资源文件。

举个🌰:定义了两个资源文件,简单配置如下

  • 中文地区:test=测试
  • 英文地区:test=test

所以可以通过 Applicationcontext.getMessage() 方法访问国际化信息,在不同的环境中获取对应的数据。

由于个人感觉这种配置相关的,可以通过 profile 切换来实现,所以没有去细看和使用,具体实现和使用请感兴趣的同学们深入了解吧。


事件监听

事件传播器的使用很像我们设计模式中的观察者模式,被观察者变动后通知观察者进行相应的逻辑处理。

在了解 Spring 如何初始化事件传播器之前,来看下 Spring 监听的简单用法。

定义监听事件 Event

新建一个类,继承于 ApplicationEvent,并且需要在构造方法中调用父类的构造函数 supre(source)

public class CarEvent extends ApplicationEvent {
  /**
   * 自定义一个消息
   */
  private String msg;
  public CarEvent(Object source) {
    super(source);
  }
  public CarEvent(Object source, String msg) {
    super(source);
    this.msg = msg;
  }
}

定义监听器 Listener

新建一个类,引用 ApplicationListener 接口,然后重载 onApplicationEvent 方法:

public class CarEventListener implements ApplicationListener {
  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof CarEvent) {
      CarEvent carEvent = (CarEvent) event;
      System.out.println("source : " + event.getSource() + ",  custom message : " + carEvent.getMsg());
    }
  }
}

由于 Spring 的消息监听器不像 kafka 等主流 MQ 可以指定发送队列或者监听主题,只要发送消息后,所有注册的监听器都会收到消息进行处理,所以这边加了一个判断,如果是我业务上需要的消息,才会进行处理。


配置文件

<bean id="testListener" class="context.event.CarEventListener"/>

将刚才写的监听器注册到 Spring 容器中


测试代码

public class EventBootstrap {
  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("factory.bean/bean-post-processor.xml");
    // 第一个参数是来源,第二个参数是自定义
    CarEvent carEvent = new CarEvent("hello",  "world");
    context.publishEvent(carEvent);
    // 消息发送之后,打印以下内容
    // source : hello,  custom message : world
  }
}

由于在配置文件中注册了监听器,然后在启动代码汇总初始化了监听事件,最终通过 context 发送消息,发现输出结果与预想的一致。

这种观察者模式实现很经典,使用起来也很简单,下面来结合源码分析一下 Spring 是如何实现消息监听的功能。


消息监听代码分析

从源码中分析,发现主要是下面三个步骤:

初始化 ApplicationEvenMulticaster

protected void initApplicationEventMulticaster() {
  ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  // 如有有自己注册class Name 是 applicationEventMulticaster,使用自定义广播器
  if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
    this.applicationEventMulticaster =
        beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    }
  }
  else {
    // 没有自定义,使用默认的事件广播器
    this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
    beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
  }
}

广播器的作用是用来广播消息,在默认的广播器 SimpleApplicationEventMulticaster 类中发现了这个方法 multicastEvent

public void multicastEvent(final 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);
    }
  }
}

可以看到,在广播事件时,会遍历所有注册的监听器进行调用 invokeListener 方法,底层调用的是监听器重载的 listener.onApplicationEvent(event),所以再次强调一次,如果使用 Spring 自带的事件监听,请在业务处理方判断事件来源,避免处理错误。


注册监听器

在上一步中,已经初始化好了广播器,所以下一步来看下,监听器的注册流程,入口方法如下:

org.springframework.context.support.AbstractApplicationContext#registerListeners

protected void registerListeners() {
  // 这里是硬编码注册的监听器
  for (ApplicationListener<?> listener : getApplicationListeners()) {
    getApplicationEventMulticaster().addApplicationListener(listener);
  }
  // 不要在这里初始化 factoryBean : 我们需要保留所有常规 bean 未初始化,以便让后处理程序应用于它们!
  // 这一步是配置文件中注册的监听器
  String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
  for (String listenerBeanName : listenerBeanNames) {
    getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
  }
  // 发布早期的应用程序事件,现在我们终于有了一个多播器=-=
  Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
  this.earlyApplicationEvents = null;
  if (earlyEventsToProcess != null) {
    for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
      getApplicationEventMulticaster().multicastEvent(earlyEvent);
    }
  }
}

这一个方法代码不多,也没啥嵌套功能,按照注释顺序将流程梳理了一遍,将我们注册的监听器加入到 applicationEventMulticaster 列表中,等待之后调用。


publishEvent

广播器和监听器都准备好了,剩下的就是发送事件,通知监听器做相应的处理:

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)

核心是这行代码:

getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

通过获取事件广播器,调用 multicastEvent 方法,进行广播事件,这一步前面也介绍过了,不再细说。


总结

这次学习,省略了书中的一些内容,有关属性编辑器、SPEL 语言和初始化非延迟加载等内容,请感兴趣的同学继续深入了解~

我们也能从 Spring 提供的这些扩展功能中学习到,通过预留后处理器,可以在 bean 实例化之前修改配置信息,或者做其他的自定义操作,例如替换占位符、过滤敏感信息等;

也可以通过广播事件,定义事件和监听器,在监听器中实现业务逻辑,由于不是直接调用监听器,而是通过事件广播器进行中转,达到了代码解耦的效果。

所以在之后的代码设计和编写中,在整体设计上,有必要的话,考虑在更高的抽象层要预留扩展功能,然后让子类重载或者实现,实现扩展的功能。


由于个人技术有限,如果有理解不到位或者错误的地方,请留下评论,我会根据朋友们的建议进行修正

代码和注释都在里面,小伙伴们可以下载我上传的代码,亲测可运行~

Gitee 地址:https://gitee.com/vip-augus/spring-analysis-note.git

Github 地址:https://github.com/Vip-Augus/spring-analysis-note

相关文章
|
6月前
|
监控 Java 应用服务中间件
微服务——SpringBoot使用归纳——为什么学习Spring Boot
本文主要探讨为什么学习Spring Boot。从Spring官方定位来看,Spring Boot旨在快速启动和运行项目,简化配置与编码。其优点包括:1) 良好的基因,继承了Spring框架的优点;2) 简化编码,通过starter依赖减少手动配置;3) 简化配置,采用Java Config方式替代繁琐的XML配置;4) 简化部署,内嵌Tomcat支持一键式启动;5) 简化监控,提供运行期性能参数获取功能。此外,从未来发展趋势看,微服务架构逐渐成为主流,而Spring Boot作为官方推荐技术,与Spring Cloud配合使用,将成为未来发展的重要方向。
186 0
微服务——SpringBoot使用归纳——为什么学习Spring Boot
|
16天前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
366 70
|
4月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
164 32
|
4月前
|
安全 Java API
Spring Boot 功能模块全解析:构建现代Java应用的技术图谱
Spring Boot不是一个单一的工具,而是一个由众多功能模块组成的生态系统。这些模块可以根据应用需求灵活组合,构建从简单的REST API到复杂的微服务系统,再到现代的AI驱动应用。
|
5月前
|
Java Spring
Spring框架的学习与应用
总的来说,Spring框架是Java开发中的一把强大的工具。通过理解其核心概念,通过实践来学习和掌握,你可以充分利用Spring框架的强大功能,提高你的开发效率和代码质量。
139 20
|
5月前
|
SQL 前端开发 Java
深入理解 Spring Boot 项目中的分页与排序功能
本文深入讲解了在Spring Boot项目中实现分页与排序功能的完整流程。通过实际案例,从Service层接口设计到Mapper层SQL动态生成,再到Controller层参数传递及前端页面交互,逐一剖析每个环节的核心逻辑与实现细节。重点包括分页计算、排序参数校验、动态SQL处理以及前后端联动,确保数据展示高效且安全。适合希望掌握分页排序实现原理的开发者参考学习。
296 4
|
6月前
|
前端开发 Java 数据库连接
Spring MVC 扩展和SSM框架整合
通过以上步骤,我们可以将Spring MVC扩展并整合到SSM框架中。这个过程包括配置Spring MVC和Spring的核心配置文件,创建控制器、服务层和MyBatis的Mapper接口及映射文件。在实际开发中,可以根据具体业务需求进行进一步的扩展和优化,以构建更加灵活和高效的企业级应用程序。
135 5
|
6月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
105 0

热门文章

最新文章