一、前言
1.1 背景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
- 不明确指定接收者的情况下,向多个对象中的一个提交一个请求
- 可动态指定一组对象处理请求
1.2 简介
职责链模式是一种行为型设计模式,它通过将请求的发送者和接收者解耦来实现请求的处理。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
优点:
- 降低系统的耦合度
- 提高代码的可扩展性和可维护性
- 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果
- 链路结构灵活,可以通过改变链路结构动态地新增或删减责任
- 易于扩展新的请求处理类(节点),符合开闭原则
缺点:
- 责任链太长或者处理时间过长,会影响整体性能
- 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃
组成部分:
- 抽象处理者(Handler):定义一个处理请求的接口,包含抽象处理方法和一个后继连接
- 具体处理者(Concrete Handler):实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者
- 客户类(Client):创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程
二、案例代码
java
package com.qiangesoft.design.behavioral.responsibilitychain;
import java.util.HashMap;
import java.util.Map;
/**
* 客户类
*
* ps:责任链模式
*/
public class Chain {
public static void main(String[] args) {
// 模拟请求
Request request = new Request();
// request.setPath("/api/news");
request.setPath("/user/1");
Map<String, String> header = new HashMap<>();
header.put("token", "111111");
request.setHeader(header);
// 组装责任链
Handler handler = getHandlerChain();
// 提交请求
handler.doHandler(request);
System.out.println("***执行了接口方法***");
}
/**
* 组装责任链
*
* @return
*/
private static Handler getHandlerChain() {
Handler tokenHandler = new TokenHandler();
Handler authHandler = new AuthHandler();
Handler logHandler = new LogHandler();
tokenHandler.setNext(authHandler)
.setNext(logHandler);
return tokenHandler;
}
}
/**
* 抽象处理者
*/
abstract class Handler {
protected Handler next;
public Handler setNext(Handler next) {
this.next = next;
return next;
}
protected abstract void doHandler(Request request);
}
/**
* 具体处理者:token校验
*/
class TokenHandler extends Handler {
@Override
public void doHandler(Request request) {
// 有些接口无需登录
String path = request.getPath();
if (path.startsWith("/api")) {
return;
}
try {
// token校验(模拟)
String token = request.getHeader().get("token");
if (token == null || "".equals(token)) {
System.out.println("用户未登录!");
return;
}
if (!"111111".equals(token)) {
System.out.println("登录已失效!");
return;
}
String username = "admin";
AuthenticationContextHolder.set(username);
System.out.println("token校验成功!");
if (next != null) {
next.doHandler(request);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
AuthenticationContextHolder.remove();
}
}
}
/**
* 具体处理者:权限校验
*/
class AuthHandler extends Handler {
@Override
public void doHandler(Request request) {
String username = AuthenticationContextHolder.get();
if (!"admin".equals(username)) {
System.out.println("当前用户无权限!");
return;
}
System.out.println("权限校验成功!");
if (next != null) {
next.doHandler(request);
}
}
}
/**
* 具体处理者:日志记录
*/
class LogHandler extends Handler {
@Override
public void doHandler(Request request) {
String username = AuthenticationContextHolder.get();
System.out.println("记录日志:请求【xxx接口】 操作人【" + username + "】!");
if (next != null) {
next.doHandler(request);
}
}
}
/**
* 模拟request对象
*/
class Request {
private String path;
private Map<String, String> header;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Map<String, String> getHeader() {
return header;
}
public void setHeader(Map<String, String> header) {
this.header = header;
}
}
/**
* 线程本地变量:用户信息
*/
class AuthenticationContextHolder {
private static final ThreadLocal<String> CURRENT_USER = new ThreadLocal<>();
public static String get() {
return CURRENT_USER.get();
}
public static void set(String user) {
CURRENT_USER.set(user);
}
public static void remove() {
CURRENT_USER.remove();
}
}
三、总结
应用场景:
- 过滤器(Filter):在Servlet中,过滤器就是使用责任链模式实现的。每个过滤器都可以决定是否处理请求,或者将其转发给下一个过滤器进行处理。
- 拦截器(Interceptor):在Spring框架中,拦截器就是使用责任链模式实现的。拦截器可以对请求进行预处理或后处理,也可以将请求转发给下一个拦截器进行处理。
- 异常处理(Exception Handling):在Java中,可以使用责任链模式来处理异常。首先,程序先尝试使用自定义的异常处理器来处理异常,如果该处理器无法处理异常,则将其转发给下一个处理器进行处理。