简介
责任链模式(Chain of Responsibility Pattern)也叫职责链模式:是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,知道有对象处理这个请求为止。属于行为型模式。
责任链的应用场景
1、工作中的审批流程
2、游戏中的闯关
责任链模式主要是解耦了请求与处理,客户只需将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点对象进行处理。适用于以下应用场景:
1、多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定;
2、在不明确指定接受者的情况下,向多个对象中的一个提交一个请求
3、可动态指定一组对象处理请求
责任链模式的UML类图:

从UML类图,我们可以看到,责任链模式只要包含两种角色:
抽象处理者(Handler):定义一个请求处理的方法,并维护一个下一个处理节点Handler对象的引用;
具体处理者(ConcreteHandler):对请求进行处理,如果不感兴趣,则进行转发。
责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解的是其模式而不是具体实现,责任链模式的独到之处是其将节点处理者组成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动了起来。
责任链模式在源码中的体现
1、JDK中的应用,Filter类
public interface Filter {
public default void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public default void destroy() {}
}
这个接口相当于责任链模型中的Handler抽象角色。通过doFilter()方法形成一条责任链。
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
FilterChain
类中也只定义了一个doFilter()
方法,那么它们是怎么串联成一个责任链呢?实际上J2EE只是定义了一个规范,具体处理逻辑是由使用者自己来实现。
- Spring中的实现
public class MockFilterChain implements FilterChain {
@Nullable
private ServletRequest request;
@Nullable
private ServletResponse response;
private final List<Filter> filters;
@Nullable
private Iterator<Filter> iterator;
...
@Override
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 = this.iterator.next();
nextFilter.doFilter(request, response, this);
}
this.request = request;
this.response = response;
}
private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {
Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
return Arrays.asList(allFilters);
}
...
}
它把链条中的所有Filter放到List中,然后在调用doFilter()方法时循环迭代List,也就是说List中的Filter会顺序执行。
2、Netty中的串行化处理Pipeline就采用了责任链设计模式,它底层采用双向链表的数据结构,将链上的各个处理器串联起来。客户端每一次请求的到来,Netty都认为Pipeline中的所有处理器都有机会处理它。因此,对于入栈的请求全部从头节点开始往后传播,一直传播到尾节点才会把消息释放掉。
Netty的责任处理器接口ChannelHandler
:
public interface ChannelHandler {
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
@Deprecated
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
// no value
}
}
Netty对责任处理接口做了更细粒度的划分,处理器被分成了两种,一种是入栈处理器ChannelInboundHandler
,另一种是出栈处理器ChannelOutboundHandler
,这两个接口都继承自
ChannelHandler
。
而所有的处理器最终都添加到Pipeline上。所以,添加删除责任处理器额接口的行为在Netty的ChannelPipeline
中进行了规定:
public interface ChannelPipeline
extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable<Entry<String, ChannelHandler>> {
ChannelPipeline addFirst(String name, ChannelHandler handler);
ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);
ChannelPipeline addLast(String name, ChannelHandler handler);
ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);
ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);
ChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);
...
}
在默认实现类中将所有的Handler都串了起来(链表):
public class DefaultChannelPipeline implements ChannelPipeline {
...
final AbstractChannelHandlerContext head;
final AbstractChannelHandlerContext tail;
...
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
...
}
在Pipeline中的任意一个节点,只要我们不手动的往下传播下去,这个事件就会终止传播在当前节点。对于入栈数据,默认会传递到尾节点进行回收。如果我们不进行下一步传播,事件就会终止在当前节点。对于出栈数据把数据写回客户端也意味着事件的终止。
责任链模式的优缺点:
优点:
1、将请求与处理解耦
2、请求处理者(节点对象)只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一级节点对象;
3、具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果;
4、链路结构灵活,可以通过改变链路结构动态的新增或删减责任;
5、易于扩展新的请求处理类(节点),符合开闭原则。
缺点:
1、责任链太长或者处理时间过长,会影响整体性能;
2、如果节点对象存在循环引用,会造成死循环,导致系统崩溃。