007:基于责任链模式构建网关权限链系统
1 基于责任链模式课程内容介绍
课程内容
- 责任链设计模式基本概念
- 责任链模式的应用场景
- 基于工厂模式整合责任链
- 基于数据库配置整合责任链
2 责任链基本介绍
责任链基本概念
多个对象形成一个链,每个对象相互指向上一个和下一个对象。
责任链应用场景
- 过滤器
- 审批 eg:请假流程(项目组长->项目经理->人事经理->总经理)
- 工作流框架
- 风控系统 金融或者p2p 上失信名单->信用卡是否逾期->蚂蚁信用积分是否大于640
- 多重if判断问题 (微服务网关) ip地址是否限流->ip地址是否在黑名单->是否传递token
3 责任链类图结构分析
Handler 单个小的执行流程(每个if判断流程)
每个处理器对象都会指向下一个处理器对象形成一个链,这个链类似链表数据结构。
如果处理器对象指向下一个处理器对象为空的情况下,说明是尾部;如果处理器对象的上一个处理器对象为空,说明该对象为头部。
4 基于工厂模式实现责任链
基于工厂模式实现责任链
public abstract class GatewayHandler {
/**
* 每个不同的处理器 处理的业务逻辑
*/
public abstract void doService();
protected GatewayHandler nextGatewayHandler;
public GatewayHandler(GatewayHandler nextGatewayHandler) {
this.nextGatewayHandler = nextGatewayHandler;
}
}
@Slf4j
public class CurrentLimitHandler extends GatewayHandler {
public CurrentLimitHandler(GatewayHandler nextGatewayHandler) {
super(nextGatewayHandler);
}
@Override
public void doService() {
log.info(">>>第一关 ip限流处理业务逻辑判断");
// 指向下一个节点
nextGatewayHandler.doService();
}
}
@Slf4j
public class BlacklistHandler extends GatewayHandler {
public BlacklistHandler(GatewayHandler nextGatewayHandler) {
super(nextGatewayHandler);
}
@Override
public void doService() {
log.info(">>>第二关 ip黑名单逻辑判断");
nextGatewayHandler.doService();
}
}
@Slf4j
public class TokenHandler extends GatewayHandler {
public TokenHandler(GatewayHandler nextGatewayHandler) {
super(nextGatewayHandler);
}
@Override
public void doService() {
log.info(">>>第三关 验证token逻辑判断");
}
}
public class FactoryHandler {
public static CurrentLimitHandler currentLimitHandler() {
return new CurrentLimitHandler(new BlacklistHandler(new TokenHandler(null)));
}
public static void main(String[] args) {
CurrentLimitHandler currentLimitHandler = FactoryHandler.currentLimitHandler();
currentLimitHandler.doService();
}
}
运行结果:
5 基于Set方式实现责任链模式
public abstract class GatewayHandler {
/**
* 每个不同的处理器 处理的业务逻辑
*/
public abstract void doService();
protected GatewayHandler nextGatewayHandler;
public GatewayHandler(){}
public void setNextGatewayHandler(GatewayHandler nextGatewayHandler) {
this.nextGatewayHandler = nextGatewayHandler;
}
}
@Slf4j
public class CurrentLimitHandler extends GatewayHandler {
public CurrentLimitHandler() {
}
@Override
public void doService() {
log.info(">>>第一关 ip限流处理业务逻辑判断");
// 指向下一个节点
nextGatewayHandler.doService();
}
}
@Slf4j
public class BlacklistHandler extends GatewayHandler {
public BlacklistHandler() {
}
@Override
public void doService() {
log.info(">>>第二关 ip黑名单逻辑判断");
nextGatewayHandler.doService();
}
}
@Slf4j
@Component
public class TokenHandler extends GatewayHandler {
public TokenHandler() {
}
@Override
public void doService() {
log.info(">>>第三关 验证token逻辑判断");
}
}
public class FactoryHandler {
public static CurrentLimitHandler currentLimitHandler2() {
CurrentLimitHandler currentLimitHandler = new CurrentLimitHandler();
BlacklistHandler blacklistHandler = new BlacklistHandler();
currentLimitHandler.setNextGatewayHandler(blacklistHandler);
blacklistHandler.setNextGatewayHandler(new TokenHandler());
return currentLimitHandler;
}
public static void main(String[] args) {
CurrentLimitHandler currentLimitHandler2 = FactoryHandler.currentLimitHandler2();
currentLimitHandler2.doService();
}
}
运行结果同上图
6 基于数据库的方式实现责任链
数据库表
CREATE TABLE `gateway_handler` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`handler_name` varchar(32) DEFAULT NULL COMMENT 'handler名称',
`handler_id` varchar(32) DEFAULT NULL COMMENT 'handler主键id',
`prev_handler_id` varchar(32) DEFAULT NULL,
`next_handler_id` varchar(32) DEFAULT NULL COMMENT '下一个handler',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COMMENT='权限表';
INSERT INTO `test`.`gateway_handler`(`ID`, `handler_name`, `handler_id`, `prev_handler_id`, `next_handler_id`) VALUES (16, 'Api接口限流', 'currentLimitHandler', NULL, 'blacklistHandler');
INSERT INTO `test`.`gateway_handler`(`ID`, `handler_name`, `handler_id`, `prev_handler_id`, `next_handler_id`) VALUES (17, '黑名单拦截', 'blacklistHandler', 'currentLimitHandler', 'tokenHandler');
INSERT INTO `test`.`gateway_handler`(`ID`, `handler_name`, `handler_id`, `prev_handler_id`, `next_handler_id`) VALUES (18, 'token验证', 'tokenHandler', 'blacklistHandler', NULL);
代码类
@Data
@TableName("gateway_handler")
public class GatewayHandlerEntity implements Serializable, Cloneable {
/** 主键ID */
private Integer id;
/** handler名称 */
private String handlerName;
/** handler主键id */
private String handlerId;
/** 下一个handlerId */
private String nextHandlerId;
}
public interface GatewayHandlerMapper extends BaseMapper<GatewayHandlerEntity> {
}
@Slf4j
@Component
public class DbHandler {
@Autowired
private GatewayHandlerMapper gatewayHandlerMapper;
// 通过将headGatewayHandler作为属性参数用作缓存,不用每次查询数据库
private GatewayHandler headGatewayHandler;
public GatewayHandler getFirstGatewayHandler() {
// 如果调用关联关系发生变化,单独写一个接口手动将headGatewayHandler刷新为null
if (headGatewayHandler != null) {
return headGatewayHandler;
}
// 查询头节点 查询pre_handler_id为空的情况下,表名为头节点
QueryWrapper<GatewayHandlerEntity> firstQueryWrapper = new QueryWrapper<>();
firstQueryWrapper.isNull("prev_handler_id");
GatewayHandlerEntity gatewayHandlerEntity = gatewayHandlerMapper.selectOne(firstQueryWrapper);
if (gatewayHandlerEntity == null) {
log.error(">>>没有配置头节点");
return null;
}
// 2.获取头节点的handlerId
String handlerId = gatewayHandlerEntity.getHandlerId();
if (StringUtils.isEmpty(handlerId)) {
log.error(">>>没有配置头节点的handler_id");
return null;
}
// 3.从容器中获取头节点handler对象
GatewayHandler headGatewayHandler = SpringUtils.getBean(handlerId, GatewayHandler.class);
if (headGatewayHandler == null) {
log.error(">>>没有配置" + handlerId + "对象");
return null;
}
// 4.获取下一个节点id
String nextHandlerId = gatewayHandlerEntity.getNextHandlerId();
// 记录当前循环变量
GatewayHandler tempGatewayHandler = headGatewayHandler;
while (StringUtils.isNotEmpty(nextHandlerId)) {
// 从Spring容器中获取下一个节点
GatewayHandler nextGatewayHandler = SpringUtils.getBean(nextHandlerId, GatewayHandler.class);
if (nextGatewayHandler == null) {
break;
}
QueryWrapper<GatewayHandlerEntity> nextQueryWrapper = new QueryWrapper<>();
nextQueryWrapper.eq("handler_id", nextHandlerId);
GatewayHandlerEntity nextGatewayEntity = gatewayHandlerMapper.selectOne(nextQueryWrapper);
if (nextGatewayEntity == null) {
break;
}
nextHandlerId = nextGatewayEntity.getNextHandlerId();
tempGatewayHandler.setNextGatewayHandler(nextGatewayHandler);
tempGatewayHandler = nextGatewayHandler;
}
this.headGatewayHandler = headGatewayHandler;
return headGatewayHandler;
}
}
@RestController
public class HandlerController {
@Autowired
private DbHandler bbHandler;
@GetMapping("/getHandler")
public void getHandler() {
bbHandler.getFirstGatewayHandler().doService();
}
}
运行结果:
责任链设计模式优缺点
优点:实现代码解耦,动态拼接每个链的关系,不需要改动代码直接通过数据库表进行维护;
缺点:粒度拆分过于精细,后期不断产生非常多类
源码下载地址(mayikt_designPattern_7.zip):
链接:https://pan.baidu.com/s/1wWKZN1MbXICZVW1Vxtwe6A
提取码:fire