java链路模式_JAVA设计模式之责任链设计模式

本文介绍了责任链模式,用于解耦请求的发送者和接收者。在微服务项目中,网关通过责任链模式实现权限控制,如API限流、黑名单拦截、用户会话检查和参数过滤。通过创建抽象的handler接口和具体实现,形成链路,并使用工厂模式初始化链路。文章展示了如何优化代码,减少冗余,并探讨了如何通过数据库动态管理handler的顺序,提高系统的可扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

责任链模式:责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用:在处理消息的时候以过滤很多道。

如何解决:拦截的类都实现统一接口。

关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例: 1、红楼梦中的"击鼓传花"。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

废话不多说,直接上手:

项目背景:网关作为微服务项目入口,拦截客户端所有的请求实现权限控制,如判断API接口限流->黑名单拦截->用户会话->参数过滤

环境准备:JDK8、springboot2.0.x、idea工具、mysql数据库...

整体流程可以缩概为如下图所示:

99124793addf573e1ba47a096d6c04d1.png

1、定义抽象的handler接口

/**

* 网关拦截器

* @author zhoumin

* @create 2020-03-14 13:20

*/

public abstract class GatewayHandler {

/**

* 使用抽象类定义共同的行为方法

*/

public abstract void service();

}

2、定义具体实现事项

/**

* @author zhoumin

* @create 2020-03-14 13:25

*/

public class ApiLimitHandler extends GatewayHandler {

//这里需要指定下一个handler

private BlacklistHandler blacklistHandler;

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("1、api接口限流.........");

//下一个handler

blacklistHandler.service();

}

//指定下一个handler

public void setNextGatewayHandler(BlacklistHandler blacklistHandler) {

this.blacklistHandler = blacklistHandler;

}

}

/**

* 黑名单拦截

*

* @author zhoumin

* @create 2020-03-14 13:26

*/

public class BlacklistHandler extends GatewayHandler {

//这里需要指定下一个handler

private ConversationHandler conversationHandler;

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("2、黑名单拦截.........");

//下一个handler

conversationHandler.service();

}

//指定下一个handler

public void setNextGatewayHandler(ConversationHandler conversationHandler) {

this.conversationHandler = conversationHandler;

}

}

/**

* 用户会话拦截

*

* @author zhoumin

* @create 2020-03-14 13:27

*/

public class ConversationHandler extends GatewayHandler {

//这里需要指定下一个handler

private ParamHandler paramHandler;

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("3、用户会话拦截.........");

//下一个handler

paramHandler.service();

}

//指定下一个handler

public void setNextGatewayHandler(ParamHandler paramHandler) {

this.paramHandler = paramHandler;

}

}

/**

* 参数拦截

*

* @author zhoumin

* @create 2020-03-14 13:30

*/

public class ParamHandler extends GatewayHandler {

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("4、用户参数过滤.........");

}

}

注意:需要在每个具体handler里面执行下一个需要执行的handler,直到最后一个

这个时候发现,只要获取第一个handler后,并执行,那么整个链路就能顺利完成,那么怎么获取第一个handler呢?答案是:使用工厂模型

/**

* 工厂创建对象

*

* @author zhoumin

* @create 2020-03-14 13:49

*/

public class FactoryHandler {

//这里static 为了方便后面调试运行

public static ApiLimitHandler getFirstGatewayHandler(){

ApiLimitHandler apiLimitHandler = new ApiLimitHandler();

BlacklistHandler blacklistHandler = new BlacklistHandler();

apiLimitHandler.setNextGatewayHandler(blacklistHandler);

ConversationHandler conversationHandler = new ConversationHandler();

blacklistHandler.setNextGatewayHandler(conversationHandler);

ParamHandler paramHandler = new ParamHandler();

conversationHandler.setNextGatewayHandler(paramHandler);

return apiLimitHandler;

}

}

至此,完成了整个流程,现在可以来简单测试下

/**

* @author zhoumin

* @create 2020-03-14 14:49

*/

@RestController

public class HandlerController {

@GetMapping("/clientHandler")

public String clientHandler(){

ApiLimitHandler apiLimitHandler = FactoryHandler.getFirstGatewayHandler();

apiLimitHandler.service();

return "success!!";

}

}

运行后可以看到控制台打印数据:

8c4a4da06ff25d83681c76de9e36c0de.png

断点调试可以清晰看到整个链路结构:

0cd619ffae1324f8475d578d9965a1ce.png

我是一个分割线emmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm.....

美中不足,我们发现代码有很多冗余,包括setNextGatewayHandler以及出现在每个handler中的下个handler,那么如何优化呢?------------->将公共方法抽取到父类中!

/**

* 网关拦截器

* @author zhoumin

* @create 2020-03-14 13:20

*/

public abstract class GatewayHandler {

protected GatewayHandler nextGatewayHandler;

/**

* 使用抽象类定义共同的行为方法

*/

public abstract void service();

//设置下一个handler

public void setNextGatewayHandler(GatewayHandler gatewayHandler){

this.nextGatewayHandler = gatewayHandler;

}

//执行下一个handler

protected void nextService(){

if (nextGatewayHandler != null){

nextGatewayHandler.service();

}

}

}

子类优化:

/**

* @author zhoumin

* @create 2020-03-14 13:25

*/

public class ApiLimitHandler extends GatewayHandler {

//这里需要指定下一个handler

// private BlacklistHandler blacklistHandler;

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("1、api接口限流.........");

//下一个handler

// blacklistHandler.service();

nextService();

}

//指定下一个handler

// public void setNextGatewayHandler(BlacklistHandler blacklistHandler) {

// this.blacklistHandler = blacklistHandler;

// }

}

/**

* 黑名单拦截

*

* @author zhoumin

* @create 2020-03-14 13:26

*/

public class BlacklistHandler extends GatewayHandler {

//这里需要指定下一个handler

// private ConversationHandler conversationHandler;

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("2、黑名单拦截.........");

//下一个handler

// conversationHandler.service();

nextService();

}

//指定下一个handler

// public void setNextGatewayHandler(ConversationHandler conversationHandler) {

// this.conversationHandler = conversationHandler;

// }

}

/**

* 用户会话拦截

*

* @author zhoumin

* @create 2020-03-14 13:27

*/

public class ConversationHandler extends GatewayHandler {

//这里需要指定下一个handler

// private ParamHandler paramHandler;

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("3、用户会话拦截.........");

//下一个handler

// paramHandler.service();

nextService();

}

//指定下一个handler

// public void setNextGatewayHandler(ParamHandler paramHandler) {

// this.paramHandler = paramHandler;

// }

}

/**

* 参数拦截

*

* @author zhoumin

* @create 2020-03-14 13:30

*/

public class ParamHandler extends GatewayHandler {

/**

* 使用抽象类定义共同的行为方法

*/

@Override

public void service() {

System.out.println("4、用户参数过滤.........");

}

}

运行结果跟上图保持一致。

至此,结束了吗?显然没有,继续.....

可以看到我们在Factory中定义的关系强耦合,如果现在我们需要改变顺序或者是新增其他handler,需要改动代码,可扩展性较差。

结合上一章,启示可以把beanId关系维护到数据库中,利用spring容器,根据beanId获取到每个具体的实例对象(注:需要将上面每一个具体handler注入到spring容器中)。

现有数据结构和数据关系如下:

056896a63749edea4d05c3d4a11b20df.png

可以很清晰看到,第一个handler的prev为null,next为我们指定的handler;而最后一个handler的next为null。

定义我们的实体类以及mapper如下:

/**

* @author zhoumin

* @create 2020-03-14 15:35

*/

@Data

public class GatewayHandlerEntity {

/** 主键ID */

private Integer id;

/** handler名称 */

private String handlerName;

/** handler主键id */

private String handlerId;

/** 下一个handler */

private String nextHandlerId;

}

/**

* @author zhoumin

* @create 2020-03-14 15:33

*/

@Mapper

public interface GatewayHandlerMapper {

/**

* 获取第一个handler

* @return

*/

GatewayHandlerEntity getFirstGatewayHandler();

/**

* 根据beanId获取当前handler

* @return

*/

GatewayHandlerEntity getByHandler(String handlerId);

}

/p>

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

SELECT

handler_name AS handlerName,

handler_id AS handlerid ,

prev_handler_id AS prevhandlerid ,

next_handler_id AS nexthandlerid

FROM gateway_handler

WHERE prev_handler_id is null

SELECT

handler_name AS handlerName,

handler_id AS handlerid ,

prev_handler_id AS prevhandlerid ,

next_handler_id AS nexthandlerid

FROM gateway_handler

WHERE handler_id=#{handlerId}

对应的service可以处理为:

/**

* @author zhoumin

* @create 2020-03-14 15:38

*/

@Component

public class GatewayHandlerService {

@Resource

private GatewayHandlerMapper gatewayHandlerMapper;

private GatewayHandler firstGatewayHandler;

public GatewayHandler getDbFirstGatewayHandler() {

//判断是否加载过

if (this.firstGatewayHandler != null) {

return this.firstGatewayHandler;

}

// 1.查询数据库第一个handler配置

GatewayHandlerEntity firstGatewayHandlerEntity = gatewayHandlerMapper.getFirstGatewayHandler();

if (firstGatewayHandlerEntity == null) {

return null;

}

String firstBeanHandlerId = firstGatewayHandlerEntity.getHandlerId();

if (StringUtils.isEmpty(firstBeanHandlerId)) {

return null;

}

// 2.根据beanId,从SpringBoot容器获取第一个handler对象

GatewayHandler firstGatewayHandler = SpringUtils.getBean(firstBeanHandlerId, GatewayHandler.class);

if (firstGatewayHandler == null) {

return null;

}

// 3. 获取下一个handlerBeanId

String nextBeanHandlerId = firstGatewayHandlerEntity.getNextHandlerId();

// 定义临时循环遍历指针

GatewayHandler tempNextGatewayHandler = firstGatewayHandler;

while (StringUtils.isNotEmpty(nextBeanHandlerId)) {

// 4.根据beanId,从SpringBoot容器获取下一个handler对象

GatewayHandler nextGatewayHandler = SpringUtils.getBean(nextBeanHandlerId, GatewayHandler.class);

if (nextGatewayHandler == null) {

break;

}

// 5.从数据库查询该hanlder信息,从而获取下一个beanId

GatewayHandlerEntity nextGatewayHandlerEntity = gatewayHandlerMapper.getByHandler(nextBeanHandlerId);

if (nextGatewayHandlerEntity == null) {

break;

}

// 6.设置下一个white循环遍历hanlderid

nextBeanHandlerId = nextGatewayHandlerEntity.getNextHandlerId();

tempNextGatewayHandler.setNextGatewayHandler(nextGatewayHandler);

tempNextGatewayHandler = nextGatewayHandler;

}

//设置只在启动时加载查询一次

this.firstGatewayHandler = firstGatewayHandler;

return firstGatewayHandler;

}

}

相应,做一次测试:

/**

* @author zhoumin

* @create 2020-03-14 14:49

*/

@RestController

public class HandlerController {

@Autowired

private GatewayHandlerService gatewayHandlerService;

@GetMapping("/clientHandler")

public String clientHandler(){

ApiLimitHandler apiLimitHandler = FactoryHandler.getFirstGatewayHandler();

apiLimitHandler.service();

return "success!!";

}

@GetMapping("/clientHandler2")

public String clientHandler2(){

GatewayHandler gatewayHandler = gatewayHandlerService.getDbFirstGatewayHandler();

gatewayHandler.service();

return "success!!";

}

}

运行后,数据如下:

f06c1a3d4ca4fac3e9f072f87cab60a0.png

如果想调节顺序,或者是新增节点,只需要修改数据库即可,相应,我们也可以把这块放到我们自己的管理后台管理。

在常用代码中,过滤器Filter就是这一模型很好的应用实例,具体可以看下:

d5ed07e576031655ade328dfb28b5998.png

doFilter即类似我们这里的nextService()方法。

352623d541ae07525bcdb9eeef70cc14.png

完~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值