Spring 版MediatR--中介者模式实现库
背景
C# 版本库 MediatR 是一个中介者模式实现类库,其核心是一个中介 者模式的.NET实现,其目的是消息发送和消息处理的解耦。它支持单播和多播形式使用同步或异步的模式来发布消息,创建和帧听事件。
java中没有找到类似类库,在对MediatR源码阅读中,发现其主要思路是借助IOC获取Request与Handler对应关系并进行处理。
中介者模式
中介者模式:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变他们之间的交互。
使用中介模式,对象之间的交互将封装在中介对象中,对象不再直接交互(解耦),而是通过中介进行交互,这减少了对象之间的依赖性,从而减少了耦合。
应用
单播消息传输
单播消息传输,也就是一对第一的消息传递,一个消息对应一个消息处理,通过 IReust
抽象单播消息,使用 IRequestHandler
进行消息处理
@ExtendWith(SpringExtension.class)
@Import(
value = {
Mediator.class,
PingPongTest.PingHandler.class,
}
)
public class PingPongTest {
@Autowired
IMediator mediator;
@Test
public void should() {
String send = mediator.send(new Ping());
assertThat(send).isNotNull();
assertThat(send).isEqualTo("Pong");
}
public static class Ping implements IRequest<String> {
}
public static class PingHandler implements IRequestHandler<Ping, String> {
@Override
public String handle(Ping request) {
return "Pong";
}
}
}
多播消息传输
多播消息传输,是一对多的消息传递,一个消息对应多个消息处理,通过 INotification
抽象多播消息,使用 INotificationHanlder
进行消息处理
@ExtendWith(SpringExtension.class)
@Import(
value = {
Mediator.class,
PingNoticeTests.Pong1.class,
PingNoticeTests.Pong2.class,
}
)
public class PingNoticeTests {
@Autowired
IMediator mediator;
@Autowired
Pong1 pong1;
@Autowired
Pong2 pong2;
@Test
public void should() {
mediator.publish(new Ping());
assertThat(pong1.getCode()).isEqualTo("Pon1");
assertThat(pong2.getCode()).isEqualTo("Pon2");
}
public static class Ping implements INotification {
}
public static class Pong1 implements INotificationHandler<Ping> {
private String code;
public String getCode() {
return code;
}
@Override
public void handle(Ping notification) {
this.code = "Pon1";
}
}
public static class Pong2 implements INotificationHandler<Ping> {
private String code;
public String getCode() {
return code;
}
@Override
public void handle(Ping notification) {
this.code = "Pon2";
}
}
}
实现
核心实现
其主要点是从Spring的ApplicationContext中获取相关接口bean,然会执行bean方法。
核心方法有两个:public(多播)和send(单播)。
借助ResolvableType类型构造解析bean信息,得到信息后从spring中获取对象实例。
/**
* 中介者实现类
* <p>
* 依赖 ApplicationContext
*/
@Component
public class Mediator implements IMediator, ApplicationContextAware {
private ApplicationContext context;
/**
* 发布同步
* <p>
* 根据通知类型和INotificationHandler,从ApplicationContext获取Handler的BeanNames,
* 将 BeanNames 转化为 INotificationHandler 的实例,每个实例调用一次handler
*
* @param notification 通知内容
* @param <TNotification> 通知类型
*/
@Override
public <TNotification extends INotification> void publish(TNotification notification) {
ResolvableType handlerType = ResolvableType.forClassWithGenerics(
INotificationHandler.class, notification.getClass());
String[] beanNamesForType = this.context.getBeanNamesForType(handlerType);
List<INotificationHandler<TNotification>> list = new ArrayList<>();
for (String beanBane :
beanNamesForType) {
list.add((INotificationHandler<TNotification>) this.context.getBean(beanBane));
}
list.forEach(h -> h.handle(notification));
}
/**
* 发送求取
* <p>
* 根据request类型,获取到response类型,
* 根据IRequestHandler、request类型、response类型从ApplicationContext获取
* IRequestHandler实例列表,取第一个实例执行handler方法。
* <p>
* <p>
* 如果为找到handler实例,抛出NoRequestHandlerException异常
*
* @param request 请求
* @param <TResponse> 响应类型
* @return 响应结果
*/
@Override
public <TResponse> TResponse send(IRequest<TResponse> request) {
Type[] genericInterfaces = request.getClass().getGenericInterfaces();
Type responseType = null;
for (Type type : genericInterfaces) {
if ((type instanceof ParameterizedType)) {
ParameterizedType parameterizedType = (ParameterizedType) type;
if (!parameterizedType.getRawType().equals(IRequest.class)) {
continue;
}
responseType = parameterizedType.getActualTypeArguments()[0];
break;
}
}
if (responseType == null) {
// 抛出异常
throw new NoRequestHandlerException(request.getClass());
}
Class<?> requestClass = request.getClass();
Class<?> responseClass = (Class<?>) responseType;
ResolvableType handlerType = ResolvableType.forClassWithGenerics(
IRequestHandler.class,
requestClass,
responseClass);
String[] beanNamesForType = this.context.getBeanNamesForType(handlerType);
List<IRequestHandler<IRequest<TResponse>, TResponse>> list = new ArrayList<>();
for (String beanBane :
beanNamesForType) {
list.add((IRequestHandler<IRequest<TResponse>, TResponse>) this.context.getBean(beanBane));
}
if (list.isEmpty()) {
throw new NoRequestHandlerException(request.getClass());
}
return list.stream()
.findFirst()
.map(h -> h.handle(request))
.orElseThrow(() -> new NoRequestHandlerException(request.getClass()));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}
public interface IBaseRequest {
}
多播接口
public interface INotification {
}
单播接口
public interface IRequest<TResponse> extends IBaseRequest {
}
public interface IPublisher {
<TNotification extends INotification> void publish(TNotification notification);
}
public interface ISender {
<TResponse> TResponse send(IRequest<TResponse> request);
}
public interface IMediator extends ISender, IPublisher {
}
多播处理接口
public interface INotificationHandler<TNotification extends INotification> {
void handle(TNotification notification);
}
单播处理接口
public interface IRequestHandler<TRequest extends IRequest<TResponse>, TResponse> {
TResponse handle(TRequest request);
}
public abstract class AbsRequestHandler<TRequest extends IRequest<TResponse>, TResponse>
implements IRequestHandler<TRequest, TResponse> {
@Override
public abstract TResponse handle(TRequest request);
}
public abstract class AbsNotificationHandler<TNotification extends INotification>
implements INotificationHandler<TNotification> {
@Override
public abstract void handle(TNotification notification);
}
public class Unit implements Comparable<Unit> {
public static final Unit VALUE = new Unit();
private Unit() {
}
@Override
public boolean equals(Object obj) {
return true;
}
@Override
public int hashCode() {
return 0;
}
@Override
public String toString() {
return "()";
}
@Override
public int compareTo(@NotNull Unit o) {
return 0;
}
}
public interface IUnitRequest extends IRequest<Unit> {
}
public class MediatorException extends RuntimeException {
}
@Getter
public class NoRequestHandlerException extends MediatorException {
private Class<?> requestClass;
public NoRequestHandlerException(
Class<?> requestClass
) {
this.requestClass = requestClass;
}
}
应用场景
mediatr 是一种进程内消息传递机制,使用泛型支持消息的只能调度,其核心是 消息解耦
,基于MediatR可以实现CQRS/EventBus等。
解除构造函数的依赖注入
public class DashboardController(
ICustomerRepository customerRepository,
IOrderService orderService,
ICustomerHistoryRepository historyRepository,
IOrderRepository orderRepository,
IProductRespoitory productRespoitory,
IRelatedProductsRepository relatedProductsRepository,
ISupportService supportService,
ILog logge
) {
}
借助 Mediator,仅需构造注入ImediatR即可
public class DashboardController(
IMediator
) {
}
service 循环依赖,使用mediatr 进行依赖解耦,并使用mediatr进行消息传递
两个service类和接口如下
public static interface IDemoAService {
String hello();
String helloWithB();
}
public static interface IDemoBService {
String hello();
String helloWithA();
}
public static class DemoAService implements IDemoAService {
private final IDemoBService bService;
public DemoAService(IDemoBService aService) {
this.bService = aService;
}
@Override
public String hello() {
return this.bService.helloWithA();
}
@Override
public String helloWithB() {
return "call A in B";
}
}
public static class DemoBService implements IDemoBService {
private final IDemoAService aService;
public DemoBService(IDemoAService aService) {
this.aService = aService;
}
@Override
public String hello() {
return this.aService.helloWithB();
}
@Override
public String helloWithA() {
return "call B in A";
}
}
此时,如果通过构造函数或属性注入(@Autowird),程序在运行时会报一下错误, 提示检测是否包括循环引用
使用 mediatr 解耦循环依赖
使用mediatr的service如下,在service构造函数注 IMediator
,并实现 IRequestHandler
接口
public static class DemoAService implements IDemoAService, IRequestHandler<RequestAService, String> {
//private final IDemoBService bService;
private final IMediator mediator;
public DemoAService(IMediator mediator) {
this.mediator = mediator;
}
@Override
public String hello() {
return this.mediator.send(new RequestBService());
}
@Override
public String helloWithB() {
return "call A in B";
}
@Override
public String handle(RequestAService request) {
return this.helloWithB();
}
}
public static class DemoBService implements IDemoBService, IRequestHandler<RequestBService, String> {
//private final IDemoAService aService;
private final IMediator mediator;
public DemoBService(IMediator mediator) {
this.mediator = mediator;
}
@Override
public String hello() {
return this.mediator.send(new RequestAService());
}
@Override
public String helloWithA() {
return "call B in A";
}
@Override
public String handle(RequestBService request) {
return this.helloWithA();
}
}
public static class RequestAService implements IRequest<String> {
}
public static class RequestBService implements IRequest<String> {
}
测试代码如下
@ExtendWith(SpringExtension.class)
@Import(
value = {
Mediator.class,
ServiceCycTests.DemoAService.class,
ServiceCycTests.DemoBService.class,
}
)
public class ServiceCycTests {
@Autowired
IDemoAService aService;
@Autowired
IDemoBService bService;
@Test
public void should() {
String a = aService.hello();
assertThat(a).isEqualTo("call B in A");
String b = bService.hello();
assertThat(b).isEqualTo("call A in B");
}
}
测试结果
关注我的公众号,一起探索新知识新技术
Spring 版MediatR--中介者模式实现库的更多相关文章
- [译]ASP.NET Core中使用MediatR实现命令和中介者模式
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9866068.html 在本文中,我将解释命令模式,以及如何利用基于命令模式的第三方库来实现它们,以及如何 ...
- NET Core中使用MediatR实现命令和中介者模式
NET Core中使用MediatR实现命令和中介者模式 https://www.cnblogs.com/yilezhu/p/9866068.html 在本文中,我将解释命令模式,以及如何利用基于命令 ...
- 在 ASP.NET Core 项目中使用 MediatR 实现中介者模式
一.前言 最近有在看 DDD 的相关资料以及微软的 eShopOnContainers 这个项目中基于 DDD 的架构设计,在 Ordering 这个示例服务中,可以看到各层之间的代码调用与我们之前 ...
- 一起来学习.net core程序使用中介者模式:MediatR插件
中介者模式是一种常见的设计模式,旨再降低程序的耦合性,因为传统的三层模式层层之间需要显示的调用,必须上层依赖下层,耦合性很高,为了解耦,将所有的指令单独放在一个位置处理,其他位置均通过这个位置来间接的 ...
- 中介者模式及在NetCore中的使用MediatR来实现
在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是"网状结构",它要求每个对象都必须知道它需要交互的对象.例如,每个人必须记住他(她)所有朋友的电话:而且, ...
- 中介者模式c#(媒婆版)
using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace 中介者模式{ ...
- C#设计模式之12:中介者模式
中介者模式 在asp.net core中实现进程内的CQRS时用mediatR是非常方便的,定义command,然后定义commandhandler,或者notification和notificati ...
- Java设计模式-中介者模式(Mediator)
中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改.如果使用中介者模式,只需关心和Mediator类的关系 ...
- 24种设计模式--中介者模式【Mediator Pattern】
各位好,大家都是来自五湖四海,都要生存,于是都找了个靠山——公司,给你发薪水的地方,那公司就要想尽办法盈利赚钱,盈利方法则不尽相同,但是作为公司都有相同三个环节:采购.销售和库存,这个怎么说呢?比如一 ...
随机推荐
- 一个Bug,让我发现了 Java 界的.AJ(锥)!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 话我放这,踩过的坑越多头发越少! 说来也是奇怪,只要是学编程的,从初次接触的 Jav ...
- 浅析 Find My 原理
什么是 Find My 网络? 2021年4月21日的苹果发布会发布了一款新的产品:AirTag.使用的是 BLE + UWB 的技术.BLE 和 Find My 网络为 AirTag 提供了 GPS ...
- JPA之排序条件查询
List<Monitoring> monitoringList = repository.findAll((root, query, cb) -> { List<Predica ...
- PTA沈师数据库原理——DB(10)_SQL实验题
R10-1 A1-3查询顾客表中所有不重复的城市 (2 分) select distinct Cityfrom customersR10-2 查询学生表所有学生记录 (2 分) select * fr ...
- 1061 Dating
Sherlock Holmes received a note with some strange strings: Let's date! 3485djDkxh4hhGE 2984akDfkkkkg ...
- 不可不知的CSS小技巧
一.表单部分 1.禁止textarea文本域的缩放 resize:none; 2.去除初始化textarea下拉条 overflow:auto; 3.如何让表单中的选项按钮,点击文字也能选中? < ...
- Laravel 队列功能 简单应用
生成任务类 默认情况下,应用程序的所有可排队任务都存储在 app/Jobs 目录下.如果 app/Jobs 目录不存在,则会在运行 make:job Artisan 命令时将创建它.你可以使用 Art ...
- dex文件格式学习
一.dex文件的生成 我们可以通过java文件来生成一个简单的dex文件 编译过程: 首先编写java代码如下: (1) 编译成 java class 文件 执行命令 : javac Hello.ja ...
- Python字体颜色设置
Python字体颜色设置 平时学习工作中,我们经常会接触到一些大佬写的Python工具,运行起来总会显示出五颜六色的字体,比如红色代表Error , 黄色代表Warning , 绿色代表Success ...
- Intel汇编语言程序设计学习-第二章 IA-32处理器体系结构-下
2.2 IA-32处理器体系结构 如前所述,IA-32是指始于Intel386直到当前最新的奔腾4的系列的处理器(额...这本书是什么时候写的啊,表示现在应该是I7啊),在IA-32的发展过程中,I ...