一、前言
相信大家平时或多或少都间接接触过责任链设计模式,只是可能有些同学自己不知道此处用的是该设计模式,比如说 Java Web 中的 Filter 过滤器,就是非常经典的责任链设计模式的例子。
那么什么是责任链设计模式呢?
客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
技术领域的相关定义总是那样的晦涩难懂,因此不理解不重要,下面将会用例子来帮助理解。
责任链模式有哪些优点,能解决什么问题,我们为什么要使用它呢?
优点:
- 将请求与处理解耦。
- 请求处理对象只需关注自己需要处理的请求进行处理即可,对于不需要自己处理的请求,直接转发给下一个处理对象即可,符合单一职责原则。
- 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果。
- 链路结构灵活,可以通过改变链路结构动态的新增或者删除处理对象。即易于拓展新的请求处理类,符合开闭原则。
缺点:
- 会存在责任链太长,而大多数处理者都不会对请求进行处理的情况,导致走完整个责任链的时间太长,影响整体性能。
- 如果责任链配置不完善,会存在处理对象循环引用,从而造成死循环,导致系统崩溃的情况。
适用场景:
- 多条件流程判断,如权限控制
- ERP 系统流程审批
- Java Web 过滤器的底层实现 Filter
- Mybatis 中的分页插件 PageHelper
二、代码示例
1. 导包信息
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
2. 代码结构
3. 具体代码
流程扩展类(主要记录每个流程的上一个处理对象和下一个处理对象的信息) ProcessDTO.java:
java
package com.dxc.responsibility.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 流程扩展类
*
* @Author xincheng.du
* @Date 2023/8/30 14:26
*/
@Data
@AllArgsConstructor
public class ProcessDTO {
/**
* 流程处理器id
*/
private Integer handlerId;
/**
* 全限定名
*/
private String fullName;
/**
* 上一个流程处理器id
*/
private Integer preHandlerId;
/**
* 下一个流程处理器id
*/
private Integer nextHandlerId;
}
流程链枚举 ProcessChainEnum.java
java
package com.dxc.responsibility.enums;
import com.dxc.responsibility.dto.ProcessDTO;
import lombok.AllArgsConstructor;
/**
* 流程链枚举
* 此处的枚举类可以换成数据库配置,当然你也可以理解为数据库表中的一条条数据
* 这样就可以根据更改枚举类或数据库中的顺序,来更改实际处理流程中的执行顺序了
*
* @Author xincheng.du
* @Date 2023/8/30 14:26
*/
@AllArgsConstructor
public enum ProcessChainEnum {
/**
* 流程链中的第一个流程
*/
FIRST(new ProcessDTO(1, "com.dxc.responsibility.handler.impl.FirstProcessHandler", null, 2)),
/**
* 流程链中的第二个流程
*/
SECOND(new ProcessDTO(2, "com.dxc.responsibility.handler.impl.SecondProcessHandler", 1, 3)),
/**
* 流程链中的第三个流程
*/
THIRD(new ProcessDTO(3, "com.dxc.responsibility.handler.impl.ThirdProcessHandler", 2, null)),
;
ProcessDTO processDTO;
public ProcessDTO getProcessDTO() {
return processDTO;
}
}
流程处理器工厂(主要用来初始化流程链,并返回第一个流程处理器) ProcessHandlerFactory.java
java
package com.dxc.responsibility.factory;
import cn.hutool.core.util.ReflectUtil;
import com.dxc.responsibility.dto.ProcessDTO;
import com.dxc.responsibility.handler.AbstractProcessHandler;
import com.dxc.responsibility.handler.impl.FirstProcessHandler;
import com.dxc.responsibility.service.ProcessService;
import com.dxc.responsibility.service.impl.ProcessServiceImpl;
import lombok.extern.slf4j.Slf4j;
/**
* 流程处理器工厂
*
* @Author xincheng.du
* @Date 2023/8/30 14:26
*/
@Slf4j
public class ProcessHandlerFactory {
private ProcessHandlerFactory() {}
private static final ProcessService processService = new ProcessServiceImpl();
/**
* 初始化流程链,并返回流程链中第一个流程处理器
*
* @return {@link FirstProcessHandler}
*/
public static FirstProcessHandler getFirstProcessHandler() {
// 获取第一个流程扩展类
ProcessDTO firstProcessDTO = processService.getFirstProcessDTO();
// 根据流程扩展类获取第一个流程处理器
AbstractProcessHandler firstProcessHandler = newProcessHandler(firstProcessDTO);
ProcessDTO tempProcessDTO = firstProcessDTO;
Integer nextHandlerId;
AbstractProcessHandler tempProcessHandler = firstProcessHandler;
// 迭代遍历所有handler,以及将它们链接起来
while ((nextHandlerId = tempProcessDTO.getNextHandlerId()) != null) {
// 根据处理器id获取流程扩展类
ProcessDTO processDTO = processService.getProcessEntity(nextHandlerId);
// 根据流程扩展类获取具体的流程处理器
AbstractProcessHandler processHandler = newProcessHandler(processDTO);
assert tempProcessHandler != null;
tempProcessHandler.setNext(processHandler);
tempProcessHandler = processHandler;
tempProcessDTO = processDTO;
}
// 返回第一个handler
return (FirstProcessHandler) firstProcessHandler;
}
/**
* 根据流程扩展类获取具体的流程处理器
*
* @param dto 流程扩展类
* @return {@link AbstractProcessHandler}
*/
private static AbstractProcessHandler newProcessHandler(ProcessDTO dto) {
// 获取全限定类名
String className = dto.getFullName();
try {
// 根据全限定类名,加载并初始化该类
Class<?> clazz = Class.forName(className);
return (AbstractProcessHandler) ReflectUtil.newInstance(clazz);
} catch (ClassNotFoundException e) {
log.error("根据流程扩展类获取流程处理器失败,原因:{},流程处理器:{}", e.getMessage(), dto.getFullName());
e.printStackTrace();
}
return null;
}
}
抽象流程处理器(主要是用来定义每个具体处理的方法模板,不做具体逻辑处理,具体的流程处理器需要集成当前抽象类,并实现各自的流程处理逻辑) AbstractProcessHandler.java
java
package com.dxc.responsibility.handler;
/**
* 流程处理抽象类
*
* @Author xincheng.du
* @Date 2023/8/31 11:25
*/
public abstract class AbstractProcessHandler {
/**
* 下一关用当前抽象类来接收
*/
protected AbstractProcessHandler next;
public void setNext(AbstractProcessHandler next) {
this.next = next;
}
/**
* 具体处理逻辑
* 需要子类实现
*/
public abstract void process();
}
第一个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) FirstProcessHandler.java
java
package com.dxc.responsibility.handler.impl;
import com.dxc.responsibility.handler.AbstractProcessHandler;
import lombok.extern.slf4j.Slf4j;
/**
* 第一个流程处理器
*
* @Author xincheng.du
* @Date 2023/8/30 11:25
*/
@Slf4j
public class FirstProcessHandler extends AbstractProcessHandler {
@Override
public void process() {
log.info("第一个流程处理开始对请求进行处理......");
if (this.next != null) {
this.next.process();
}
}
}
第二个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) SecondProcessHandler.java
java
package com.dxc.responsibility.handler.impl;
import com.dxc.responsibility.handler.AbstractProcessHandler;
import lombok.extern.slf4j.Slf4j;
/**
* 第二个流程处理器
*
* @Author xincheng.du
* @Date 2023/8/30 11:25
*/
@Slf4j
public class SecondProcessHandler extends AbstractProcessHandler {
@Override
public void process() {
log.info("第二个流程处理开始对请求进行处理......");
if (this.next != null) {
this.next.process();
}
}
}
第三个流程处理器(此处模拟具体的流程处理对象,具体的处理逻辑写在此处) SecondProcessHandler.java
java
package com.dxc.responsibility.handler.impl;
import com.dxc.responsibility.handler.AbstractProcessHandler;
import lombok.extern.slf4j.Slf4j;
/**
* 第三个流程处理器
*
* @Author xincheng.du
* @Date 2023/8/30 11:25
*/
@Slf4j
public class ThirdProcessHandler extends AbstractProcessHandler {
@Override
public void process() {
log.info("第三个流程处理开始对请求进行处理......");
if (this.next != null) {
this.next.process();
}
}
}
流程扩展类接口(主要对流程扩展类进行封装初始化,为流程处理器工厂提供方法) ProcessService.java
java
package com.dxc.responsibility.service;
import com.dxc.responsibility.dto.ProcessDTO;
/**
* 流程扩展类 接口
*
* @Author xincheng.du
* @Date 2023/8/30 14:26
*/
public interface ProcessService {
/**
* 根据流程处理器id获取流程扩展类
*
* @param handlerId 流程处理器id
* @return {@link ProcessDTO}
*/
ProcessDTO getProcessEntity(Integer handlerId);
/**
* 获取第一个流程扩展类
*
* @return {@link ProcessDTO}
*/
ProcessDTO getFirstProcessDTO();
}
流程扩展类接口的具体实现 ProcessServiceImpl.java
java
package com.dxc.responsibility.service.impl;
import com.dxc.responsibility.dto.ProcessDTO;
import com.dxc.responsibility.enums.ProcessChainEnum;
import com.dxc.responsibility.service.ProcessService;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
/**
* 流程扩展类 逻辑处理
*
* @Author xincheng.du
* @Date 2023/8/30 14:26
*/
@Slf4j
public class ProcessServiceImpl implements ProcessService {
/**
* 流程扩展类Map
* key=流程处理器id value=流程扩展类 ProcessDTO
*/
private static Map<Integer, ProcessDTO> processDTOMap = new HashMap<>();
/**
* 流程链初始化
* 将枚举中配置的handler初始化到map中,方便获取
*/
static {
ProcessChainEnum[] values = ProcessChainEnum.values();
for (ProcessChainEnum value : values) {
ProcessDTO processDTO = value.getProcessDTO();
processDTOMap.put(processDTO.getHandlerId(), processDTO);
}
}
@Override
public ProcessDTO getProcessEntity(Integer handlerId) {
return processDTOMap.get(handlerId);
}
@Override
public ProcessDTO getFirstProcessDTO() {
for (Map.Entry<Integer, ProcessDTO> entry : processDTOMap.entrySet()) {
ProcessDTO value = entry.getValue();
// 没有上一个handler的就是第一个
if (value.getPreHandlerId() == null) {
return value;
}
}
log.error("获取第一个流程扩展类出错");
return null;
}
}
客户端(相当于请求发起者) ProcessClient.java
java
package com.dxc.responsibility;
import com.dxc.responsibility.factory.ProcessHandlerFactory;
import com.dxc.responsibility.handler.AbstractProcessHandler;
/**
* 客户端
*
* @Author xincheng.du
* @Date 2023/8/30 14:26
*/
public class ProcessClient {
public static void main(String[] args) {
// 获取第一个流程处理器
AbstractProcessHandler firstProcessHandler = ProcessHandlerFactory.getFirstProcessHandler();
assert firstProcessHandler != null;
// 执行第一个流程处理器
firstProcessHandler.process();
}
}