设计模式第七章(责任链模式)
前言
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,其核心思想是:将多个请求处理者(对象)连接成一条 "链",当一个请求产生时,会沿着这条链依次传递,直到链中的某个处理者能够处理该请求为止。
核心目的
避免请求的发送者与接收者之间形成直接耦合 ------ 发送者不需要知道具体哪个对象会处理请求,只需将请求传递给链的起始端,由链自行决定谁来处理。
关键角色
-
抽象处理者(Handler)
定义一个处理请求的接口(通常包含一个处理方法),并持有一个 "下一个处理者" 的引用(即链中的下一个节点)。
-
具体处理者(ConcreteHandler)
实现抽象处理者的接口,负责判断自己是否能处理当前请求:
- 如果能处理,则直接处理;
- 如果不能处理,则将请求传递给 "下一个处理者"。
-
客户端(Client)
创建具体处理者对象,按顺序组装成责任链,并发起请求(将请求传递给链的第一个处理者)。
适用场景
- 多个对象都可能处理同一请求,但具体由谁处理需要动态决定(如权限校验、日志记录、异常处理等);
- 希望避免请求发送者与处理者的直接关联(解耦);
- 需要动态调整处理者的顺序或增减处理者(如扩展功能时)。
优点
- 解耦:请求发送者无需知道谁会处理请求,处理者也无需知道请求的全貌;
- 灵活:可动态调整链中处理者的顺序或增减处理者(符合 "开闭原则");
- 简化对象:每个处理者只需关注自己能处理的请求,职责单一。
缺点
- 效率风险:如果链过长,请求可能需要经过多个处理者才能被处理,影响性能;
- 无人处理:如果链中所有处理者都无法处理请求,可能导致请求 "丢失"(需提前设计兜底逻辑)。
Java 中的常见应用
- Servlet 中的 Filter 链:多个 Filter 按顺序处理请求,每个 Filter 可选择处理或传递给下一个;
- Spring 中的拦截器(Interceptor):请求经过多个拦截器组成的链,依次执行预处理、后处理等操作。
通过责任链模式,代码会更具灵活性和可扩展性,尤其适合处理流程化、多步骤的业务场景。
责任链基本使用
我们现在需要接受前端传过来的一个user对象,里面有name 和 age 属性,我们需要校验这两个属性的合法性,传统的写法就是那个用户对象,一个一个的去遍历进行校验,那么我们可以写一个注解,放到属性上面,然后调用校验方法
注解类
-
length
java@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Length { int value(); }
-
Max
java@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Max { int value(); }
-
Min
java@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Min { int value(); }
实体类 user
java
public class User {
@Length(4)
private String name;
@Max(17)
@Min(17)
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
异常类
java
public class ValidatorException extends RuntimeException{
public ValidatorException(String message) {
super(message);
}
}
校验器类
java
public class Validator {
public void validate(Object bean) throws Exception{
Class<?> aClass = bean.getClass();
for (Field field : aClass.getDeclaredFields()) {
field.setAccessible(true);
ValidateChain chain = buildValidateChain(field);
chain.validate(field.get(bean));
}
}
/**
* 组链的过程
* @param field
* @return
*/
private ValidateChain buildValidateChain(Field field) {
ValidateChain chain = new ValidateChain();
Max max = field.getAnnotation(Max.class);
if (max != null) {
chain.addLastValidaHandler(new MaxValidateHandler(max.value()));
}
Min min = field.getAnnotation(Min.class);
if (min != null) {
chain.addLastValidaHandler(new MinValidateHandler(min.value()));
}
Length length = field.getAnnotation(Length.class);
if (length != null) {
chain.addLastValidaHandler(new LengthValidateHandler(length.value()));
}
return chain;
}
}
ValidateChain 类
java
public class ValidateChain {
private final List<ValidateHandler> validateHandlers = new ArrayList<>();
public void validate(Object object) {
for (ValidateHandler handler : validateHandlers) {
handler.validate(object);
}
}
public void addLastValidaHandler(ValidateHandler handler) {
validateHandlers.add(handler);
}
}
校验类
java
public interface ValidateHandler {
void validate(Object object)throws ValidatorException;
}
public class LengthValidateHandler implements ValidateHandler{
private final Integer length;
public LengthValidateHandler(Integer length) {
this.length = length;
}
@Override
public void validate(Object object) throws ValidatorException {
if (object instanceof String strValue) {
if (strValue.length() != length) {
throw new ValidatorException("当前值长度:"+(strValue.length()) + ",期望的值:"+ length);
}
}
}
}
public class MaxValidateHandler implements ValidateHandler{
private final Integer max;
public MaxValidateHandler(Integer max) {
this.max = max;
}
@Override
public void validate(Object object) throws ValidatorException {
if (object instanceof Integer intValue) {
if (intValue > max) {
throw new ValidatorException("当前值过大:"+intValue + ",期望的值:"+max);
}
}
public class MinValidateHandler implements ValidateHandler{
private final Integer min;
public MinValidateHandler(Integer min) {
this.min = min;
}
@Override
public void validate(Object object) throws ValidatorException {
if (object instanceof Integer intValue) {
if (intValue < min) {
throw new ValidatorException("当前值过小:"+intValue + ",期望的值:"+min);
}
}
}
}
测试类
java
public class Main {
public static void main(String[] args) throws Exception {
User user = new User("zhangsan", 18);
Validator validator = new Validator();
validator.validate(user);
//当前需求是,我需要把错误的信息都返回,而不是直接中断了
}
}

遗留问题点
-
有的属性上面是有多个校验的,但是只能显示一个错误信息,我需要把所有的错误都显示出来,传统解决方式,在验证里面加上 try cath 。
javapublic void validate(Object object) { List<String> errList = new ArrayList<>(); for (ValidateHandler handler : validateHandlers) { try { handler.validate(object); } catch (ValidatorException e) { errList.add(e.getMessage()); } } if (!errList.isEmpty()) { throw new ValidatorException(errList.toString()); } }
责任链升级
我们刚刚看到,校验我们采用的是抛异常的方式,那么我们就没有办法将一个属性的错误全部暴露出来,这个时候,我们需要有一个上下文的支持,用来存储上下文异常信息;
上下文类
java
public class ValidatorContext {
private final List<String> errMsgList = new ArrayList<>();
private boolean stop = false;
private int index = 0;
private Object value;
public ValidatorContext(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public void appendError(String error) {
errMsgList.add(error);
}
public void stopChain() {
this.stop = true;
}
public boolean shouldStop() {
return this.stop;
}
// 责任了继续往下走
public void doNext(Object value) {
index++;
this.value = value;
}
public int currentIndex() {
return index;
}
// 抛出错误异常
public void throwExceptionIfNotSuccess() {
if (!errMsgList.isEmpty()) {
throw new ValidatorException(errMsgList.toString());
}
}
}

支持next才调用下一个校验
该方式类似于 servelt 中的 dofilter,每次我们使用完了后,需要继续调用一下才进行下一步,否则就不进行校验;
java
public class ValidateChain {
private final List<ValidateHandler> validateHandlers = new ArrayList<>();
// 校验上下文
public void validate(Object object) {
ValidatorContext context = new ValidatorContext(object);
while (true) {
int index = context.currentIndex();
if (index == validateHandlers.size()) {
break;
}
ValidateHandler handler = validateHandlers.get(index);
handler.validate(context.getValue(),context);
if (index == context.currentIndex()) {
// 说明没有调用 doNext函数
break;
}
}
// for (ValidateHandler handler : validateHandlers) {
// handler.validate(object,context);
// if (context.shouldStop()) {
// break;
// }
// }
context.throwExceptionIfNotSuccess();
}
public void addLastValidaHandler(ValidateHandler handler) {
validateHandlers.add(handler);
}
}
测试
我们修改MaxValidateHandler,然后在校验里面关闭 doNext值
@Override
public void validate(Object object, ValidatorContext context) throws ValidatorException {
if (object instanceof Integer intValue) {
if (intValue > max) {
context.appendError("当前值过大:"+intValue + ",期望的值:"+max);
}
}
//context.doNext(object);
}

支持修改值的上下文
我们在任意步骤都可以对值进来修改,从而达到我们的目的;
public void validate(Object object) {
ValidatorContext context = new ValidatorContext(object);
while (true) {
int index = context.currentIndex();
if (index == validateHandlers.size()) {
break;
}
ValidateHandler handler = validateHandlers.get(index);
handler.validate(context.getValue(),context);
if (index == context.currentIndex()) {
// 说明没有调用 doNext函数
break;
}
}
测试
java
@Override
public void validate(Object object, ValidatorContext context) throws ValidatorException {
if (object instanceof Integer intValue) {
if (intValue > max) {
context.appendError("当前值过大:"+intValue + ",期望的值:"+max);
}
}
context.doNext(20);
}

拓展
其实我们可以在上下文里面,加入一些其他的标识,用一个map存储起来,当传入到任意的过滤器中的时候,我们都可以进行获取,从而做一些额外的操作
servlet 中的 Filter 应用
java
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException;

实际使用
我们可以在filte中增加日志的追溯id
java
public class HttpFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(HttpFilter.class);
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse httpServletResponse = (HttpServletResponse) res;
//跨域的header设置
httpServletResponse.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", request.getMethod());
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
try{
if (StaticSourceFilter.isStaticSource(request.getRequestURI())) {
chain.doFilter(req, res);
return;
}
ServletRequest requestWrapper = null;
if (req instanceof HttpServletRequest) {
//requestWrapper = new RequestWrapperFilter((HttpServletRequest) req);
}
String traceId = LogUtil.getTraceId();
ipLog(request, traceId);
MDC.put("uuid", traceId);
if (requestWrapper == null) {
chain.doFilter(req, res);
} else {
chain.doFilter(requestWrapper, res);
}
}finally {
MDC.clear();
}
}
总结
我们看到最终我们需要调用 chain.doFilter(requestWrapper, res); 方法进行下一步的传递操作