1. 责任链模式—是什么
专业版
- 含义:客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。
- 内在含义:
- 使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。
- 将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
- 其过程实际上是一个递归调用。
- 它属于一种行为模式。
生活版
例如:《红楼梦》中的击鼓传花
击鼓串花:是一种热闹而又紧张的饮酒游戏。
**游戏规则:**在酒宴上宾客一次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。开始击鼓时,花束就开始依次传递,鼓声遗落,如果花束在某人手中,则该人就得饮酒。
比如说,贾母、贾赦、贾政、贾宝玉和贾环是五个参加击鼓传花游戏的传花者,他们组成一个环链。击鼓者将话传给贾母,开始传花游戏。花由贾母传给贾赦,由贾赦传给贾政,由贾政传给贾宝玉,由贾宝玉传给贾环,由贾环传回给贾母,如此往复,如下图所示。当鼓声停止时,手中有花的人就得执行酒令。
如图所示:

例如:打游戏《植物大战僵尸》闯关
每一个关独有一个老大,如果打不过在原关重来,打过了就下一关,每打过一关就会解锁下一关,这种就相当于链表一样,你的责任就是负责闯关,不管你闯到第几关,给我打个老大回来就好,打不过告诉你失败再那关,重新来,直到打败老大
2. 责任链模式—作用
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
3. 责任链模式—核心使用规则
抽象处理者(Handler)角色
:
- 定义出一个处理请求的接口。
- 如果需要,接口可以定义出一个方法以设定和返回对下一个对象的引用。
- 这个角色通常由一个 Java 抽象类或者 Java 接口实现。
具体处理者(ConcreteHandler)角色
:
- 具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下一个对象。
- 由于具体处理者持有对下一个对象的引用。
4. 责任链模式—应用场景
多条件流程判断:权限控制
ERP 系统中流程审批业务:总经理、人事经理、项目经理
Java 过滤器的底层实现 Filter :在 Java 过滤器中客户端发送请求到服务器端,过滤会经过参数过滤、session 过滤、表单过滤、隐藏过滤、检测请求头过滤。
5. 责任链模式—案例 1:模拟网关权限控制
功能要求:在网关作为微服务程序的入口,拦截客户端所有的请求实现权限控制 。
- 第一关:API 接口限流
- 第二关:黑名单拦截
- 第三关:用户会话信息拦截
UML 结构图如下所示:

1. 抽象处理者(Handler)
- 作用:负责处理请求
- 步骤:
- 创建一个抽象处理者
Handler
- 定义出一个方法()以设定下一个对象
setNextHandler()
- 定义一个返回对下一个对象的引用
getNextHandler()
- 创建一个抽象处理者
java
package com.gateway.handler.abstracts;
/**
* Created by Calvin on 2019/5/9
* 定义一个抽象的处理器
*/
public abstract class Handler {
/**
* 下一个处理任务对象
*/
protected Handler nextHandler;
/**
* 共同的处理行为(强制必须实现)
* 具体功能
*/
public abstract void function();
/**
* 设置下一个处理任务
* @param nextHandler 下一个处理对象
*/
public void setNextHandler(Handler nextHandler){
this.nextHandler = nextHandler;
}
/**
* 获取下一个处理任务
*/
protected void getNextHandler(){
if(nextHandler != null) {
// 指向下一个处理器功能
nextHandler.function();
}
}
}
2. 具体处理者(ConcreteHandler)
- 具体处理者:
ApiCurrentLimitHandler
API 接口限流BlackListHandler
黑名单拦截ConversationHandler
用户会话信息拦截
- 功效:
- 具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下一个对象。
- 由于具体处理者持有对下一个对象的引用
getNextHandler()
。
java
package com.gateway.handler;
import com.gateway.handler.abstracts.Handler;
import org.springframework.stereotype.Component;
/**
* Created by Calvin on 2019/5/9
* API接口限流处理器
*/
@Component
public class ApiCurrentLimitHandler extends Handler {
@Override
public void function() {
System.out.println(">>>>>>>>>>> 第一关:API接口限流......");
// 第一关执行完毕执行第二关
getNextHandler();
}
}
java
package com.gateway.handler;
import com.gateway.handler.abstracts.Handler;
import org.springframework.stereotype.Component;
/**
* Created by Calvin on 2019/5/9
* 黑名单拦截处理器
*/
@Component
public class BlackListHandler extends Handler {
@Override
public void function() {
System.out.println(">>>>>>>>>>> 第二关:黑名单拦截......");
// 第二关执行第三关任务
getNextHandler();
}
}
java
package com.gateway.handler;
import com.gateway.handler.abstracts.Handler;
import org.springframework.stereotype.Component;
/**
* Created by Calvin on 2019/5/9
*/
@Component
public class ConversationHandler extends Handler {
@Override
public void function() {
System.out.println(">>>>>>>>>>>第三关:用户会话信息拦截......");
}
}
3. 在网关实现类中,通过数据库获取处理器 BeanID,再通过处理器 BeanId 从上下文中实例处理器,并且递归设置处理器中的下一个处理器;最终,执行处理器中的所有功能。
3.1 执行相应的 SQL 语句
- 实现方式:数据库表中设置链表结构,在处理器中添加上一个处理器和下一个处理器。
mysql
CREATE TABLE `gateway_handler` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`handler_name` varchar(32) DEFAULT NULL COMMENT '处理器名称',
`handler_bean_id` varchar(32) DEFAULT NULL COMMENT '处理器BeanID',
`pre_handler_bean_id` varchar(32) DEFAULT NULL COMMENT '上一个处理器BeanID',
`next_handler_bean_id` varchar(32) DEFAULT NULL COMMENT '下一个处理器BeanID',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COMMENT='权限表';
-- ----------------------------
-- Records of gateway_handler
-- ----------------------------
INSERT INTO `gateway_handler` VALUES ('1', 'Api接口限流', 'apiCurrentLimitHandler', null, 'blackListHandler');
INSERT INTO `gateway_handler` VALUES ('2', '黑名单拦截', 'blackListHandler', 'apiCurrentLimitHandler', 'conversationHandler');
INSERT INTO `gateway_handler` VALUES ('3', '会话验证', 'conversationHandler', 'blackListHandler', null);
3.2 创建数据访问层,查询处理器
getFirstHandler()
查询第一个处理器。getByHandler()
根据处理器 BeanId, 查询处理器。
java
package com.gateway.mapper;
import com.gateway.entity.GatewayHandlerEntity;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* Created by Calvin on 2019/5/9
*/
public interface HandlerMapper {
/**
* 查询第一个处理器
*
* @return 网管处理器实体类
*/
@Select("SELECT " +
"id AS id," +
"handler_name AS handlerName," +
"handler_bean_id AS handlerBeanId ," +
"pre_handler_bean_id AS preHandlerBeanId ," +
"next_handler_bean_id AS nextHandlerBeanId " +
"FROM gateway_handler " +
"WHERE pre_handler_bean_id IS NULL;")
GatewayHandlerEntity getFirstHandler();
/**
* 根据处理器BeanId, 查询处理器
*
* @param handlerBeanId 处理器BeanId
* @return 网管处理器实体类
*/
@Select("SELECT " +
"id AS id," +
"handler_name AS handlerName," +
"handler_bean_id AS handlerBeanId ," +
"pre_handler_bean_id AS preHandlerBeanId ," +
"next_handler_bean_id AS nextHandlerBeanId " +
"FROM gateway_handler " +
"WHERE handler_bean_id=#{handlerBeanId}")
GatewayHandlerEntity getByHandler(@Param("handlerBeanId") String handlerBeanId);
}
3.3 创建网关实现类,通过数据库查询,递归设置下一个处理器。、
- 网关实现类
GatewayServiceImpl.java
java
package com.gateway.service.impl;
import com.gateway.entity.GatewayHandlerEntity;
import com.gateway.handler.abstracts.Handler;
import com.gateway.mapper.HandlerMapper;
import com.gateway.service.GatewayService;
import com.gateway.utils.ContextUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by Calvin on 2019/5/10
*/
@Service
public class GatewayServiceImpl implements GatewayService {
@Autowired
private HandlerMapper handlerMapper;
// 处理器第二次存在内存当中
private Handler handler;
@Override
public void function() {
// 如果处理器为空,执行第一次赋值后,第二次存在内存当中
if (handler == null) {
// 1.从数据库中查找地址第一个处理器信息
GatewayHandlerEntity firstHandlerEntity = handlerMapper.getFirstHandler();
// 2.获取第一个处理器
String handlerBeanId = firstHandlerEntity.getHandlerBeanId();
Handler firstHandler = ContextUtils.getBean(handlerBeanId, Handler.class);
// 3.记录当前循环handler对象
Handler tempHandler = firstHandler;
if(firstHandlerEntity != null){
// 4.获取下一个处理器beanId
String nextHandlerBeanId = firstHandlerEntity.getNextHandlerBeanId();
// 5. 递归
recursive(nextHandlerBeanId, tempHandler);
}
handler = tempHandler;
}
handler.function();
}
/**
* 递归:设置下一个处理器
*
* @param nextHandlerBeanId 下一个处理器BeanId
* @param tempHandler 临时处理器
*/
private void recursive(String nextHandlerBeanId, Handler tempHandler) {
// 处理器中的下一个处理器不为空。
if (!StringUtils.isEmpty(nextHandlerBeanId)) {
// 下一个处理器不为空,得到下一个处理器.
Handler nextHandler = ContextUtils.getBean(nextHandlerBeanId, Handler.class);
// 设置临时处理器中的下一个处理器
tempHandler.setNextHandler(nextHandler);
// 设置下一个nextHandlerId
GatewayHandlerEntity nextHandlerEntity = handlerMapper.getByHandler(nextHandlerBeanId);
if (null != nextHandlerEntity) {
// 赋值的作用让它继续判断是否有下一个
nextHandlerBeanId = nextHandlerEntity.getNextHandlerBeanId();
tempHandler = nextHandler;
// 递归
this.recursive(nextHandlerBeanId, tempHandler);
}
}
}
}
4. 最后,创建网关 API 接口进行访问。
GatewayService.java
: 网关接口
java
package com.gateway.service;
public interface GatewayService {
/**
* 网关功能
* 1. API 网关限流处理器
* 2. 黑名单拦截
* 3. 用户会话信息拦截
*/
void function();
}
GatewayController.java
:网关 API 接口
java
package com.gateway.controller;
import com.gateway.service.GatewayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "gateway")
public class GatewayController {
@Autowired
private GatewayService gatewayService;
@GetMapping("/api")
public String api() {
gatewayService.function();
return "success";
}
}
6. 结果演示

7. 责任链模式—优缺点
优点:
- 动态组合职责
- 说明:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。
- 请求者和接受者解耦
- 说明:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。
缺点:
- 产生很多细粒度的对象
- 说明:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
- 不能保证请求一定被接收
- 说明:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。