跳到主要内容

责任链模式

定义

职责链模式(chain of responsibility pattern):将请求的发送和接受解耦,避免将一个请求的发送者与接收者耦合在一起,让多个接受对象都有机会处理请求,将接受请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。

在职责链模式中,多个处理器(也就是上面提到的“接受对象”)依次处理同一个请求。一个请求先经过 A 处理器,然后再把请求传递给 B 处理器,B 处理器处理完再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫做职责链模式。

在 GoF 的定义中,一旦某个处理器能处理这个请求,就不会继续将请求传递给后续的处理器了。 当然,在实际的开发中,也存在对这个模式的变体,那就是请求不会中途终止,而是会被所有的处理器都处理一遍。

职责链模式有两种常用的实现,一种是使用链表来存储处理器,另一种是使用数组来存储处理器,后面一种实现方式更加简单。

模式原理

抽象的处理者类

/**
* 抽象处理者角色
*/
public abstract class Handler {
protected Handler successor = null;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public final void handle(){
boolean handled = doHandle();
if (successor!=null&&!handled){
successor.handle();
}
};
protected abstract boolean doHandle();
}


具体的处理者类A

public class HandlerA extends Handler {


@Override
protected boolean doHandle() {
System.out.println("HandlerA.doHandle");
return false;
}
}


具体的处理者类B
public class HandlerB extends Handler {

@Override
protected boolean doHandle() {
System.out.println("HandlerB.doHandle");
return false;
}
}

具体的处理者类C
public class HandlerC extends Handler {

@Override
protected boolean doHandle() {
System.out.println("HandlerC.doHandle");
return false;
}
}


HandleChain
package com.e6yun.project.behavior.chainofresponsibility.example5;

public class HandlerChain {
private Handler head= null;
private Handler tail= null;
public void addHandler(Handler handler){
handler.setSuccessor(null); // 链尾
if (head == null) { // 链头
head = handler;
tail = handler;
return;
}
// 其余情况,在原本的链尾添加元素
tail.setSuccessor(handler);
tail = handler; // 修改链尾指针
}
public void handle(){
if (head!=null){
head.handle();
}
}
}


客户端
public class Client {

public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.addHandler(new HandlerC());
chain.handle();
}
}

在 GoF 给出的定义中,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一个变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。

所有处理器都处理
public abstract class Handler {
protected Handler successor = null;

public void setSuccessor(Handler successor) {
this.successor = successor;
}

public final void handle() {
doHandle();
if (successor != null) {
successor.handle();
}
}

protected abstract void doHandle();
}

public class HandlerA extends Handler {
@Override
protected void doHandle() {
//...
}
}

public class HandlerB extends Handler {
@Override
protected void doHandle() {
//...
}
}

public class HandlerChain {
private Handler head = null;
private Handler tail = null;

public void addHandler(Handler handler) {
handler.setSuccessor(null);

if (head == null) {
head = handler;
tail = handler;
return;
}

tail.setSuccessor(handler);
tail = handler;
}

public void handle() {
if (head != null) {
head.handle();
}
}
}

// 使用举例
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.handle();
}
}

应用实例

使用职责链模式实现审批流,审批的过程会有不同级别的负责人加入进行审批(支持一级或者二级、三级审核人参与审核)。

不使用设计模式

不使用设计模式的审批流程
public class AuthService {

public void auth(String userId, String orderId) {
// 模拟从数据库获取用户信息
String userLevel = getUserLevel(userId);

if ("3".equals(userLevel)) {
System.out.println("3级审批负责人处理:" + orderId);
// 3级审批逻辑
if (Math.random() > 0.5) {
System.out.println("3级审批通过");
// 继续2级审批
if ("2".equals(getUserLevel("level2_user"))) {
System.out.println("2级审批负责人处理:" + orderId);
if (Math.random() > 0.5) {
System.out.println("2级审批通过");
// 继续1级审批
if ("1".equals(getUserLevel("level1_user"))) {
System.out.println("1级审批负责人处理:" + orderId);
if (Math.random() > 0.5) {
System.out.println("1级审批通过");
System.out.println("审批流程完成");
} else {
System.out.println("1级审批拒绝");
}
}
} else {
System.out.println("2级审批拒绝");
}
}
} else {
System.out.println("3级审批拒绝");
}
} else if ("2".equals(userLevel)) {
System.out.println("2级审批负责人处理:" + orderId);
if (Math.random() > 0.5) {
System.out.println("2级审批通过");
// 继续1级审批
if ("1".equals(getUserLevel("level1_user"))) {
System.out.println("1级审批负责人处理:" + orderId);
if (Math.random() > 0.5) {
System.out.println("1级审批通过");
System.out.println("审批流程完成");
} else {
System.out.println("1级审批拒绝");
}
}
} else {
System.out.println("2级审批拒绝");
}
} else if ("1".equals(userLevel)) {
System.out.println("1级审批负责人处理:" + orderId);
if (Math.random() > 0.5) {
System.out.println("1级审批通过");
System.out.println("审批流程完成");
} else {
System.out.println("1级审批拒绝");
}
}
}

private String getUserLevel(String userId) {
// 模拟从数据库获取用户级别
if ("level3_user".equals(userId)) return "3";
if ("level2_user".equals(userId)) return "2";
if ("level1_user".equals(userId)) return "1";
return "3"; // 默认3级
}
}

问题分析:

  • 代码中存在大量的 if-else 嵌套
  • 审批逻辑硬编码,难以扩展
  • 违反了开闭原则,添加新的审批级别需要修改现有代码
  • 代码可读性差,维护困难

职责链模式重构代码

审批请求对象
public class AuthInfo {
private String userId;
private String orderId;
private String level;

public AuthInfo(String userId, String orderId, String level) {
this.userId = userId;
this.orderId = orderId;
this.level = level;
}

// getter 和 setter 方法
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }

public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }

public String getLevel() { return level; }
public void setLevel(String level) { this.level = level; }
}
抽象审批链
public abstract class AuthLink {
protected String levelUserId; // 级别人员ID
protected String levelUserName; // 级别人员姓名
private AuthLink next; // 责任链

public AuthLink(String levelUserId, String levelUserName) {
this.levelUserId = levelUserId;
this.levelUserName = levelUserName;
}

public AuthLink next() {
return next;
}

public AuthLink appendNext(AuthLink next) {
this.next = next;
return this;
}

public abstract AuthInfo doAuth(String uId, String orderId, AuthInfo authInfo);
}

职责链模式重构代码

框架中的过滤器、拦截器

职责链模式常用在框架的开发中,为框架提供扩展点,让框架使用者可以在不修改框架源码的情况下,基于扩展点添加新的功能,职责链模式最常用来开发框架的过滤器和拦截器。

Servlet Filter

Servlet Filter 是 Java Servlet 规范中定义的组件,翻译成中文就是过滤器,它可以实现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,

所以,只要是支持 Servlet 的 Web 容器(比如 Tomcat 、Jetty 等),都支持过滤器功能。

在实际项目中,使用 Servlet Filter 如下, 添加一个过滤器,只需要定义一个 javax.servlet.Filter 接口的过滤器,并且将它配置在 web.xml 配置文件中。Web 容器启动的时候,会读取 web.xml 中的配置, 创建过滤器对象。当有请求到来的时候,会先经过过滤器,然后才由 Servlet 来处理。

public class LogFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 在创建Filter时自动调用,
// 其中filterConfig包含这个Filter的配置参数,比如name之类的(从配置文件中读取的)
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("拦截客户端发送来的请求.");
chain.doFilter(request, response);
System.out.println("拦截发送给客户端的响应.");
}

@Override
public void destroy() {
// 在销毁Filter时自动调用
}
}

// 在web.xml配置文件中如下配置:
<filter>
<filter-name>logFilter</filter-name>
<filter-class>com.xzg.cd.LogFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>logFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

在刚刚的示例代码中,添加过滤器非常方便,不需要修改任何代码,定义一个实现 javax.servlet.Filter 的类,再改改配置就搞定了,完全符合开闭原则。

Servlet Filter 做的如此好的扩展性,利用的就是职责链模式。

职责链模式的实现包含处理器接口(IHandler )或抽象类(Handler),以及处理器链(HandlerChain)。对应 Servlet Filter ,javax.servlet.Filter 就是处理器接口,FilterChain 就是处理器链。

Servlet 只是一个规范,并不包含具体的实现,所以 Servlet 中的 FilterChain 只是一个接口定义。具体的实现类由遵从 Servlet 规范的 Web 容器来提供,比如 ApplicationFilterChain 类就是 Tomcat 提供的 FilterChain 的实现类,源码如下所示。

public final class ApplicationFilterChain implements FilterChain {
private int pos = 0; //当前执行到了哪个filter
private int n; //filter的个数
private ApplicationFilterConfig[] filters;
private Servlet servlet;

@Override
public void doFilter(ServletRequest request, ServletResponse response) {
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
} else {
// filter都处理完毕后,执行servlet
servlet.service(request, response);
}
}

public void addFilter(ApplicationFilterConfig filterConfig) {
for (ApplicationFilterConfig filter:filters)
if (filter==filterConfig)
return;

if (n == filters.length) {//扩容
ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
}

ApplicationFilterChain 类的 doFilter() 函数的代码实现比较有技巧,实际上是一个递归调用。可以用每个 Filter (比如 LogFilter)的 doFilter 的代码实现,直接替换 ApplicationFilterChain 的第 12 行代码。

  @Override
public void doFilter(ServletRequest request, ServletResponse response) {
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
//filter.doFilter(request, response, this);
//把filter.doFilter的代码实现展开替换到这里
System.out.println("拦截客户端发送来的请求.");
chain.doFilter(request, response); // chain就是this
System.out.println("拦截发送给客户端的响应.")
} else {
// filter都处理完毕后,执行servlet
servlet.service(request, response);
}
}

这样实现是为了在一个 doFilter() 方法中,支持双向拦截,技能拦截客户端发送来的请求,也能拦截发送给客户端的响应。

Spring Interceptor

Spring Interceptor 翻译成中文就是拦截器,和 Servlet 的不同之处在于,Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter ,然后再经过 Spring Interceptor ,最后到达具体的业务代码中。

下面是使用 Spring Interception 的一个代码示例,实现的功能和上面的 LogFilter 完全相同,只是实现方式上稍有区别。LogFilter 对请求和响应的拦截是在 doFilter() 一个函数中实现的,而 LogInterceptor 对请求的拦截在 preHandle() 中实现,对响应的拦截在 postHandler() 中实现。

public class LogInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截客户端发送来的请求.");
return true; // 继续后续的处理
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截发送给客户端的响应.");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("这里总是被执行.");
}
}

//在Spring MVC配置文件中配置interceptors
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="com.xzg.cd.LogInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

Spring Interceptor 底层依然是基于职责链模式实现的。其中,HandlerExecutionChain 类是职责链模式中的处理器链。它的实现相较于 Tomcat 中的 ApplicationFilterChain 来说,逻辑更加清晰,不需要使用递归来实现,主要是因为它将请求和响应的拦截工作,拆分到了两个函数中实现。HandlerExecutionChain 的源码如下所示:

public class HandlerExecutionChain {
private final Object handler;
private HandlerInterceptor[] interceptors;

public void addInterceptor(HandlerInterceptor interceptor) {
initInterceptorList().add(interceptor);
}

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
}
}
return true;
}

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
}

在 Spring 框架中,DispatcherServlet 的 doDispatch() 方法来分发请求,它在真正的业务逻辑执行前后,执行 HandlerExecutionChain 的 applyPreHandler() 和 applyPostHandler() 函数,用来实现拦截的功能。

总结

职责链模式,可以让框架代码满足开闭原则。添加一个新的处理器,只需要修改客户端代码。

职责链模式常用在框架的开发中,为框架提供扩展点,让框架使用者可以在不修改框架源码的情况下,基于扩展点添加新的功能,比如常见的过滤器、拦截器功能。

Filter 可以拿到原始的 http 请求,但是拿不到请求的控制器和请求控制器中的方法的信息;Interceptor 可以拿到

优点

  • 降低了对象之间的耦合度:降低了请求发送者和接收者的耦合度
  • 增强了系统的可扩展性: 可以根据需要增加新的请求处理类,满足开闭原则
  • 增强了给对象指派职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者修改它们。
  • 责任链简化了对象之间的连接:一个对象只需要保持一个指向其后继者的引用,不需要保持其它所有者的引用,这避免了众多的 if 或者 if...else 语句。
  • 责任分担:每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合单一职责原则。

缺点

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能受到一定的影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错。比如循环调用等。

使用场景

  • 在运行时需要动态使用多个关联对象来处理同一次请求时。比如,请假流程、员工入职流程、编译打包发布上线流程、敏感词过滤等。
  • 不想让使用者知道具体的处理逻辑时,比如,做权限校验的登录拦截器。
  • 需要动态更换处理对象时。比如,工单处理系统、网关 API 过滤规则系统等。
  • 职责链模式经常被用在框架开发中,用来实现框架的过滤器、拦截器功能,让框架的使用者在不修改源码的情况下,添加新的拦截器功能。