SpringBoot自定义消息总线

一、前言

        在现代的分布式系统中,消息传递已成为一个非常流行的模式。它使得系统内的不同部分可以松耦合地通信,从而实现更高效、更可靠的应用程序。本博客将介绍SpringBoot如何提供简单易用的消息传递机制,并展示如何自定义消息总线以满足特定需求。

二、依赖引入

// gradle 自身需求资源库 放头部
buildscript {repositories {maven { url 'https://maven.aliyun.com/repository/public' }// 加载其他Maven仓库mavenCentral()}dependencies {classpath('org.springframework.boot:spring-boot-gradle-plugin:2.1.1.RELEASE')// 加载插件,用到里面的函数方法}
}apply plugin: 'java'
apply plugin: 'idea'
// 使用spring boot 框架
apply plugin: 'org.springframework.boot'
// 使用spring boot的自动依赖管理
apply plugin: 'io.spring.dependency-management'// 版本信息
group 'com.littledyf'
version '1.0-SNAPSHOT'// 执行项目中所使用的的资源仓库
repositories {maven { url 'https://maven.aliyun.com/repository/public' }mavenCentral()
}// 项目中需要的依赖
dependencies {// 添加 jupiter 测试的依赖testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'// 添加 jupiter 测试的依赖testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'// 添加 spring-boot-starter-web 的依赖 必须 排除了security 根据自身需求implementation('org.springframework.boot:spring-boot-starter-web') {exclude group: 'org.springframework.security', module: 'spring-security-config'}// 添加 spring-boot-starter-test 该依赖对于编译测试是必须的,默认包含编译产品依赖和编译时依赖testImplementation 'org.springframework.boot:spring-boot-starter-test'// 添加 junit 测试的依赖testImplementation group: 'junit', name: 'junit', version: '4.11'// 添加 lombokannotationProcessor 'org.projectlombok:lombok:1.18.22' // annotationProcessor代表main下代码的注解执行器testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'// testAnnotationProcessor代表test下代码的注解执行器compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.22' // compile代表编译时使用的lombok}test {useJUnitPlatform()
}

三、代码

        定义注册器实现类:

import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver;import java.util.HashMap;
import java.util.Map;/*** @description 注册器*/
public class Registry {/*** Query对象和命令提供者的对应关系*/private Map<Class<? extends Query>,QueryProvider> queryProviderMap =  new HashMap<>();/*** Event对象和命令提供者的对应关系*/private Map<Class<? extends Event>,EventProvider> eventProviderMap =  new HashMap<>();public Registry(ApplicationContext applicationContext){String[] names = applicationContext.getBeanNamesForType(QueryHandler.class);for (String name : names) {registerQuery(applicationContext,name);}names = applicationContext.getBeanNamesForType(EventHandler.class);for (String name : names) {registerEvent(applicationContext,name);}}private void registerQuery(ApplicationContext applicationContext, String name) {Class<QueryHandler<?,?>> handlerClass = (Class<QueryHandler<?,?>>) applicationContext.getType(name);Class<?>[] generics = GenericTypeResolver.resolveTypeArguments(handlerClass, QueryHandler.class);Class<? extends Query> queryType  = (Class<? extends Query>) generics[1];queryProviderMap.put(queryType, new QueryProvider(applicationContext, handlerClass));}private void registerEvent(ApplicationContext applicationContext, String name) {Class<EventHandler<?>> handlerClass = (    Class<EventHandler<?>>) applicationContext.getType(name);Class<?>[] generics = GenericTypeResolver.resolveTypeArguments(handlerClass, EventHandler.class);Class<? extends Event> eventType  = (Class<? extends Event>) generics[0];eventProviderMap.put(eventType, new EventProvider(applicationContext, handlerClass));}/*** 获取具体的QueryHandler   <R, Q extends Query<R>>定义R  Q的类型* @param queryClass* @param <R>* @param <Q>* @return*/<R, Q extends Query<R>> QueryHandler<R,Q> getQuery(Class<Q> queryClass) {return queryProviderMap.get(queryClass).get();}/*** 获取具体的EventHandler* @param eventClass* @return*/<E extends Event> EventHandler<E> getEvent(Class<? extends Event> eventClass) {return eventProviderMap.get(eventClass).get();}
}

        消息总线接口,定义两个方法,一个执行查询,一个执行事件:

/*** @description  消息总线*/
public interface Bus {<R,Q extends Query<R>> R executeQuery(Q query);<E extends Event> void dispatchEvent(E event);
}

        消息总线实现类:

public class SpringBus implements Bus {private final Registry registry;public SpringBus(Registry registry) {this.registry = registry;}@Overridepublic <R, Q extends Query<R>> R executeQuery(Q query) {QueryHandler<R, Q> queryHandler = (QueryHandler<R, Q>) registry.getQuery(query.getClass());return queryHandler.handle(query);}@Overridepublic <E extends Event> void dispatchEvent(E event) {EventHandler<E> eventHandler = (EventHandler<E>) registry.getEvent(event.getClass());eventHandler.process(event);}
}

        Query接口:

public interface Query<R> {}

        QueryHandler接口:

public interface QueryHandler<R, C extends Query<R>> {R handle(C query);
}

        QueryProvider类:

import org.springframework.context.ApplicationContext;/*** query  提供者* @param <H>*/
public class QueryProvider<H extends QueryHandler<?, ?>> {private final ApplicationContext applicationContext;private final Class<H> type;QueryProvider(ApplicationContext applicationContext, Class<H> type) {this.applicationContext = applicationContext;this.type = type;}public H get() {return applicationContext.getBean(type);}
}

        Event类似,Event接口:

public interface Event {}

        EventHandler接口:

/*** @description  事件处理器*/
public interface EventHandler<E extends Event> {/**** @param event  事件*/void process(E event);
}

        EventProvider类:

import org.springframework.context.ApplicationContext;/*** event  提供者* @param <H>*/
public class EventProvider<H extends EventHandler<?>> {private final ApplicationContext applicationContext;private final Class<H> type;EventProvider(ApplicationContext applicationContext, Class<H> type) {this.applicationContext = applicationContext;this.type = type;}public H get() {return applicationContext.getBean(type);}
}

        实体类:

import com.littledyf.cqs.Query;
import lombok.Data;import java.io.Serializable;
import java.util.List;@Data
public class TestDto implements Serializable, Query<List<TestVo>> {private String name;
}
import lombok.Data;@Data
public class TestVo {private String nameVo;
}

        Query具体实现类:

import com.littledyf.cqs.QueryHandler;
import com.littledyf.cqs.domain.TestDto;
import com.littledyf.cqs.domain.TestVo;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component
@NoArgsConstructor
public class TestQueryHandler implements QueryHandler<List<TestVo>, TestDto> {@Overridepublic List<TestVo> handle(TestDto testDto) {List<TestVo> testVos = new ArrayList<>();TestVo testVo = new TestVo();testVo.setNameVo(testDto.getName());testVos.add(testVo);return testVos;}
}

        Controller层:

import com.littledyf.cqs.Bus;
import com.littledyf.cqs.domain.TestDto;
import com.littledyf.cqs.domain.TestVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.List;@Slf4j
@RestController
@RequestMapping("/my-test/cqs")
public class CqsController {@Resourceprivate Bus bus;@PostMapping(value = "/query-test")public List<TestVo> queryTest(@RequestBody TestDto testDto)  {return bus.executeQuery(testDto);}
}

        SpringBoot启动类,启动类中进行ApplicationContext的注入:

import com.littledyf.cqs.Bus;
import com.littledyf.cqs.Registry;
import com.littledyf.cqs.SpringBus;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;@SpringBootApplication
public class MyTestApplication {public static void main(String[] args) {SpringApplication.run(MyTestApplication.class, args);}/*** 注册器*/@Beanpublic Registry registry(ApplicationContext applicationContext) {return new Registry(applicationContext);}/*** 消息总线*/@Beanpublic Bus commandBus(Registry registry) {return new SpringBus(registry);}
}

        yml文件配置:

server:port: 8080
spring:application:name: my-test-service

四、测试

        这里只要模拟了查询,事件等与查询类似,需要实现具体的接口。整体实现就是在SpringBoot启动时加载注册类,注册类会根据具体的类注入相应的bean,在具体调用时,会根据不同的类实现调用相关的bean。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WSL中为Ubuntu和Debian设置固定IP的终极指南

文章目录 **WSL中为Ubuntu和Debian设置固定IP的终极指南****引言/背景****1. 传统方法****2. 新方法:添加指定IP而不是更改IP****结论**WSL中为Ubuntu和Debian设置固定IP的终极指南 引言/背景 随着WSL(Windows Subsystem for Linux)的普及,越来越多的开发者开始在Windows…

基于Gin框架的HTTP接口限速实践

在当今的微服务架构和RESTful API主导的时代&#xff0c;HTTP接口在各个业务模块之间扮演着重要的角色。随着业务规模的不断扩大&#xff0c;接口的访问频率和负载也随之增加。为了确保系统的稳定性和性能&#xff0c;接口限速成了一个重要的话题。 1 接口限速的使用场景 接口…

MongoDB 的简介

MongoDB 趋势 对于 MongoDB 的认识 Q&A QA什么是 MongoDB&#xff1f; 一个以 JSON 为数据模型的文档数据库一个以 JSON 为数据模型的文档数据库文档来自于“JSON Document”&#xff0c;并非我们一般理解的 PDF&#xff0c;WORD谁开发 MongDB&#xff1f; 上市公司 MongoD…

第 3 章 栈和队列(单链队列)

1. 背景说明 队列(queue)是一种先进先出(first in first out,缩为 FIFO)的线性表。它只允许在表的一端进行插入&#xff0c;而在另一端删除元素。 2. 示例代码 1&#xff09;status.h /* DataStructure 预定义常量和类型头文件 */#ifndef STATUS_H #define STATUS_H/* 函数结果…

【rust/egui】(七)看看template的app.rs:Slider

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0上一篇&#xff1a;这里 Slider 滑块&#xff0c;如下图 定义…

单片机-控制按键点亮LED灯

1、按键电路图 定义四个按键引脚 1、按键按下 为 输入为低电平 2、按键不按下 IO有上拉电阻&#xff0c;为高电平 // 定义 按键的 管教 sbit KEY1 P3^1; sbit KEY2 P3^0; sbit KEY3 P3^2; sbit KEY4 P3^3; 2、LED灯电路图 LED 输出高电平为亮 // 定义LED灯 管教 sbit LED1…

Web服务器部署上线踩坑流程回顾

5月份时曾部署上线了C的Web服务器&#xff0c;温故而知新&#xff0c;本篇文章梳理总结一下部署流程知识&#xff1b; 最初的解决方案&#xff1a;https://blog.csdn.net/BinBinCome/article/details/129750951?spm1001.2014.3001.5501后来的解决方案&#xff1a;https://blog…

海格里斯HEGERLS高密度料箱式四向穿梭车存储系统有哪些显著优势?

近些年仓储货架向着自动化、智能化发展&#xff0c;因此市面上出现很多不同类型的智能自动化仓储货架。其中&#xff0c;最受企业青睐的便是四向穿梭车货架。四向穿梭车货架根据其载重不同可分为托盘式和料箱式两大类。这两种不同类型的四向穿梭车货架在结构形式和控制方式上基…

微服务-gateway鉴权

文章目录 一、前言二、gateway鉴权1、依赖配置2、编写代码3、GlobalFilter详解3.1、GlobalFilter简介3.2、GlobalFilter自定义执行顺序3.2.1、实现Order接口实现自定义执行顺序 一、前言 网关是介于客户端和服务器端之间的中间层&#xff0c;所有的外部请求都会先经过 网关这一…

深入了解Docker镜像操作

Docker是一种流行的容器化平台&#xff0c;它允许开发者将应用程序及其依赖项打包成容器&#xff0c;以便在不同环境中轻松部署和运行。在Docker中&#xff0c;镜像是构建容器的基础&#xff0c;有些家人们可能在服务器上对docker镜像的操作命令不是很熟悉&#xff0c;本文将深…

【LeetCode-中等题】200. 岛屿数量

文章目录 题目方法一&#xff1a;深度优先搜索 dfs方法二&#xff1a;广度优先搜索 bfs方法三&#xff1a;&#xff08;重点掌握&#xff09;并查集 题目 方法一&#xff1a;深度优先搜索 dfs 思路&#xff1a;让一个扫描指针扫描每一个格子&#xff0c;然后每扫到一个为1的格…

Spring Boot日志基础使用 设置日志级别

然后 我们来说日志 日志在实际开发中还是非常重要的 即可记录项目状态和一些特殊情况发生 因为 我们这里不是将项目 所以 讲的也不会特别深 基本还是将Spring Boot的日志设置或控制这一类的东西 相对业务的领域我们就不涉及了 日志 log 初期最明显的作用在于 开发中 你可以用…