Skip to content

责任链模式

定义:将链中每一个结点看作一个对象,每个节点处理的请求均不相同,且内部自动维护一个下一结点的对象。每一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个结点对象,直到有对象处理这个请求为止

本质:把请求和处理解耦,可以让请求在处理链中传递,最后被处理

适用场景:

  1. 多个对象可以处理同一请求,但具体由哪个对象处理则要在运行时动态决定
  2. 在不明确指定接受者的情况下,向多个对象中的一个提交一次请求
  3. 可动态指定一组对象处理请求

优点:

  1. 将请求和处理解耦,请求发送者不需要关心链路结构,只需等待结果
  2. 节点对象只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的、无法处理的,直接转发到下一个节点对象
  3. 易于扩展新的或移除旧的节点对象,动态调整链路结构

缺点:

  1. 责任链太长或者处理时间太长,会影响整体性能
  2. 如果链路中节点对象存在循环引用的情况,会造成系统崩溃

示例

以登录认证的流程为例,假设登录时需要完成三个操作

  1. 判断用户名和密码是否为空
  2. 进行登录
  3. 权限校验

这些操作都是必须要做的,而且都是有严格的先后顺序,使用 if...else... 代码块不美观,使用责任链模式可以把这些步骤都串联起来

用户类

java
public class Member {
    private String loginName;
    private String loginPass;
    private String roleName;

    public Member(String loginName, String loginPass) {
        this.loginName = loginName;
        this.loginPass = loginPass;
    }
    
    // get、set 方法省略
    // ...
}

处理业务逻辑的抽象 Handler,定义了一个抽象方法 doHandler ,具体处理的逻辑需要子类自行实现,同时维护了下一个处理对象节点。使用建造者模式来完成责任链的构建,更优雅

java
public abstract class Handler<T> {
    protected Handler next;
    public void next(Handler next){ this.next = next;}

    public abstract void doHandler(Member member);

    public static class Builder<T>{
        private Handler<T> head;
        private Handler<T> tail;

        public Builder<T> addHandler(Handler handler){
            if (this.head == null) {
                this.head = this.tail = handler;
                return this;
            }
            // 头指针不移动,始终指向第一个,每次都把尾指针往后挪
            this.tail.next(handler);
            this.tail = handler;
            return this;
        }

        public Handler<T> build(){
            return this.head;
        }

    }
}

判断用户名密码是否为空的节点

java
public class ValidateHandler extends Handler {
    public void doHandler(Member member) {
        if(StringUtils.isEmpty(member.getLoginName()) ||
                StringUtils.isEmpty(member.getLoginPass())){
            System.out.println("用户名和密码为空");
            return;
        }
        System.out.println("用户名和密码不为空,可以往下执行");
        if(null != next) {
            next.doHandler(member);
        }
    }
}

完成登录动作的节点

java
public class LoginHandler extends Handler {
    public void doHandler(Member member) {
        System.out.println("登录成功!");
        member.setRoleName("管理员");
        if(null != next) {
            next.doHandler(member);
        }
    }
}

校验权限的节点

java
public class AuthHandler extends Handler {
    public void doHandler(Member member) {
        if(!"管理员".equals(member.getRoleName())){
            System.out.println("您不是管理员,没有操作权限");
            return;
        }
        System.out.println("允许操作");
    }
}

使用门面模式封装所有细节,提供一个对外使用的方法

java
public class MemberService {
    public void login(String loginName,String loginPass){
        Handler.Builder builder = new Handler.Builder();
        builder.addHandler(new ValidateHandler())
               .addHandler(new LoginHandler())
               .addHandler(new AuthHandler());
        builder.build().doHandler(new Member(loginName,loginPass));
    }
}

客户端调用

java
public class Test {
    public static void main(String[] args) {
        MemberService memberService = new MemberService();
        memberService.login("tom","666");
    }
}

责任链模式在源码中的体现

Spring

Servlet 中定义了一个过滤器的接口,里面有一个抽象方法 doFilter() 负责执行具体的过滤工作,相当于责任链模式中的抽象节点的角色

java
public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {}

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {}
}

其中最后一个参数 FilterChain 也是Servlet 中定义的一个过滤器链的接口

java
public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

具体的串联可以看到 Spring 中的 MockFilterChain 类,把过滤器链中的所有 Filter 放到集合中,然后在调用 doFilter() 方法中迭代这个集合,使得集合中的所有 Filter 可以按顺序执行,因为 Filter 中的 doFilter() 方法最后会调用传入的 FilterChaindoFilter() 方法

java
public class MockFilterChain implements FilterChain {
    @Nullable
    private ServletRequest request;
    @Nullable
    private ServletResponse response;
    private final List<Filter> filters;
    @Nullable
    private Iterator<Filter> iterator;
    
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        Assert.notNull(request, "Request must not be null");
        Assert.notNull(response, "Response must not be null");
        Assert.state(this.request == null, "This FilterChain has already been called!");
        if (this.iterator == null) {
            this.iterator = this.filters.iterator();
        }

        if (this.iterator.hasNext()) {
            Filter nextFilter = (Filter)this.iterator.next();
            nextFilter.doFilter(request, response, this);
        }

        this.request = request;
        this.response = response;
    }
}