⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 Java知音 「idea」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

「什么是责任链」

责任链模式(Chain of Responsibility Pattern):责任链模式是⼀种对象的⾏为模式。

我们可以简单地理解为:当一个请求从执行开始到结束,需要经过很多层的处理,此时我们可以将这些不同层的处理给抽象出来,其中的每一层就是一个过滤链路,将它们窜起来之后,就会形成链状结构。如下图所示:

「为什么要这么设计,这样设计的好处在哪?」

「解耦:」 可以将每一层的逻辑都单独封装起来,互相不干预,达到解耦的效果。另外调用方也不必关心链路内部的那些handler都做了什么,链路内部的数据传递关系统一封装好。

「开闭原则:」 其实统一封装成多层之后,也满足了设计原则中的开闭原则,只需要新增或移开一项handler,就可以不影响其他层代码,直接改变原先责任链的逻辑。

「责任链模式有什么不足点?」

这里我归纳了下几个在实践中发现的不足点:

「链路长:」 请求必须要从链头出发,执行到链路末端,所以在链路的每一层中对于代码的性能需要谨慎考虑。

「排查困难:」 如果一旦任意一环出现了问题,要记得打好日志。

「长链路的业务代码就一定适合使用责任链模式吗?」

这里我先给出我的思考答案:不。我们来看看下边这个代码案例:

这是一段看起来非常长而且业务层次也很清晰的一段代码,如果我们将这段代码改成用责任链模式来实现,大概就是这样的。

这样看来代码变得简单了许多,调用方也确实不知道这里面处理了什么东西。但是这样的做法合适吗?

其实这里我们漏了一个步骤,那就是没有结合业务场景进行分析,过度设计地使用了责任链模式。

责任链模式,其实是比较适用于执行逻辑有先后顺序的场景,然后这里我所贴出来的代码其实有些方法是可以并行执行的,如果使用了责任链模式反而处理时间更长了些。

所以这里的代码 更好的做法应该是通过一个事件抛出 或者 是mq发送消息,去通知到各个业务服务进行相关操作。例如下边的代码所示:

/**
* 会员升级处理
*
* @param currentLevel
* @param role
* @param userId
* @param price
* @param currentPrice
*/
public void upgradeV3(Integer currentLevel, int role, long userId, int price, int currentPrice) {
//包装一些参数...
mqTemplate.send(upgradeMsg);
}

「责任链的扩展思考」

「一链到底」

其实责任链的玩法有许多种,第一种 也是最经典的一种,从链路的开始执行到末尾。

这种链路我们的代码实现可以如下所示:

首先我们统一定义一个链路层的接口:

public interface BizFilterNode { 
//这个BizFilterContext是参数传递时候使用的上下文对象
void handle(BizFilterContext bizFilterContext);
}

然后就是如何遍历这批BizFilterNode对象

public void filterHandle(BizFilterContext bizFilterContext) { 
for (BizFilterNode bizFilterNode : bizFilterNodes) {
bizFilterNode.handle(bizFilterContext);
}
}

直接一个for循环,然后对各层的链路节点遍历,执行相关的invoke方法。

「中途暂停」

中途暂停的意思是,链路执行到了一半,某一环节有问题,则整个脸路暂停了。例如下图所示:

这个时候,我们的代码可以这么来设计:

public interface BizFilterNode { 
void handle(BizFilterContext bizFilterContext);
//增加一个是否终止执行后边逻辑
boolean terminateChain(BizFilterContext bizFilterContext);
}

然后还是在遍历的时候,进行判断处理

public void filterHandle(BizFilterContext bizFilterContext) {   
for (BizFilterNode bizFilterNode : bizFilterNodes) {
//全链路终止
if (bizFilterNode.terminateChain(bizFilterContext)) {
return;
}
bizFilterNode.handle(bizFilterContext);
}
}

「跳过当前环节」

所谓的跳过当前环节,其实意思是指当前的handler不想执行,直接跳到下一层的handler里面执行。

这个时候,我们只需要再在原有的代码里做写扩展就可以了:

public interface BizFilterNode {
void handle(BizFilterContext bizFilterContext);

boolean terminateChain(BizFilterContext bizFilterContext);

//是否跳过当前节点
boolean skipNode(BizFilterContext bizFilterContext);

}

遍历节点的时候,进行逻辑判断处理

public void filterHandle(BizFilterContext bizFilterContext) {  
for (BizFilterNode bizFilterNode : bizFilterNodes) {
if (bizFilterNode.terminateChain(bizFilterContext)) {
return;
}
//判断是否要跳过当前环节
if (bizFilterNode.skipNode(bizFilterContext)) {
continue;
}
bizFilterNode.handle(bizFilterContext);
}
}

「责任链模式在开源组件中的使用」

责任链在各个组件中应⽤很多,基本都⼤同⼩异。但是 Netty 的责任链相对来说有趣⼀些,我们⽤它来举

例简单介绍在netty中是如何使⽤责任链。 ChannelHandler 是 Netty 提供的默认处理者。核⼼的两个实现是 ChannelInboundHandler 和 ChannelOutboundHandler 。

  • ChannelInboundHandler:⼊站处理器,处理读数据的逻辑。
  • ChannelOutBoundHandler:出站处理器,处理写数据的逻辑。

当数据传递到netty的时候,会有一个读事件触发,接着会通过ChannelHandler里面的一个责任链。只不过Netty的责任链采用的是双向链路。这里的双向是指业务设计,并不是数据结构采用了双向链表。

ChannelInboundHandler里面有多个inbound节点,处理了不同的业务逻辑,而ChannelOutBoundHandler里面则有许多不同的outbound节点,也处理了不同的业务逻辑。

「小结」

责任链模式只是众多设计模式中的一种,合理的使用设计模式不一定会提升代码的性能,但是对于代码的维护性会有一定的帮助。在深入了解了它们的设计思想之后,希望可以对你平时的工作有所帮助。

文章目录