引言

    中介者,何为中介者,顾名思义就是我们的在处理A和B之间的关系的时候,引入一个中间人,来处理这两者之间的关系,例如生活中我们需要去租房,买房,都会有中介,来处理房东和租客之间的协调关系,这个就是中介者,落实到具体的代码中呢,就像我们的Controller可能会依赖很多的Service层面的东西,在这里的代码都会注入很多Service,导致依赖很严重,耦合较高,并且多的情况下会使得Controller的代码看起来并不是那么简洁,而中介者模式,恰恰可以解决这一痛楚,降低Controller和Service层面的依赖,通过一个接口,来实现Controller调用业务层面的操作,在中介者体系下,我们更多的是关注我们自身的业务,以及在业务之中,我们如何构建我们的业务模型,以及每个业务需要做的事件处理操作即可,换做以往的开发模式,我们需要依赖Bussiness A,B,C三个Service。在引入了Mediator中介者模式,我们的Controller只需要注入并引入IMediator的对象,即可实现调用对应的A,B,C的业务操作。接下来,我们一起看看,如何设计以及如何使用。

                    

设计

    首先我是一名使用c#开发多于Java开发的人员,所以代码的编写风格,更多的是趋向于c#的写法,诸位请见谅,同时在中介者模式,Mediator在c#那边有一个成熟的框架,并且已经很多人在使用,在这里我只是简单的实现一个Java的一个版本,诸位,请莫见怪。整体的设计如下,在自动装配,3以前的版本和以后的版本是一样的,踩了个坑,以前老的方式是META-INF下面创建一个spring.factories,里面写入你自动配置的类,在新版本则是在META-INF下面创建一个spring文件夹,里面创建一个org.springframework.boot.autoconfigure.AutoConfiguration.imports的文件,在这里去写你要自动配置的类的路径即可,多个是多行,一行一个。                                   

         

    看上面的目录结构,我们可以看到有三个注解,第一个注解代表某个类是一个CommandHandler,这个类必须实现ICommahdHandler的接口,或者IEmptyCommandHandler接口,接口定义如下,里面都只是一个Handle的方法,ICommandHandler的泛型T必须实现IRequest的接口,这个代表是方法的入参,同时IRequest的泛型R代表是Handle的返回值,这个可以理解为,我们一个业务Handle的请求和响应。而下面的IEmptyCommandHandler是一个无返回值但是有入参的一个操作接口,其实这个可以用ApplicationEvent来代替也是没问题的,同时在C#的Mediator也提供了Publish的方法 ,这个也是没有返回值,其用法和Application.publishEvent是一样的,所以我在代码里仅定义了接口,并没有去实现。当我们自己实现了CommandHandler的接口以及请求和响应的时候,在Controller我们就只需要定义一个IMediator的对象即可,Autowired获取就行了。

package com.mediator.Handler;

import com.mediator.Request.IRequest;

import java.util.concurrent.Future;

public interface ICommandHandler<T extends IRequest<R>,R>  {

    R Handle(T request);
}
package com.mediator.Handler; import com.mediator.Request.IEmptyRequest; public interface IEmptyCommandHandler<T extends IEmptyRequest>
{
void Handle(T request);
}
package com.mediator.Request;

public interface IRequest<R> {

}
public interface IEmptyRequest {
}

    这里是MediatorComfiguration的具体代码,我们需要获取到ApplicationContext上下文对象,在执行我们的InjectMediator的方法的时候,我们会先去找看有没有使用EnableCommandHandler注解的类对象,以此来判断有没有开启中介者模式并且使用,然后我们这个注解定义了Path,这是一个String,保存我们需要扫描的包路径,如果路径不为空,就获取默认的BeanFactory对象,我们需要来动态的注入我们的实现的各种Handler到容器以及PipeLine,在scanCommandHandlerClasses方法里,我们去根据包路径去扫描这个包下面,使用了CommandHandler注解的所有的类,最终获取到了一个BeanDefinition集合,我们获取到了Bean的ClassName作为Bean的名字,同时设置生命周期是request的,注入到容器里面,下面的是获取PipeLine注解的实现类的,标记这个注解代表的是Handler接口的Aop实现对象,也是定义了一个是空的Handler的Aop和CommandHandler的Aop,三个方法分别代表,在执行Handler之前,之后,以及出现异常的时候的调用。

public interface IPipeline<T,R> {
void BeforeForHandler(IRequest<R> para);
void AfterForHandler(R res);
void HandlerException(Exception ex);
}
public interface IEmptyPipeline<T> {
void BeforeForHandler(IEmptyRequest para);
void AfterForHandler();
void HandlerException(Exception ex);
}
package com.mediator;

import com.mediator.Annotations.CommandHandler;
import com.mediator.Annotations.EnableCommandHandler;
import com.mediator.Annotations.PipeLine;
import com.mediator.Mediator.IMediator;
import com.mediator.Mediator.impl.Mediator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter; import java.util.Set; @Configuration
public class MediatorConfiguration { @Autowired
private ApplicationContext context;
public MediatorConfiguration()
{ }
@Bean
public IMediator InjectMediator()
{
var enable=context.getBeansWithAnnotation(EnableCommandHandler.class);
if (!enable.isEmpty())
{
var application=enable.values().iterator().next();
EnableCommandHandler handler = AnnotationUtils.findAnnotation(application.getClass(), EnableCommandHandler.class);
if (handler!=null)
{
var path=handler.path();
if (!path.isEmpty())
{ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) context;
DefaultListableBeanFactory factory=(DefaultListableBeanFactory)configurableApplicationContext.getBeanFactory();
var allCommandClass=scanCommandHandlerClasses(path);
if (!allCommandClass.isEmpty())
{
for(BeanDefinition item:allCommandClass)
{
var name=item.getBeanClassName();
item.setScope("request");
factory.registerBeanDefinition(name,item);
}
}
var pipeClass=scanPipeLineClasses(path);
if (!pipeClass.isEmpty())
{
for(BeanDefinition item:pipeClass)
{
var name=item.getBeanClassName();
item.setScope("request");
factory.registerBeanDefinition(name,item);
}
}
}
}
}
return new Mediator();
}
private Set<BeanDefinition> scanCommandHandlerClasses(String basePackage) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
ClassPathBeanDefinitionScanner scanner =
new ClassPathBeanDefinitionScanner(context,true);
// 添加过滤条件,只扫描带有 @CommandHandler 注解的类
scanner.addIncludeFilter(new AnnotationTypeFilter(CommandHandler.class));
// 扫描指定包路径
Set<BeanDefinition> candidates = scanner.findCandidateComponents(basePackage);
return candidates;
}
private Set<BeanDefinition> scanPipeLineClasses(String basePackage) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
ClassPathBeanDefinitionScanner scanner =
new ClassPathBeanDefinitionScanner(context,true);
// 添加过滤条件,只扫描带有 @PipeLine 注解的类
scanner.addIncludeFilter(new AnnotationTypeFilter(PipeLine.class));
// 扫描指定包路径
Set<BeanDefinition> candidates = scanner.findCandidateComponents(basePackage);
return candidates;
}
}

    IMediator里就提供了两个可以用的方法,有返回值和无返回值的Send方法,分别对应执行的是ICommandHandler和IEmptyCommandHandler两个接口的实现,publish方法没有实现就是因为可以用ApplicationEvent去实现,可以看下面的实现,我们会先获取入参的Class对象,这里是IRequest是一个泛型,所以我们获取它第一个泛型的ParameterType,在下面获取泛型的具体的Class对象,这样我们就从Bean里面拿我们之前注入的自己实现的ICommandHandler的ResolvableType,同时获取对应的PipeLine的ResolvableType,在下面根据ResolvableType拿到Bean的Provider,同时判断如果存在这个Bean就赋值给我们的pipe对象,如果不存在就为null,在下面我们就去获取我们的ICommandHandler的对象,同理去拿Provider,根据上面我们的判断,是否存在PipeLine的Aop,如果存在,调用前就去调用BeforeForHandler方法,传入入参,继续往下走就是调用Handle的方法,获取返回值,在调用AfterForHandler的方法,同时下面有异常的时候,会调用异常的方法。下面的EmptyHandler无返回值的调用和有返回值的同出一辙。接下来我们看看,具体在代码中如何使用。

public interface IMediator {

    <R> R Send(IRequest<R> value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
<T>void Publish(INotification<T> notification);
void Send(IEmptyRequest val);
}
package com.mediator.Mediator.impl;

import com.mediator.Aop.IEmptyPipeline;
import com.mediator.Aop.IPipeline;
import com.mediator.Handler.ICommandHandler;
import com.mediator.Handler.IEmptyCommandHandler;
import com.mediator.Mediator.IMediator;
import com.mediator.Request.IEmptyRequest;
import com.mediator.Request.INotification;
import com.mediator.Request.IRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
@Component
public class Mediator implements IMediator {
@Autowired
private ApplicationContext context; public Mediator()
{
}
@Override
public <R> R Send(IRequest<R> values) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
if (values!=null)
{
var valuesClass=values.getClass();
var parameter=valuesClass.getGenericInterfaces()[0];
if (parameter instanceof ParameterizedType)
{
Class generaType=(Class) ((ParameterizedType) parameter).getActualTypeArguments()[0];
var commandresolvableType=ResolvableType.forClassWithGenerics(ICommandHandler.class,valuesClass,generaType);
var pipeLineresolvableType=ResolvableType.forClassWithGenerics(IPipeline.class,valuesClass,generaType);
IPipeline pipe=null;
if (pipeLineresolvableType!=null)
{
var pipeLineprovider=context.getBeanProvider(pipeLineresolvableType);
if (pipeLineprovider!=null)
{
var object=pipeLineprovider.getIfAvailable();
pipe=object!=null?(IPipeline)object:null;
}
}
boolean bPipeIsNull=pipe==null?true:false;
if (commandresolvableType!=null)
{
var provider=context.getBeanProvider(commandresolvableType);
var handlerobject=provider.getIfAvailable();
ICommandHandler handler=handlerobject!=null?(ICommandHandler)handlerobject:null;
if (!bPipeIsNull)
{
pipe.BeforeForHandler(values);
}
if (handler!=null)
{
try {
R res=(R)handler.Handle(values);
if (!bPipeIsNull)
{
pipe.AfterForHandler(res);
}
return res;
}
catch (Exception ex)
{
if (!bPipeIsNull) {
pipe.HandlerException(ex);
}
}
}
}
}
}
return null;
} @Override
public <T> void Publish(INotification<T> notification)
{ } @Override
public void Send(IEmptyRequest val)
{
if (val!=null)
{
var classObj=val.getClass();
var commandresolvableType=ResolvableType.forClassWithGenerics(IEmptyCommandHandler.class,classObj);
var pipeLineresolvableType=ResolvableType.forClassWithGenerics(IEmptyPipeline.class,classObj);
IEmptyPipeline pipe=null;
if (pipeLineresolvableType!=null)
{
var pipeLineprovider=context.getBeanProvider(pipeLineresolvableType);
if (pipeLineprovider!=null)
{
var object=pipeLineprovider.getIfAvailable();
pipe=object!=null?(IEmptyPipeline)object:null;
}
}
boolean bPipeIsNull=pipe==null?true:false;
if (commandresolvableType!=null)
{
var provider=context.getBeanProvider(commandresolvableType);
var handlerobject=provider.getIfAvailable();
IEmptyCommandHandler handler=handlerobject!=null?(IEmptyCommandHandler)handlerobject:null;
if (handler!=null)
{
try
{
if (!bPipeIsNull) {
pipe.BeforeForHandler(val);
}
handler.Handle(val);
if (!bPipeIsNull) {
pipe.AfterForHandler();
}
}
catch (Exception ex)
{
if (!bPipeIsNull) {
pipe.HandlerException(ex);
}
}
}
}
}
}
}

使用

    在做的测试项目里,需要在启动类先标记EnableCommandHandler注解,设置存放CommandHandler和Aop的包的路径,然后,下面第二段代码是我定义的一些测试的Request和Response,需要实现特定的接口标记是Request,并且指名Response。

@SpringBootApplication
@EnableAsync
@EnableCommandHandler(path = "com.example.streamtransport.Handler")
public class StreamTransportApplication { public static void main(String[] args) { SpringApplication.run(StreamTransportApplication.class, args);
} }
public class TestARequest implements IRequest<TestAResponse> {

}

public class TestAResponse {

}
public class EmptyRequest implements IEmptyRequest {
}
public class BussinessRequest implements IRequest<BussinessResponse> {
}
public class BussinessResponse {
}

  下面这段代码是实现了自定义Handler,上面标记我们是一个CommandHandler,无返回值的Handler和有返回值的一样,不过不同的实现接口而已,同时在我们的CommandHandler,可以依赖IMediator,我们也可以在ACommandHandler调用BCommandHandler,在这样的模式下,我们只关注我们的入参和响应模型,

@CommandHandler
public class TestHandler implements ICommandHandler<TestARequest, TestAResponse> {
public TestHandler()
{
var t="1";
}
@Override
public TestAResponse Handle(TestARequest request) {
return new TestAResponse();
}
}
@CommandHandler
public class EmptyCommandHandler implements IEmptyCommandHandler<EmptyRequest> {
@Override
public void Handle(EmptyRequest request)
{
var a=1;
}
}
@CommandHandler
public class BussinessHandler implements ICommandHandler<BussinessRequest, BussinessResponse> {
@Autowired
private IMediator mediator;
@Override
public BussinessResponse Handle(BussinessRequest request) {
mediator.Send(new EmptyRequest());
return new BussinessResponse();
}
}

    下面这段代码,则是我们自己实现的Aop管道,实现三个方法,并且输出每一步骤执行的具体的动作。接下来,我们跑起来,看看输出。

@PipeLine
public class Pipeline implements IPipeline<TestARequest, TestAResponse> {
@Override
public void BeforeForHandler(IRequest<TestAResponse> para)
{
System.out.println("BeforeForHandler");
} @Override
public void AfterForHandler(TestAResponse res)
{
System.out.println("AfterForHandler");
} @Override
public void HandlerException(Exception ex)
{
System.out.println("HandlerException");
}
} @PipeLine
public class EmptyPipeLine implements IEmptyPipeline<EmptyRequest> {
@Override
public void BeforeForHandler(IEmptyRequest para) { System.out.println("EmptyBeforeForHandler");
} @Override
public void AfterForHandler() {
System.out.println("EmptyAfterForHandler");
} @Override
public void HandlerException(Exception ex) {
System.out.println("EmptyException");
}
}

    下面的图片中,GetHellos方法刚开始调用了一个Send,传入了一个EmptyRequest对象,输出了第一次的EmptyBeforeForHandler,EmptyAfterForHandler,在后面我们调用了有返回值的传入了TestARequest对象,输出了BeforeForHandler和AfterForHandler,由于在上面的Aop我们没有定义BussinessHandler对应的PipeLine,所以在这里输出是没有Bussiness的输出,但是我们在BussinessHandler里面有Send了一次EmptyRequest,所以最后又输出了一边Empty的Aop的输出。

结束

    以上便是我今天的分享的内容,Java大腿子请莫见怪,代码能跑就行,并没有做更多的优化,仅仅作为一个分享的例子,谢谢大家,代码已上传至Gitee,

    Mediator:https://gitee.com/cxd199645/mediator.git

    MediatorDemo:https://gitee.com/cxd199645/mediator-demo.git

    有什么问题可以一起讨论。

【中介者模式(Mediator)】使用Java实现中介者模式的更多相关文章

  1. 《Java设计模式》之调停者模式(Mediator)

    调停者模式是对象的行为模式.调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显引用.从而使它们能够较松散地耦合.当这些对象中的某些对象之间的相互作用发生改变时,不会马上影响到其它的一些 ...

  2. 《JAVA设计模式》之中介者模式(Mediator)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述调停者(Mediator)模式的: 调停者模式是对象的行为模式.调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显引用.从 ...

  3. JAVA 设计模式 中介者模式

    用途 中介者模式 (Mediator) 用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 中介者模式是一种行为型模式. 结 ...

  4. Java设计模式----中介者模式

    说到中介大家都不会陌生,买房子租房子有中介,出国留学有中介,买卖二手车还是有中介.那么中介到底是个什么角色呢?实际上,中介就是让买卖双方不必面对面直接交流,由他/她来完成买卖双方的交易,达到解耦买卖人 ...

  5. 调停者模式Mediator(中介者模式)详解

    原文链接:https://www.cnblogs.com/java-my-life/archive/2012/06/20/2554024.html 在阎宏博士的<JAVA与模式>一书中开头 ...

  6. [工作中的设计模式]中介模式模式Mediator

    一.模式解析 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互. 中介模式又叫调停者模式,他有如下特点: 1.有多个系统或者对 ...

  7. 18.中介者模式(Mediator Pattern)

    using System; namespace Test { class Program { /// <summary> /// 中介者模式,定义了一个中介对象来封装一系列对象之间的交互关 ...

  8. 中介者模式(Mediator)

    GOF:用一个中介对象来封装一系列的对象交互.中介者使对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 类图:

  9. 【转】设计模式 ( 十五 ) 中介者模式Mediator(对象行为型)

    设计模式 ( 十五 ) 中介者模式Mediator(对象行为型) 1.概述 在面向对象的软件设计与开发过程中,根据"单一职责原则",我们应该尽量将对象细化,使其只负责或呈现单一的职 ...

  10. 二十四种设计模式:中介者模式(Mediator Pattern)

    中介者模式(Mediator Pattern) 介绍用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 示例有一个Messa ...

随机推荐

  1. modeless dialog in html

    <!DOCTYPE html> <html lang="zh_CN"> <head> <meta charset="UTF-8& ...

  2. 苹果推信群发,苹果推信群发软件,iMessage群发系统

    在当今数字化的时代,智能手机的普及率已达到了前所未有的高度,其中,苹果公司的iPhone无疑是市场上最受欢迎的智能手机之一,然而,与手机的广泛应用相伴的是,众多企业对于如何有效地向这些手机用户推送信息 ...

  3. ElasticSearch之cat thread pool API

    命令样例如下: curl -X GET "https://localhost:9200/_cat/thread_pool?v=true&pretty" --cacert $ ...

  4. Linux发行版的基础目录名称、命名法则及功能规定

    罗列Linux发行版的基础目录名称命名法则及功用规定 目录描述 /主层次 的根,也是整个文件系统层次结构的根目录 /bin存放在单用户模式可用的必要命令二进制文件,所有用户都可用,如 cat.ls.c ...

  5. 常见的Java中SQL注解的用法

    @Select:用于查询操作,标注在方法上,指定相应的SQL查询语句. @Select("SELECT * FROM table_name WHERE condition") Li ...

  6. Spring Boot3 系列:Spring Boot3 跨域配置 Cors

    目录 什么是CORS? Spring Boot 如何配置CORS? 前端代码 注解配置 全局配置 过滤器配置 注意事项 什么是CORS? CORS,全称是"跨源资源共享"(Cros ...

  7. Spark Streaming快速入门

    Spark Streaming快速入门 一.简介 Spark Streaming 是构建在 Spark Core 基础之上的流处理框架(但实际上是微批次处理框架),是 Spark 非常重要的组成部分. ...

  8. C++中自定义结构体或类作为关联容器的键

    目录 1. 概述 2. 实例 1. 概述 STL中像set和map这样的容器是通过红黑树来实现的,插入到容器中的对象是顺序存放的,采用这样的方式是非常便于查找的,查找效率能够达到O(log n).所以 ...

  9. BeanDefinition解密:构建和管理Spring Beans的基石

    本文分享自华为云社区<Spring高手之路11--BeanDefinition解密:构建和管理Spring Beans的基石>,作者: 砖业洋__ . BeanDefinition是Spr ...

  10. 划重点丨详解Java流程控制语句知识点

    摘要:流程控制语句就是用来控制程序中各语句执行的顺序,下面将详细介绍java流程控制语句. 流程控制语句就是用来控制程序中各语句执行的顺序,下面将详细介绍java流程控制语句. Q: break后面加 ...