Spring-事件

Java 事件/监听器编程模型

设计模式-观察者模式的拓展

  • 可观察者对象(消息发送者) Java.util.Observalbe
  • 观察者 java.util.Observer

标准化接口(标记接口)

  • 事件对象 java.util.EventObject
  • 事件监听器 java.util.EventListener
public class ObserverDemo {
    public static void main(String[] args) {
        Observable observable = new EventObservable();
        observable.addObserver(new EventObserver());
        observable.notifyObservers("hello");
    }

    /**
     * 因为我们要调用 change 监听者这个方法才能生效 但是这个方法是个protected 所以我们进行拓展
     * 
     */
    static class EventObservable extends Observable {
        public void setChanged(){
            super.setChanged();
        }
        public void notifyObservers(Object args) {
            setChanged();
            super.notifyObservers(new EventObject(args));
            clearChanged();
        }
    }
    static class EventObserver implements Observer, EventListener {
        @Override
        public void update(Observable o, Object event) {
            EventObject eventObject = (EventObject) event;
            System.out.println("收到消息:" + eventObject);
        }
    }
}

理解:
发布事件的是被监听的对向,里面会注册监听器,也就是需要感知当前对象变化的对象。
JDKEventListener提供了这个标记接口,算是一种规范,表名这个是事件的监听器。
EventObject 这个也算是一个标准,这个对象是方便数据在事件发布的时候进行传递。

面向接口的事件/监听器设计模式

在这里插入图片描述
基本模式:
一般监听器会继承EventListener
一般事件会继承EventObject

面向注解的事件/监听器设计模式

在这里插入图片描述

Spirng 标准事件 ApplicationEvent

在这里插入图片描述

基于接口的事件监听器

在这里插入图片描述

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    // 注册一个事件监听
    context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println(event.getTimestamp() + "接收到事件 : " + event);
        }
    });
    context.refresh();
    context.close();
}

可以看到这里收到了两个事件,那么事件从哪里发布的呢?请看事件发布器。
在这里插入图片描述

基于注解的事件监听器

在这里插入图片描述

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(ApplicatonListenrDemo.class);

    context.refresh();

    context.close();
}
@EventListener
public void onApplicationEvent(ApplicationEvent applicationEvent) {
    System.out.println(applicationEvent);
}
// 这样的话就会分类别来处理
@EventListener
public void onApplicationEvent(ContextRefreshedEvent applicationEvent) {
   System.out.println(applicationEvent + "re");
}
// 异步处理
@EnableAsync
public class ApplicatonListenrDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ApplicatonListenrDemo.class);

        context.refresh();

        context.close();
    }
    @EventListener
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println(applicationEvent);
    }
    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent applicationEvent) {
        System.out.println(applicationEvent + "re");
    }
    @EventListener
    @Async
    public void onApplicationEvent(ContextClosedEvent applicationEvent) {
        System.out.println(applicationEvent);
        System.out.println(Thread.currentThread().getId());
    }
}
// 控制顺序
public class ApplicatonListenrDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ApplicatonListenrDemo.class);

        context.refresh();

        context.close();
    }

    @EventListener
    @Order(2)
    public void onApplicationEvent2(ContextRefreshedEvent applicationEvent) {
        System.out.println("=======");
    }
    @EventListener
    @Order(1)
    public void onApplicationEvent1(ContextRefreshedEvent applicationEvent) {
        System.out.println("****");
    }
}

注册Spirng ApplicationListenner

方法一: ApplicationListener 作为SpirngBean注册

context.register(MyEventListener.class);
static class MyEventListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("=====>");
    }
}

方法二:通过ConfigrableApplicationContextAPI 注册

context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
   @Override
   public void onApplicationEvent(ApplicationEvent event) {
       System.out.println(event.getTimestamp() + "接收到事件 : " + event);
   }
});

事件发布器

在这里插入图片描述

public class ApplicatonListenrDemo implements ApplicationEventPublisherAware {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ApplicatonListenrDemo.class);
        context.refresh();
        context.start();
        context.close();
    }
    @EventListener
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println(event);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        applicationEventPublisher.publishEvent(new ApplicationEvent("hello") {
        });
        // 发布任意对象 重载方法
        applicationEventPublisher.publishEvent("yes");
    }
}

事件发布器如何找到对应的监听器进行实事件的发布呢? 在发布时间的时候会查缓存,缓存如果没有对应的监听器,则会更具事件泛型类型进行判断。
在这里插入图片描述
根据事件的泛型类型进行判断,如果类型符合加入监听器数组。
在这里插入图片描述

Spring 事件传播

在这里插入图片描述

public static void main(String[] args) {
    // 1 创建 parent Spring 应用上下文
     AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
     parent.setId("parent");
     parent.register(Mylistener.class);
    // 创建current spring 应用上下文
     AnnotationConfigApplicationContext current = new AnnotationConfigApplicationContext();
     current.setId("current");
     current.register(Mylistener.class);
     current.setParent(parent);
    // current parent
     parent.refresh();
     current.refresh();
     parent.close();
     current.close();
 }
static class Mylistener implements ApplicationListener<ContextRefreshedEvent> {
   @Override
   public void onApplicationEvent(ContextRefreshedEvent event) {
       System.out.printf("监听到应用上下文[ID %s]\n", event.getApplicationContext().getId());
   }
}

第一个事件触发是parent,第二,三个由于事件传播子和父都触发了这个事件:
在这里插入图片描述

原理就是,源码会在父也发布事件:
在这里插入图片描述
如何避免:

public class HierachicalEventDemo {
    public static void main(String[] args) {
       // 1 创建 parent Spring 应用上下文
        AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
        parent.setId("parent");
        parent.register(Mylistener.class);
       // 创建current spring 应用上下文
        AnnotationConfigApplicationContext current = new AnnotationConfigApplicationContext();
        current.setId("current");
        current.register(Mylistener.class);
        current.setParent(parent);
       // current parent
        parent.refresh();
        current.refresh();
        parent.close();
        current.close();
    }
    static class Mylistener implements ApplicationListener<ContextRefreshedEvent> {
        /**
         * 这里之所以要静态是因为我们在 parent 和 current 是不是一样的对象 也就是有两对象
         * 但是静态字段就是类共用的
         * 如果时间发布过不再重新发布
         */
        private static Set<ApplicationEvent> processedEvents = new LinkedHashSet();
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            if (processedEvents.add(event)) {
                System.out.printf("监听到应用上下文[ID %s] %s\n ", event.getApplicationContext().getId(), event);
            }
        }
    }
}

Spirng 内建事件

在这里插入图片描述

Spring Payload 事件

在这里插入图片描述使用的时候不能简单继承使用,发送方法最好是用object这个方法。

自定义Spirng事件

在这里插入图片描述

public class MyEvent extends ApplicationEvent {
    public MyEvent(String msg) {
        super(msg);
    }
    @Override
    public String getSource() {
        return (String) super.getSource();
    }
    public String getMessage() {
        return getSource();
    }
}
public class MyListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println(event.getMessage());
    }
}
public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Mylistener.class);
        context.refresh();
        context.publishEvent(new MyEvent("test event"));
        context.close();
    }
}

事件发布注入

ApplicationEventPublisherAwae 回调接口
通过@Autowired ApplicationEventPublisher

依赖查找

在这里插入图片描述
在这里插入图片描述

ApplicationEventMulticaster的底层实现

在这里插入图片描述
AbstractContext事件分发布是通过ApplicationEventMulticaster来实现的:
在这里插入图片描述

同步和异步Spirng事件广播

在这里插入图片描述
如果是异步如果要设置Executeor 是需要类型转换的,不是基于接口的编程方式。

public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Mylistener.class);
        context.refresh();
        ApplicationEventMulticaster multicaster = context.getBean(ApplicationEventMulticaster.class);
        if (multicaster instanceof SimpleApplicationEventMulticaster) {
            ExecutorService executor = newSingleThreadExecutor();
            SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = (SimpleApplicationEventMulticaster) multicaster;
            simpleApplicationEventMulticaster.setTaskExecutor(executor);
            // 优雅的关闭线程池
            simpleApplicationEventMulticaster.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
                @Override
                public void onApplicationEvent(ContextClosedEvent event) {
                    if (!executor.isShutdown()) {
                        executor.shutdown();
                    }
                }
            });
        }
        context.publishEvent(new MyEvent("test event"));
        context.close();
    }
}

通过注解的方式实现:

@EnableAsync
public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Demo.class);
        context.refresh();
        context.publishEvent(new MyEvent("test event"));
        context.close();
    }
    @EventListener
    @Async
    public void onApplicationContext(ApplicationEvent event) {
        System.out.println(Thread.currentThread().getName() +  event);
    }
 	// 这是自定义我们的线程池
    @Bean
    Executor taskExecutor() {
        return Executors.newSingleThreadExecutor();
    }
}

事件的异常情况

在这里插入图片描述

public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Demo.class);
        context.refresh();
        ApplicationEventMulticaster multicaster = context.getBean(ApplicationEventMulticaster.class);
        if (multicaster instanceof SimpleApplicationEventMulticaster) {
            SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = (SimpleApplicationEventMulticaster) multicaster;
            simpleApplicationEventMulticaster.setErrorHandler((t) ->{
                System.out.printf("发生了异常" );
            });
        }
        context.publishEvent(new MyEvent("test event"));
        context.close();
    }
    @EventListener

    public void onApplicationContext(ContextClosedEvent event) {
        System.out.println(Thread.currentThread().getName() +  event);
        throw new RuntimeException("制造异常");
    }

    @Bean
    Executor taskExecutor() {
        return Executors.newSingleThreadExecutor();
    }
}

Spirng 事件/监听实现原理

在这里插入图片描述
在这里插入图片描述
ListenerRetriever 会过滤对应的ApplicationListener Event实例 这个Event实例包括它本身及它的孙子类
在这里插入图片描述
处理泛型并过滤:
在这里插入图片描述
在处理事件的时候就会去获取对应的监听器:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
参考资料:小马哥核心编程思想。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/716695.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在招聘中开始使用AI?

在人工智能时代&#xff0c;招聘团队面临着“用更少的钱做更多的事情”的压力&#xff1a;用更少的钱和更少的团队。根据一项调查&#xff0c;58%的受访者认为&#xff0c;“提高我们助教团队的效率&#xff0c;降低成本”是明年招聘职位的首要任务。在招聘中使用人工智能是提高…

40、基于深度学习的线性预测设计(matlab)

1、原理及流程 深度学习的线性预测是一种利用深度神经网络模型进行线性回归预测的方法。其设计原理主要基于神经网络的层次化特性&#xff0c;利用多层感知器&#xff08;MLP&#xff09;等模型进行特征学习和非线性变换&#xff0c;从而提高线性预测的准确性。 设计流程如下…

▶《强化学习的数学原理》(2024春)_西湖大学赵世钰 Ch0 一张图讲完强化学习原理

PPT 截取有用信息。 课程网站做习题。总体 MOOC 过一遍 1、视频 学堂在线 习题 2、相应章节 过电子书 [2023.8 版本] 复习 3、总体 MOOC 过一遍 学堂在线 课程页面链接 中国大学MOOC 课程页面链接 B 站 视频链接 PPT和书籍下载网址&#xff1a; 【github链接】 onedrive链接&…

原子阿波罗STM32F767程序的控制器改为STM32F407驱动LCD屏

由于手里没有原子大神的F429开发板&#xff0c;又还想学习原子大神的F429开发板程序&#xff0c;前几天&#xff0c;经过更换控制器&#xff0c;成功把原子大神的F429开发板程序用到了F407开发板上&#xff0c;驱动LCD屏显示成功&#xff0c;目的&#xff0c;就是熟悉原子大神的…

记录第一次突发情况

项目场景&#xff1a; 这台云服务器主要是我学习在用&#xff0c;也不是很大&#xff0c;2核2g3M40G硬盘。 在这台服务器上&#xff0c;我主要使用了docker并且把所有的东西&#xff0c;都通过docker安装&#xff0c;比如MySQL&#xff0c;redis&#xff0c; elasticsearch。 …

安装 Nuxt.js 的步骤和注意事项

title: 安装 Nuxt.js 的步骤和注意事项 date: 2024/6/17 updated: 2024/6/17 author: cmdragon excerpt: Nuxt.js在Vue.js基础上提供的服务器端渲染框架优势&#xff0c;包括提高开发效率、代码维护性和应用性能。指南详细说明了从环境准备、Nuxt.js安装配置到进阶部署技巧&…

生成式人工智能备案办理指南,深度解析大模型备案全流程

早在2023年年初&#xff0c;国家互联网信息办公室、工业和信息化部、公安部针对深度合成服务制定的《互联网信息服务深度合成管理规定》&#xff08;“《深度合成管理规定》”&#xff09;顺利施行&#xff0c;其明确了深度合成服务相关方的义务与主体责任&#xff0c;强化了对…

RERCS系统开发实战案例-Part06 FPM Application添加列表组件(List UIBB)

在FPM Application中添加搜索结果的List UIBB 1&#xff09;添加List UIBB 2&#xff09;提示配置标识不存在&#xff0c;则需要新建配置标识&#xff08;* 每个组件都必须有对应的配置标识&#xff09;&#xff1b; 3&#xff09;选择对应的包和请求 4&#xff09;为List UIB…

一键掌控,4G红外插座引领智能生活新潮流!

随着科技的进步&#xff0c;市场上出现大量带语音、手机APP可控制的智能插座产品&#xff0c;由此可看出客户对产品的功能要求也越来越高&#xff0c;追求舒适的体验感&#xff0c;特别是对操控性的要求越来越高。但是目前大部分红外遥控插座均为WiFi插座类型&#xff0c;WiFi红…

【一步一步了解Java系列】:认识String类

看到这句话的时候证明&#xff1a;此刻你我都在努力 加油陌生人 个人主页&#xff1a;Gu Gu Study专栏&#xff1a;一步一步了解Java 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努力留下足迹 喜欢的话可以点个赞谢谢了。 作者&#xff1a;小闭…

Java异常和文件

一、异常 1.定义 异常&#xff1a;异常就是代表程序出现的问题 体系结构&#xff1a; 最上层的是 Throwable 类&#xff0c;下面有两个子类&#xff1a; ① Error&#xff1a;代表系统级别的问题&#xff08;属于严重问题&#xff0c;比如&#xff1a;内存溢出&#xff09;。…

企业该怎么进行流程管理?

众所周知&#xff0c;流程管理在企业中是一种有效的方法&#xff0c;可以帮助组织优化运营、提高效率并降低成本。 下面是一些步骤&#xff0c;可以帮助大家在企业中实施流程管理&#xff1a; 确定目标&#xff1a;首先&#xff0c;明确企业的目标和愿景。这将帮助您确定需要…

Python画箱线图展示数据分布情况

箱线图&#xff08;Boxplot&#xff09;是一种常用的统计图表&#xff0c;用于展示数据的分布情况。 它由五个统计量组成&#xff1a;最小值、第一四分位数&#xff08;Q1&#xff09;、中位数&#xff08;Q2&#xff09;、第三四分位数&#xff08;Q3&#xff09;和最大值。 …

ffmpeg封装和解封装介绍-(10)综合完成视频重编码为h265,解封装解码编码再封装

主函数逐句解析&#xff1a; 由于代码太多我们只解析主函数&#xff0c;&#xff08;其他封装函数见前面文章&#xff0c;同时用到了解码编码封装代码&#xff09;。 初始化和参数处理 int main(int argc, char* argv[]) {/// 输入参数处理string useage "124_test_x…

1.华为路由器-三层交换机-二层交换机组网连接

AR1配置GE 0/0/0接口IP [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0] [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.1 24 [Huawei]iP route-static 192.168.0.0 16 1.1.1.2三层交换机配置如下 创建vlan [Huawei]vlan batch 10 20配置接口ip [Huawei]int g0/0/1 [Huawei…

让AI 赋予人类超强的记忆力

遗忘曲线告诉我们&#xff0c;绝大部分新掌握的知识约在一周后被遗忘&#xff0c;一个月左右基本忘光。「好记性不如一个烂笔头」&#xff0c;借助AI还真能做出这样「烂笔头」。 提升个人的记忆力-个人搜索引擎 个人搜索引擎的想法是一个强大而诱人的想法。如果有一个应用程序可…

你的iPhone安全吗?想要保护个人隐私一定要这么做

在这个数字化时代&#xff0c;个人隐私安全显得尤为重要&#xff0c;尤其是对于那些依赖智能手机处理日常事务的用户而言。作为市场上最受欢迎的智能手机之一&#xff0c;iPhone的安全性备受关注&#xff0c;但即便如此&#xff0c;它也可能成为黑客攻击和非法监控的目标。如何…

慎投!新增1本中科院1区顶刊被“On Hold”

本周投稿推荐 SSCI • 中科院2区&#xff0c;6.0-7.0&#xff08;录用友好&#xff09; EI • 各领域沾边均可&#xff08;2天录用&#xff09; CNKI • 7天录用-检索&#xff08;急录友好&#xff09; SCI&EI • 4区生物医学类&#xff0c;0.5-1.0&#xff08;录用…

CC1310 LaunchPad开发板底噪测试

测试射频底噪时&#xff0c;主要关注的是在无信号输入时&#xff0c;系统或器件产生的最小噪声功率。这通常涉及到使用频谱分析仪&#xff08;频谱仪&#xff09;来测量输出噪声功率谱密度。以下是进行射频底噪测试的几种方法&#xff1a; 使用频谱仪直接测量&#xff1a; 通过…

做LLM推理时,常见的显卡如何选择?

随着开源LLM越来越成熟&#xff0c;业务接入LLM推理也成为必然&#xff0c;如何选模型大小和显卡&#xff0c;主要看下面这些。 一、选GPU显卡 在选择显卡进行大型语言模型推理时&#xff0c;主要要看下面几个指标&#xff1a; 1、 VRAM&#xff08;视频随机存取存储器&…