Google Guava 发布订阅模式/生产消费者模式 使用详情

目录

Guava 介绍

应用场景举例

1. 引入 Maven 依赖

 2. 自定义 Event 事件类

3. 定义 EventListener 事件订阅者

4. 定义 EventBus 事件总线

5. 定义 Controller 进行测试


Guava 介绍

        Guava 是一组来自 Google 的核心 Java 库,里面包括新的集合 类型(例如 Multimap 和 MultiSet),不可变集合、图形库、 以及用于并发、I/O、哈希、基元、字符串,发布/订阅模式等等。接下来主要讲解 发布订阅模式。

Guava 发布订阅主要包含以下主要核心部分:

  • Event 事件
  • Publisher 事件发布者
  • EventListener 事件订阅者
  • EventBus 事件总线

工作流程

        Publisher 事件发布者  通过 EventBus 事件总线 发布事件,然后 EventBus 事件总线 把事件传给 Subscriber 事件订阅者 消费。

工作原理图

应用场景举例

        当用户注册App后,可能会产生很多行为,比如需要发短信提醒用户,注册成功,获取100积分,又或者需要给注册成功的用户送优惠卷。如果按我们平时的写法,则需要在用户注册成功后,返回请求前,需要引入发短信和发优惠卷的逻辑,不仅使冗余在注册代码中,造成耦合度太高。职责不分离。

        这时就可以引入Guava 的发布订阅模式。让发送短信的监听器 和 发优惠卷的监听器 同时监听同一个事件即可。

1. 引入 Maven 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <groupId>com.xinxin</groupId>
    <artifactId>cyh</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.70</version>
        </dependency>
    </dependencies>
</project>

 2. 自定义 Event 事件类

        Event 类是我们生产者和消费者 消息传播的载体,也就是发送的内容,通常我们以 Event 为后缀来命名事件类。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @description: 用户注册事件
 * @author: cyh
 * @create: 2024-11-02 17:07
 **/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserRegisterEvent {
    private Long userId;

    private Date registerTime;
}

3. 定义 EventListener 事件订阅者

        事件订阅者即事件的监听者,接受事件消费的一端。监听者会一直监听他们所关注的事件。

事件订阅者的定义,需要在方法上添加@Subscribe注解声明自己为事件订阅者,然后方法参数是他们监控的 Event 事件。

    一般@Subscribe可以配合@AllowConcurrentEvents注解一起使用,这个注解是用来标识当前订阅者是线程安全的,可以减少同步开销。

3.1 定义 发送短信 事件订阅者

/**
 * @description: 短信事件监听器
 * @author: cyh
 * @create: 2024-11-02 17:10
 **/
@Slf4j
@Component
public class SmsEventListener {


        @Subscribe
        @AllowConcurrentEvents
        public void recordRegisterLog(UserRegisterEvent event) {
            Long userId = event.getUserId();
            Date registerTime = event.getRegisterTime();
            log.info("短信监听器 监听到用户注册行为,用户 {} 在 {} 注册成功,事件内容为:{}", userId, registerTime, JSON.toJSONString(event));
            //发送短信通知
            log.info("发送短信通知");
        }
        
}

3.2 定义 发送优惠卷 事件订阅者

/**
 * @description: 优惠卷事件监听器
 * @author: cyh
 * @create: 2024-11-02 17:10
 **/
@Slf4j
@Component
public class CouponEventListener {

        @Subscribe
        @AllowConcurrentEvents
        public void recordRegisterLog(UserRegisterEvent event) {
            Long userId = event.getUserId();
            Date registerTime = event.getRegisterTime();
            log.info("优惠卷监听器 监听到用户注册行为,用户 {} 在 {} 注册成功,事件内容为:{}", userId, registerTime, JSON.toJSONString(event));
            //发送优惠卷
            log.info("发送优惠卷");
        }
}

4. 定义 EventBus 事件总线

        EventBus 事件总线的作用是,将 Event 事件 转发给 EventListener 事件订阅者。所以 首先我们就要把 事件订阅者注册给总线,它才知道有哪些订阅者需要转发。 然后将不同的 Event 事件 转发给订阅了该事件的订阅者。

事件总线有两个作用:

  •   发布消息
  •   转发消息给订阅者        

4.1 EventBus 事件总线,代码定义

/**
 * @description:  事件总线
 * @author: cyh
 * @create: 2024-11-02 17:16
 **/
@Slf4j
@Component
public class EventBusCenter {

    private static EventBus eventBus;
    private static AsyncEventBus asyncEventBus;
    private static Executor executor = new ThreadPoolExecutor(12, 12, 60,
            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 异步事件单例模式
     * @return
     */
    private static synchronized AsyncEventBus getAsyncEventBus() {
        if(asyncEventBus == null){
            asyncEventBus = new AsyncEventBus(executor);
        }
        return asyncEventBus;
    }

    /**
     * 同步事件单例模式
     * @return
     */
    private static synchronized EventBus getEventBus() {
        if(eventBus == null) {
            eventBus = new EventBus();
        }
        return eventBus;
    }

    public static void register(Object object) {
        getEventBus().register(object);
        getAsyncEventBus().register(object);
    }

    public static void unregister(Object object) {
        getEventBus().unregister(object);
        getAsyncEventBus().unregister(object);
    }


    /**
     * 同步发送事件
     * @param event
     */
    public static void post(Object event) {
        log.info("同步发送事件内容:{}", JSON.toJSONString(event));
        eventBus.post(event);
    }



    /**
     * 异步发送事件
     * @param event
     */
    public static void asyncPost(Object event) {
        log.info("异步发送事件内容:{}", JSON.toJSONString(event));
        asyncEventBus.post(event);
    }
}

 4.2 将 EventListener 事件订阅者注册到总线中

@Order(1)
@Slf4j
@Component
@Configuration
public class RegisterListenerToBus implements ApplicationListener<ApplicationReadyEvent> {
 
    @Resource
    private SmsEventListener smsEventListener;
    @Resource
    private CouponEventListener couponEventListener;
    

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        log.info("ApplicationReadyEvent init restTemplate.");
        try {
            //监听器注册
            EventBusCenter.register(smsEventListener);
            EventBusCenter.register(couponEventListener);
        } catch (Exception e) {
            log.error("初始化配置失败!", e);
        }
        log.info("ApplicationReadyEvent init restTemplates finished.");
    }
 
}

5. 定义 Controller 进行测试

/**
 * @description:
 * @author: cyh
 * @create: 2024-11-02 17:28
 **/
@RestController
public class RegisterController {

    @GetMapping("/register")
    public String register(Long userId){
        UserRegisterEvent event = new UserRegisterEvent(userId, new Date());
        //同步发送
        EventBusCenter.post(event);
//        //异步发送
//        EventBusCenter.asyncPost(event);
        return "ok";
    }
}

测试结果

同步发送事件内容:{"registerTime":1730557363231,"userId":1594}
短信监听器 监听到用户注册行为,用户 1594 在 Sat Nov 02 22:22:43 CST 2024 注册成功,事件内容为:{"registerTime":1730557363231,"userId":1594}
发送短信通知
优惠卷监听器 监听到用户注册行为,用户 1594 在 Sat Nov 02 22:22:43 CST 2024 注册成功,事件内容为:{"registerTime":1730557363231,"userId":1594}
发送优惠卷

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

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

相关文章

Idea如何推送项目到gitee

第一步&#xff1a;先在你的gitee创建一个仓库 第二步&#xff1a; 点击推送 点击定义远程&#xff0c;将URL换成你仓库的&#xff0c;填好你的用户名和密码 可以看到已经推送到仓库了

gdb和make工具

gdb工具&#xff1a; GDB的主要功能 断点设置&#xff1a;允许开发者在特定的代码行设置断点&#xff0c;当程序执行到该行时会自动暂停&#xff0c;方便开发者进行调试和分析。 变量查看与修改&#xff1a;在程序运行过程中&#xff0c;可以查看和修改变量的值&#xff0c;以…

一周内从0到1开发一款 AR眼镜 相机应用?

目录 1. &#x1f4c2; 前言 2. &#x1f4a0; 任务拆分 2.1 产品需求拆分 2.2 开发工作拆分 3. &#x1f531; 开发实现 3.1 代码目录截图 3.2 app 模块 3.3 middleware 模块 3.4 portal 模块 4. ⚛️ 拍照与录像 4.1 前滑后滑统一处理 4.2 初始化 View 以及 Came…

推荐一款功能强大的数据库开发管理工具:SQLite Expert Pro

SQLite Expert Professional是一个功能强大的工具&#xff0c;旨在简化SQLite3数据库的开发。 它是SQLite的一个功能丰富的管理和开发工具&#xff0c;旨在满足所有用户从编写简单SQL查询到开发复杂数据库的需求。 图形界面支持所有SQLite功能。 它包括一个可视化查询构建器&a…

C#与C++交互开发系列(十七):线程安全

前言 在跨平台开发和多线程编程中&#xff0c;线程安全是不可忽视的重要因素。C和C#中提供了各自的线程同步机制&#xff0c;但在跨语言调用中&#xff0c;如何确保数据一致性、避免数据竞争和死锁等问题&#xff0c;是开发人员必须考虑的重点。 本文将介绍在C#和C交互开发中确…

数据库SQL学习笔记

第 1 章 绪论 1.1 数据库系统概述 1.1.1 四个基本概念 数据库系统(DBS) 定义&#xff1a;是指在计算机系统中引入数据库后的系统构成 构成&#xff1a;数据库&#xff0c;数据库管理系统&#xff08;及其开发工具&#xff09;&#xff0c;应用系统&#xff0c;数据库管理员…

Java项目实战II基于Spring Boot的智慧生活商城系统的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着科技的飞速发展&#xff0c;人们的…

.net Core 使用Panda.DynamicWebApi动态构造路由

我们以前是通过创建controller来创建API&#xff0c;通过controller来显示的生成路由&#xff0c;这里我们讲解下如何不通过controller&#xff0c;构造API路由 安装 Panda.DynamicWebApi 1.2.2 1.2.2 Swashbuckle.AspNetCore 6.2.3 6.2.3添加ServiceAction…

[ 内网渗透实战篇-1 ] 单域环境搭建与安装域环境判断域控定位CS插件装载CS上线

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

还在使用ElementUI不如试一试DaisyUI,DaisyUI: Tailwind CSS 的高效组件库,

DaisyUI: Tailwind CSS 的高效组件库 daisyUI官网&#xff1a;https://daisyui.com/ 在现代网页开发中&#xff0c;快速构建美观且响应式的用户界面是每个开发者追求的目标。Tailwind CSS 是一个流行的实用程序优先的 CSS 框架&#xff0c;它允许开发者直接在 HTML 中使用预…

《大数据与人工智能:提升数据质量与数量的利器》

《大数据与人工智能&#xff1a;提升数据质量与数量的利器》 一、大数据与人工智能的融合趋势二、大数据增加数据数量的方法&#xff08;一&#xff09;不同途径的数据增量&#xff08;二&#xff09;数据增强的多样方法 三、人工智能提升数据数量的手段&#xff08;一&#xf…

通义灵码实操—飞机大战游戏

通义灵码实操—飞机大战游戏 有没有想象过自己独立编写一个有趣的小游戏。在本实践课程中&#xff0c;你不仅可以实现这个想法&#xff0c;而且还将得到通义灵码智能编程助手的支持与指导。我们将携手步入编程的神奇世界&#xff0c;以一种简洁、高效且具有创造性的方式&#…

AI之硬件对比:据传英伟达Nvidia2025年将推出RTX 5090-32GB/RTX 5080-24GB、华为2025年推出910C/910D

AI之硬件对比&#xff1a;据传英伟达Nvidia2025年将推出RTX 5090-32GB/RTX 5080-24GB、华为2025年推出910C/910D 目录 Nvidia的显卡 Nvidia的5090/5080/4090/4080&#xff1a;据传传英伟达Nvidia RTX 5090后续推出32GB版且RTX 5080后续或推出24GB版 RTX 5090相较于RTX 4090&…

7.2 设计模式

设计模式 7.3.1 设计模式的要素7.3.2 创建型设计模式7.3.3 结构性设计模式1. Adapter (适配器)2. Bridge(桥接)3.Composite(组合)4.Decorator(装饰)5.Facade(外观)6.Flyweight(享元)7.Proxy(代理)8. 结构型模式比较 7.3.4 行为型设计模式1 Chain of Responsibility 责任链模式2…

狐假虎威,数据流图其实很简单

不同于类图、用例图和顺序图等等UML里面的概念&#xff0c;DFD数据流图术语结构化分析的范畴。它从数据传递和加工角度&#xff0c;以图形方式来表达系统的逻辑功能、数据在系统内部的逻辑流向和逻辑变换过程。 两句话来概括数据流图&#xff1a; 功能是用来描述整个系统中信息…

一周搞定模电!(2) 超详细!!新手小白必看!

目录 稳压二极管 整流二极管 开关二极管 电容 1、什么是电容 2、电容的作用 2.1 旁路的作用 2.2 去耦&#xff08;退耦&#xff09;电容的作用 2.3 滤波和储能 3.电容在电路中的连接问题 稳压二极管 嵌入式系统&#xff0c;作为一种专用计算机系统&#xff0c;被广泛…

CPU Study - Pipeline Basic

参考来源&#xff1a;《超标量处理器设计》—— 姚永斌 超标量处理器 一个程序执行时间的公式如下&#xff0c;而这个公式通常也反映了处理器的性能&#xff1a; 图中的CPI - Cycle Per Instruction也就是CPU每条指令需要的周期数量&#xff0c;CPI计算方法就是周期数量除以…

串口屏控制的自动滑轨(未完工)

序言 疫情期间自己制作了一个自动滑轨&#xff0c;基于无线遥控的&#xff0c;但是整体太大了&#xff0c;非常不方便携带&#xff0c;所以重新设计了一个新的&#xff0c;以2020铝型材做导轨的滑轨&#xff0c;目前2020做滑轨已经很成熟了&#xff0c;配件也都非常便宜&#x…

Python 自动化脚本集合:开源免费、跨平台、助你告别重复劳动、高效便捷完成各种任务!

引言 你是否也厌倦了重复繁琐的操作&#xff1f;让 Python-Geeks 的 Automation-scripts 库来替你完成重复工作&#xff01;这个仓库汇聚了200多个各种实用且高效的 Python 脚本&#xff0c;可以自动执行各种任务&#xff0c;让你从繁重的劳动中解放出来&#xff0c;真正享受生…