• [源码系列:手写spring] IOC第十四节:容器事件和事件监听器


    代码分支

    https://github.com/yihuiaa/little-spring/tree/event-and-event-listenericon-default.png?t=N7T8https://github.com/yihuiaa/little-spring/tree/event-and-event-listener

    内容介绍

    事件监听器机制

            Spring的容器事件和事件监听器机制允许应用程序在容器中发生特定事件时执行自定义逻辑。这是一种基于观察者模式的设计,其中容器充当主题,而事件监听器则充当观察者。

    1. 事件(Event):事件是表示应用程序中某个特定事件的对象,通常扩展自ApplicationEvent类。您可以创建自定义事件来表示您关心的特定事件。

    2. 事件监听器(Event Listener):事件监听器是一个实现了ApplicationListener接口的Bean,它负责处理特定事件。监听器可以注册到容器中以接收特定类型的事件,并在事件发生时执行相应的操作。

    3. ApplicationEventMulticaster:这是Spring事件传播机制的关键部分,它负责将事件传播给注册的监听器。Spring提供了不同的ApplicationEventMulticaster实现,最常见的是SimpleApplicationEventMulticasterAsyncApplicationEventMulticaster

    4. AbstractApplicationContextAbstractApplicationContext是Spring上下文的抽象基类,它包括了事件发布和监听的功能。

    实现原理

    • ApplicationEventMulticasterApplicationEventMulticaster的主要职责是管理事件监听器以及在事件发生时通知它们。它通常是Spring应用程序上下文的一部分。

      • SimpleApplicationEventMulticaster这是最简单的ApplicationEventMulticaster实现,它将事件同步传播给所有已注册的监听器。在事件发布时,它遍历注册的监听器列表,并调用每个监听器的onApplicationEvent方法。

      • AsyncApplicationEventMulticasterSimpleApplicationEventMulticaster不同,AsyncApplicationEventMulticaster可以异步地传播事件。它使用线程池来处理事件监听器的通知,因此不会阻塞主线程。

    • AbstractApplicationContextAbstractApplicationContext是Spring上下文的抽象基类,它包括了事件发布和监听的功能。它具有以下主要方法:

      • publishEvent(ApplicationEvent event)用于发布事件。它会将事件传递给ApplicationEventMulticaster以进行分发。

      • addApplicationListener(ApplicationListener listener)用于向容器中注册事件监听器。

      • refresh()通常在容器启动时调用,用于初始化上下文和注册所有默认的事件监听器。实例化ApplicationEventMulticaster、注册监听器并发布容器刷新事件ContextRefreshedEvent。

      • doClose() : 发布容器关闭事件ContextClosedEvent。

    知识补充

    观察者模式

            察言观色、思考分析一直是人类认识客观事物的重要途径。观察行为通常是一种为了对目标状态变化做出及时响应而采取的监控及调查活动。观察者模式(Observer)可以针对被观察对象与观察者对象之间一对多的依赖关系建立起一种行为自动触发机制,当被观察对象状态发生变化时主动对外发起广播,以通知所有观察者做出响应。观察者往往眼观六路,耳听八方,随时监控着被观察对象的一举一动。作为主动方的观察者对象必须与被观察对象建立依赖关系,以获得其最新动态,例如记者与新闻、摄影师与景物、护士与病人、股民与股市等

            现实中的观察者(Observer)往往是主动方,这是由于目标主题(Subject)缺乏主观能动性造成的,其状态的更新并不能主动地通知观察者,这就造成观察行为的持续往复。而在软件设计中我们可以将目标主题作为主动方角色,将观察者反转为被动方角色,建立反向驱动式的消息响应机制,以此来避免做无用功,优化软件效率,请参看观察者模式的类结构,如图所示。

    观察者模式的类结构

    观察者模式的各角色定义如下。

    ■ Subject(目标主题):被观察的目标主题的接口抽象,维护观察者对象列表,并定义注册方法register()(订阅)与通知方法notify()(发布)。对应本章例程中的商店类Shop。

    ■ ConcreteSubject(主题实现):被观察的目标主题的具体实现类,持有一个属性状态State,可以有多种实现。对应本章例程中的商店类Shop。

    ■ Observer(观察者):观察者的接口抽象,定义响应方法update()。对应本章例程中的买家类Buyer。

    ■ ConcreteObserver(观察者实现):观察者的具体实现类,可以有任意多个子类实现。实现了响应方法update(),收到通知后进行自己独特的处理。对应本章例程中的手机买家类PhoneFans、海淘买家类HandChopper。 

     一对多关系

            基于这种一对多的关系网,观察者模式以多态化(泛型化)的方式弱化了目标主题与观察者之间强耦合的依赖关系,标准化它们的消息交互接口,并让主客关系发生反转,以“单方驱动全局”模式取代“多方持续轮询”模式,使目标主题(单方)的任何状态更新都能被即刻通过广播的形式通知观察者们(多方),解决了状态同步知悉的效率问题。

                                                                                                                  ——《秒懂设计模式》        

    案例

            现在,让我们提供一个使用案例来演示Spring容器事件和事件监听器的工作原理:

            假设你正在构建一个在线商店应用程序,并希望在用户下订单时发送邮件通知。首先,你需要创建一个自定义事件来表示订单的提交:

    1. import org.springframework.context.ApplicationEvent;
    2. public class OrderEvent extends ApplicationEvent {
    3. private String orderId;
    4. public OrderEvent(Object source, String orderId) {
    5. super(source);
    6. this.orderId = orderId;
    7. }
    8. public String getOrderId() {
    9. return orderId;
    10. }
    11. }

    接下来,需要创建一个事件监听器来处理订单事件并发送邮件通知:

    1. import org.springframework.context.ApplicationListener;
    2. public class EmailNotificationListener implements ApplicationListener {
    3. @Override
    4. public void onApplicationEvent(OrderEvent event) {
    5. // 在这里编写发送邮件通知的逻辑,可以使用JavaMail等库
    6. String orderId = event.getOrderId();
    7. System.out.println("Sending email notification for order: " + orderId);
    8. }
    9. }

    然后,在Spring配置文件中注册事件监听器:
     

    <bean class="com.example.EmailNotificationListener" />
    

    最后,在应用程序中发布订单事件:

    1. import org.springframework.context.ApplicationContext;
    2. import org.springframework.context.support.ClassPathXmlApplicationContext;
    3. public class OrderApplication {
    4. public static void main(String[] args) {
    5. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    6. // 模拟用户下订单
    7. String orderId = "12345";
    8. OrderEvent orderEvent = new OrderEvent(context, orderId);
    9. // 发布订单事件
    10. context.publishEvent(orderEvent);
    11. }
    12. }

            当你运行OrderApplication时,它会发布订单事件,触发EmailNotificationListener中的邮件通知逻辑,从而实现了基于事件的订单通知功能。在真实场景下,可以创建一个专门的订单服务或业务逻辑组件,用于处理订单的创建和其他相关操作。在这个服务中注入ApplicationContext以获取事件发布的能力。

    核心代码

    ApplicationEvent(事件接口)

              继承了JDK自带的EventObject接口,source记录最初的事件源。

    1. public abstract class ApplicationEvent extends EventObject {
    2. public ApplicationEvent(Object source) {
    3. super(source);
    4. }
    5. }

    ApplicationListener(监听器接口)

            继承了JDK自带的EventListener接口,ApplicationListener 接口是 Spring Framework 中的一个重要接口,用于监听应用程序中的事件,并在事件发生时执行特定的操作。ApplicationListener 接口包含一个方法 onApplicationEvent(ApplicationEvent event),该方法用于处理接收到的事件。

    1. public interface ApplicationListenerextends ApplicationEvent> {
    2. /**
    3. * 观察者的响应方法
    4. * @param event
    5. */
    6. void onApplicationEvent(E event);
    7. }

    ApplicationEventPublisher(事件发布者)

    1. public interface ApplicationEventPublisher {
    2. /**
    3. * 发布事件
    4. *
    5. * @param event
    6. */
    7. void publishEvent(ApplicationEvent event);
    8. }

    ApplicationEventMulticaster(事件广播器接口)

    包括添加事件监听器,删除时间监听器,广播事件的方法定义。

    1. public interface ApplicationEventMulticaster {
    2. void addApplicationListener(ApplicationListener listener);
    3. void removeApplicationListener(ApplicationListener listener);
    4. void multicastEvent(ApplicationEvent event);
    5. }

    AbstractApplicationEventMulticaster(抽象广播器类实现通用方法和属性)

    1. public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {
    2. /**
    3. * 监听器集合,相当于观察者模式中的观察者列表
    4. */
    5. public final Set> applicationListeners = new HashSet<>();
    6. private BeanFactory beanFactory;
    7. @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    8. this.beanFactory = beanFactory;
    9. }
    10. @Override public void addApplicationListener(ApplicationListener listener) {
    11. applicationListeners.add((ApplicationListener) listener);
    12. }
    13. @Override public void removeApplicationListener(ApplicationListener listener) {
    14. applicationListeners.remove(listener);
    15. }
    16. }

    SimpleApplicationEventMulticaster(简单广播器实现类)

    1. public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster{
    2. /**
    3. * 创建一个 SimpleApplicationEventMulticaster 实例,并指定用于查找监听器的 BeanFactory。
    4. *
    5. * @param beanFactory 用于查找监听器的 BeanFactory
    6. */
    7. public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
    8. setBeanFactory(beanFactory);
    9. }
    10. /**
    11. * 广播给定的应用程序事件到所有注册的监听器。
    12. * @param event 要多播的应用程序事件
    13. */
    14. @Override public void multicastEvent(ApplicationEvent event) {
    15. for (ApplicationListener listener : applicationListeners) {
    16. if(supportsEvent(listener,event){
    17. listener.onApplicationEvent(event);
    18. }
    19. }
    20. }
    21. /**
    22. * 检查监听器是否对给定的事件感兴趣。
    23. *
    24. * @param applicationListener 要检查的监听器
    25. * @param event 要检查的事件
    26. * @return 如果监听器支持处理事件,则返回 true,否则返回 false
    27. */
    28. protected boolean supportsEvent(ApplicationListener applicationListener, ApplicationEvent event) {
    29. //适用于简单实例化策略SimpleInstantiationStrategy
    30. //todo 有兴趣的同学可以完善cglib的实例化方法加以适配
    31. //获取泛型接口
    32. Type genericInterfaceType = applicationListener.getClass().getGenericInterfaces()[0];
    33. // 获取泛型接口中的实际类型参数,也就是是事件的类型
    34. Type actualTypeArgument = ((ParameterizedType)genericInterfaceType).getActualTypeArguments()[0];
    35. //事件名称
    36. String eventClassName = actualTypeArgument.getTypeName();
    37. Class eventClass;
    38. try {
    39. eventClass = Class.forName(eventClassName);
    40. }catch (ClassNotFoundException e) {
    41. throw new BeansException("wrong event class name: " + eventClassName);
    42. }
    43. // 检查事件是否是监听器支持的类型
    44. // 如果事件类是监听器泛型参数的子类或实现类,则返回 true,表示监听器支持处理此事件
    45. return eventClass.isAssignableFrom(event.getClass());
    46. }
    47. }

    ContextClosedEvent

    表示当应用程序上下文(ApplicationContext)关闭时触发的事件。   

    1. public class ContextClosedEvent extends ApplicationContextEvent {
    2. public ContextClosedEvent(ApplicationContext source) {
    3. super(source);
    4. }
    5. }

     ContextRefreshedEvent

    表示当应用程序上下文(ApplicationContext)成功刷新并初始化后触发的事件。

    1. public class ContextRefreshedEvent extends ApplicationContextEvent {
    2. public ContextRefreshedEvent(ApplicationContext source) {
    3. super(source);
    4. }
    5. }

    ApplicationContext

    添加发布事件的能力,继承发布者接口。

    1. public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader,ApplicationEventPublisher {
    2. }

    AbstractApplicationContext

    修改刷新容器的方法,刷新容器时初始化事件广播器,注册事件监听器,发布容器刷新事件。

    1. ...
    2. @Override public void refresh() throws BeansException {
    3. //创建BeanFactory,并加载BeanDefinition
    4. refreshBeanFactory();
    5. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    6. //添加ApplicationContextAwareProcessor,让继承自ApplicationContextAware的bean能感知bean
    7. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    8. //执行BeanFactoryPostProcessor
    9. invokeBeanFactoryPostProcessors(beanFactory);
    10. //BeanPostProcessor的实例化提前与其他bean
    11. registerBeanPostProcessors(beanFactory);
    12. //初始化事件广播器
    13. initApplicationEventMulticaster();
    14. //注册事件监听器
    15. registerListeners();
    16. //提前实例化单例Bean
    17. beanFactory.preInstantiateSingletons();
    18. //发布容器刷新完成事件
    19. finishRefresh();
    20. }
    21. /**
    22. * 初始化事件广播器
    23. */
    24. private void initApplicationEventMulticaster(){
    25. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    26. applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
    27. beanFactory.addSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,applicationEventMulticaster);
    28. }
    29. /**
    30. * 注册事件监听器
    31. */
    32. private void registerListeners(){
    33. Collection listeners = getBeansOfType(ApplicationListener.class).values();
    34. for(ApplicationListener listener : listeners){
    35. this.applicationEventMulticaster.addApplicationListener(listener);
    36. }
    37. }
    38. /**
    39. * 发布容器刷新完成事件
    40. */
    41. protected void finishRefresh() {
    42. publishEvent(new ContextRefreshedEvent(this));
    43. }
    44. @Override public void publishEvent(ApplicationEvent event) {
    45. applicationEventMulticaster.multicastEvent(event);
    46. }
    47. protected void doClose(){
    48. //发布容器关闭事件
    49. publishEvent(new ContextClosedEvent(this));
    50. destroyBeans();
    51. }
    52. ...

    测试

    测试代码

    event-and-event-listener.xml

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans
    6. http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/context
    8. http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    9. <bean class="common.event.ContextRefreshedEventListener"/>
    10. <bean class="common.event.CustomEventListener"/>
    11. <bean class="common.event.ContextClosedEventListener"/>
    12. beans>

    ContextClosedEventListener

    1. /**
    2. * ● @author: YiHui
    3. * ● @date: Created in 2023/9/10
    4. * ● @notes: 容器关闭事件监听器
    5. */
    6. public class ContextClosedEventListener implements ApplicationListener {
    7. @Override
    8. public void onApplicationEvent(ContextClosedEvent event) {
    9. System.out.println(this.getClass().getName()+ "::我听到了-容器关闭了-开始执行ContextClosedEvent");
    10. }
    11. }

    ContextRefreshedEventListener

    1. /**
    2. * ● @author: YiHui
    3. * ● @date: Created in 2023/9/10
    4. * ● @notes: 容器刷新事件监听器
    5. */
    6. public class ContextRefreshedEventListener implements ApplicationListener {
    7. @Override
    8. public void onApplicationEvent(ContextRefreshedEvent event) {
    9. System.out.println(this.getClass().getName()+ "::我听到了-容器刷新了-开始执行ContextRefreshedEvent");
    10. }
    11. }

     自定义事件和监听器

    1. /**
    2. * ● @author: YiHui
    3. * ● @date: Created in 2023/9/10
    4. * ● @notes:自定义事件
    5. */
    6. public class CustomEvent extends ApplicationContextEvent {
    7. public CustomEvent(ApplicationContext source) {
    8. super(source);
    9. }
    10. }
    1. public class CustomEventListener implements ApplicationListener {
    2. @Override
    3. public void onApplicationEvent(CustomEvent event) {
    4. System.out.println(this.getClass().getName()+ "::我听到了-自定义监听器-开始执行我的事件");
    5. }
    6. }

    单元测试

    1. public class EventAndEventListenerTest {
    2. @Test
    3. public void testEventListener() throws Exception {
    4. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:event-and-event-listener.xml");
    5. applicationContext.publishEvent(new CustomEvent(applicationContext));
    6. applicationContext.registerShutdownHook();//或者applicationContext.close()主动关闭容器;
    7. }
    8. }

    测试结果

    1. common.event.ContextRefreshedEventListener::我听到了-容器刷新了-开始执行ContextRefreshedEvent
    2. common.event.CustomEventListener::我听到了-自定义监听器-开始执行我的事件
    3. common.event.ContextClosedEventListener::我听到了-容器关闭了-开始执行ContextClosedEvent


            

  • 相关阅读:
    linux下下载部署nginx
    如何理解Java是按值传递
    SRS WebRTC Whip 和 Whep 部署体验问题
    摸鱼有理:大脑一思考就在积累毒素,必须休息才能清除|Cell子刊
    c语言分层理解(c语言字符串+内存库函数)
    基于最小均方误差linear minimum mean square error(LMMSE)插值算法的图像超分辨重构研究-附Matlab代码
    封装弹出框vue3
    (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
    照片批量处理 7000张
    多步验证Odoo功能模块
  • 原文地址:https://blog.csdn.net/weixin_43848166/article/details/132714669