职责链

定义

多个处理器收尾相连组成一个链条,依次对同一个请求进行处理。链条上的处理器各自承担各自的职责,所以叫做职责链模式(Chain Of Responsibility Design Pattern)。

责任链模式的作用是:复用和扩展,降低代码复杂度。特别是在框架开发中,可以利用职责链提供框架的扩展点,在不改变源码的情况下,基于扩展点定制框架的功能。此外,利用职责链还可以灵活地配置组合处理器。

GOF 定义:
将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

在GOF的定义中,如果链条上的某个处理器能够处理请求,那边请求就不会往下传递。实际中,还有另外一种变体:请求会被所有处理器处理一遍,不存在中断的情况。

职责链如何应对代码复杂度
将大块代码逻辑拆分成函数,将大类拆分成小类,是应对代码复杂性的常用方法。应用职责链模式,将逻辑拆分到了不同的实现类中。

职责链如何让代码满足开闭原则
当要扩展新的功能点时,职责链只需添加一个新的处理器,其它代码完全不需要修改。

实现

职责链可以基于链表实现,也可以基于数组实现。

基于链表实现时,定义Handler为所有处理器的抽象父类,doHandler()是定义职责的抽象方法,实例变量nextHandler指向下一个处理器,将多个处理器串联起来。为了将业务代码和调用下一个处理器的代码分离,引入模板模式,将责任链的调用逻辑方法放在Handler中的模板方法handle()中。每个具体的处理类继承Handler并实现其中的doHandler()方法,实现具体的业务逻辑即可。

在Spring框架下,可以利用依赖注入,为每个处理类注入nextHandler。在下面示例中,职责链:HandlerA -> HandlerB


public abstract class Handler {

    protected Handler nextHandler;

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

    protected abstract void doHandle();

}


@Component("handlerA")
public class HandlerA extends Handler {

    @Resource(name = "handlerB")
    public void setHandler(Handler handler) {
        this.nextHandler = handler;
    }

    @Override
    protected void doHandle() {
        System.out.println("Handler A");
    }
}


@Component("handlerB")
public class HandlerB extends Handler {

    @Override
    protected void doHandle() {
        System.out.println("Handler B");
    }
}


@SpringBootTest
@RunWith(SpringRunner.class)
public class TestHandler {

    @Resource(name = "handlerA")
    private Handler handler;

    @Test
    public void test() {
        handler.handle();
    }

}

在非Spring框架下,需手动为每个处理器设置nextHandler。HandlerChain 是处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。

public abstract class Handler {

    protected Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

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

    protected abstract void doHandle();

}


public class HandlerA extends Handler {

    @Override
    protected void doHandle() {
        System.out.println("Handler A");
    }
}


public class HandlerB extends Handler {

    @Override
    protected void doHandle() {
        System.out.println("Handler B");
    }
}


public class HandlerChain {

    private Handler head;

    private Handler tail;

    public void addHandler(Handler handler) {
        if (head == null) {
            head = handler;
            tail = handler;
        } else {
            tail.setNextHandler(handler);
            tail = handler;
        }
    }

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


public class Application {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.addHandler(new HandlerA());
        handlerChain.addHandler(new HandlerB());

        handlerChain.execute();
    }
}

案例

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

Servlet Filter
Servlet Filter 是 Java Servlet 规范中定义的组件,用于对HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。

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

在这里插入图片描述

在Servlet Filter,javax.servlet.Filter是处理器接口,FilterChain 是处理器链,ApplicationFilterChain 类是 Tomcat 提供的 FilterChain 的实现类,使用数据保存所有的处理器,并依次执行。


public final class ApplicationFilterChain implements FilterChain {
    private int pos = 0; //当前执行到了哪个filter
    private int n; //filter的个数
    private ApplicationFilterConfig[] filters;// 所有注册的过滤器
    private Servlet servlet;
    public static final int INCREMENT = 10;

    // 执行过滤职责链
    @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() 函数的代码实现比较有技巧,实际上是一个递归调用。这样实现主要是为了在一个 doFilter() 方法中,支持双向拦截,既能拦截客户端发送来的请求,也能拦截发送给客户端的响应。

Spring Interceptor
Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中。与Servlet Filter相同,都用来实现对 HTTP 请求进行拦截处理。Spring Interceptor对请求的拦截在 preHandle() 中实现,对响应的拦截在 postHandle() 中实现,而Servlet Filter 请求和响应的拦截是在 doFilter() 一个函数中实现的。

添加一个Spring Interceptor只需定义一个实现 org.springframework.web.servlet.HandlerInterceptor 接口的拦截器类,然后加入到Spring的配置文件中。

在这里插入图片描述

在Spring Interceptor中,HandlerInterceptor 是 处理器接口类,HandlerExecutionChain 类是职责链模式中的处理器链。


/**
 * Handler execution chain, consisting of handler object and any handler interceptors.
 * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
 *
 * @author Juergen Hoeller
 * @since 20.06.2003
 * @see HandlerInterceptor
 */
public class HandlerExecutionChain {

    private final Object handler;

    private HandlerInterceptor[] interceptors;

    public void addInterceptors(HandlerInterceptor... interceptors) {
        if (!ObjectUtils.isEmpty(interceptors)) {
            CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
        }
    }


    /**
     * Apply preHandle methods of registered interceptors.
     * @return {@code true} if the execution chain should proceed with the
     * next interceptor or the handler itself. Else, DispatcherServlet assumes
     * that this interceptor has already dealt with the response itself.
     */
    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;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }

    /**
     * Apply postHandle methods of registered interceptors.
     */
    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);
            }
        }
    }

    /**
     * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
     * Will just invoke afterCompletion for all interceptors whose preHandle invocation
     * has successfully completed and returned true.
     */
    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);
                }
            }
        }
    }
    

}


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

 /**
     * Process the actual dispatching to the handler.
     * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
     * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
     * to find the first that supports the handler class.
     * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
     * themselves to decide which methods are acceptable.
     * @param request current HTTP request
     * @param response current HTTP response
     * @throws Exception in case of any kind of processing failure
     */
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;

        mappedHandler = getHandler(processedRequest);

        ....
        
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
        }
        
        ...
        
        // Actually invoke the handler.
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        ...
        
        mappedHandler.applyPostHandle(processedRequest, response, mv);
    }

参考

《设计模式之美》- 极客时间

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值