Spring-事件

news2024/10/6 12:26:00

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实例包括它本身及它的孙子类
在这里插入图片描述
处理泛型并过滤:
在这里插入图片描述
在处理事件的时候就会去获取对应的监听器:
在这里插入图片描述

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

神经网络 torch.nn---nn.LSTM()

torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) LSTM — PyTorch 2.3 documentation LSTM层的作用 LSTM层:长短时记忆网络层&#xff0c;它的主要作用是对输入序列进行处理&#xff0c;对序列中的每个元素进行编码并保存它们的状态&#xff0c;以便后续的处理。 …

H5的3D展示有可能代替PC传统3D展示么?

H5的3D展示技术正在快速发展&#xff0c;并且随着5G网络的普及和手机硬件性能的提升&#xff0c;H5的3D展示在某些方面已经能够接近甚至超越传统PC上的3D展示效果&#xff0c;比如 博维数孪 的渲染能力及效果。但H5和PC的3D展示互相之间是各有优势领域和行业支持&#xff0c;短…

hw面试总结

在这里给大家推荐一个阿里云的活动&#xff0c;可白嫖一年2h4g服务器 活动链接&#xff1a;https://university.aliyun.com/mobile?clubTaskBizsubTask…11404246…10212…&userCodeks0bglxp 一、漏洞分析 1.SQL注入 原理&#xff1a; 当Web应用向后台数据库传递SQL…

LLM之RAG实战(四十)| 使用LangChain SQL Agent和MySQL搭建多层RAG ChatBot

在传统的意义上&#xff0c;RAG 主要是从文档中检索用户想要的数据&#xff0c;从而提高大模型的能力&#xff0c;减少幻觉问题。今天&#xff0c;我们从另一个维度介绍RAG&#xff0c;RAG不从文档中获取数据&#xff0c;而是从MySQL数据库检索数据。我们可以使用LangChain SQL…

Nature子刊:你的健康在出生就被肠道病毒定义了?

近期&#xff0c;四川大学最新在《Nature Communications》期刊上(IF16.6)发表的“A metagenomic catalog of the early-life human gut virome”研究论文中&#xff0c;通过对宏病毒组结果进行分析&#xff0c;提供了最全面和最完整的生命早期人类肠道病毒组蓝图。 期刊&#…

[图解]《分析模式》漫谈03-Party是什么

1 00:00:00,790 --> 00:00:03,930 今天我们来看一下&#xff0c;Party是什么 2 00:00:05,710 --> 00:00:07,470 当然我们这里说的不是政治的 3 00:00:07,880 --> 00:00:08,350 Party 4 00:00:09,230 --> 00:00:11,110 是《分析模式》里面的一个用词 5 00:00:14…

短信群发平台选择时要看好对方的资质等问题!

在数字化营销日益盛行的今天&#xff0c;短信群发平台已成为企业与用户沟通的重要桥梁。然而&#xff0c;选择一个合适的短信群发平台并非易事&#xff0c;尤其需要考虑对方的资质等关键问题。 首先&#xff0c;资质是衡量一个短信群发平台是否正规、专业的重要指标。在选择平…

美团的 AI 面试有点简单

刷到一个美团的 AI 实习生的面试帖子&#xff0c;帖子虽然不长&#xff0c;但是把美团 AI 评测算法实习生面试的问题都po出来了。 单纯的看帖子中面试官提出的问题&#xff0c;并不是很难&#xff0c;大部分集中在考察AI项目和对AI模型的理解上&#xff0c;并没有过多的考察AI算…

企业服务器上云还是下云哪种比较好?-尚云Sunclouds

如今很多中小企业都面临一个艰难的选择&#xff0c;是要选择将服务器迁移至数据中心托管&#xff08;下云&#xff09;或者直接迁移到云端&#xff08;上云&#xff09;。中小企业是社会发展的中坚力量&#xff0c;他们的特点少而明显&#xff1a;资金少&#xff0c;增长快&…

【GTD时间管理法】“让‘聪明的自己“为“不太聪明的自己”打点好一切!

大脑是用来思考而不是用来记事的。 该如何解放大脑&#xff0c;让我们能够有条不紊地处理好一切事情呢&#xff1f; 世界知名时间管理大师戴维艾伦认为&#xff0c;只需要三个步骤&#xff1a;清空大脑——制订计划——行动起来。他把这种时间管理方法称作“GTD”&#xff0c…

【投稿优惠|权威主办】2024年能源、智能制造与材料科学国际学术会议(ICEIMMS 2024)

【投稿优惠|权威主办】2024年能源、智能制造与材料科学国际学术会议&#xff08;ICEIMMS 2024&#xff09; 2024 International Academic Conference on Energy, Intelligent Manufacturing, and Materials Science&#xff08;ICEIMMS 2024&#xff09; ▶会议简介 2024年能源…

净化机应用领域广泛 美国是我国净化机主要出口国

净化机应用领域广泛 美国是我国净化机主要出口国 净化机&#xff0c;又称为空气清洁设备或空气清新机&#xff0c;是一种专门设计用于滤除或杀灭空气污染物、提升空气清洁度的装置。净化机具备高效的过滤功能&#xff0c;能够滤除空气中的悬浮微粒、细菌、病毒和花粉等污染物&a…

Element UI 一键校验多表单(v-for循环表单,异步校验规则,v-for 中的 ref 属性,避坑 forEach 不支持异步 await )

需求描述 表单为数组 v-for 循环得到的多表单&#xff0c;如可自由增删的动态表单表单中存在异步校验规则&#xff0c;如姓名需访问接口校验是否已存在点击提交按钮&#xff0c;需一键校验所有表单&#xff0c;仅当所有表单都通过校验&#xff0c;才能最终提交到后台 效果预览 …

大模型应用之路:从提示词到通用人工智能(AGI)

前言 大模型在人工智能领域的应用正迅速扩展&#xff0c;从最初的提示词&#xff08;Prompt&#xff09;工程到追求通用人工智能&#xff08;AGI&#xff09;的宏伟目标&#xff0c;这一旅程充满了挑战与创新。本文将探索大模型在实际应用中的进展&#xff0c;以及它们如何为实…

微服务开发与实战Day07 - MQ高级篇

一、消息可靠性问题 首先&#xff0c;分析一下消息丢失的可能性有哪些。 消息从发送者发送消息&#xff0c;到消费者处理消息&#xff0c;需要经过的流程是这样的&#xff1a; 消息从生产者到消费者的每一步都可能导致消息丢失&#xff1a; 发送消息时丢失&#xff1a; 生产…

【车载AI音视频电脑】200万像素迷你一体机

产品主要特点&#xff1a; -设备安装方便简洁&#xff0c;可通过3M胶直接将设备粘 贴到车前挡风玻璃上 -支持IE预览&#xff0c;手机&#xff0c;PAD实时预览&#xff0c; 支持电脑客 户端实时预览功能 -内置2路模拟高清, 每路均可达到200万像素。另 外可扩充2路1080P模拟…

取证工作: SysTools SQL Log Analyzer, 完整的 SQL Server 日志取证分析

天津鸿萌科贸发展有限公司是 Systools 系列软件的授权代理商。 SysTools SQL Log Analyzer 是 Systools 取证工具系列之一&#xff0c;用于调查 SQL Server 事务日志&#xff0c;以对数据库篡改进行取证分析。 什么是 SQL Server 事务日志&#xff1f; 在深入研究 SQL 事务日…

【Linux文件篇】磁盘到用户空间:Linux文件系统架构全景

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 前言&#xff1a;我们前面的博客中一直提到的是被进程打开的文件&#xff0c;而系统中不仅仅只有被打开的文件还有很多没被打开的文件。如果没有被打开&#xff0c;那么文件是在哪里进行保存的呢?那我们又如何快速定位…

Vue.js入门教程:轻松掌握前端框架的魔法

随着前端技术的飞速发展&#xff0c;Vue.js凭借其简洁、易上手和高效的特点&#xff0c;成为了前端开发者们的新宠。本文将带你走进Vue.js的世界&#xff0c;从零开始&#xff0c;一步步掌握这个强大的前端框架。 一、什么是Vue.js Vue.js是一款构建用户界面的渐进式JavaScri…

数据结构——栈(Stack)详解

1. 栈&#xff08;Stack&#xff09; 1.1 概念 栈&#xff1a;一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中数据元素遵循后进先出LIFO(Last In First Out)的原则 压栈&am…