定义
多个处理器收尾相连组成一个链条,依次对同一个请求进行处理。链条上的处理器各自承担各自的职责,所以叫做职责链模式(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);
}
参考
《设计模式之美》- 极客时间