来看客户端调用代码:
public class Test { public static void main(String[] args) { MemberService memberService = new MemberService(); memberService.login("tom","666"); } }
其运行结果如下:
用户名和密码校验成功,可以往下执行 登录成功! 您是管理员,允许操作
其实我们平时使用的很多权限校验框架都是运用这样一个原理,将各个维度的权限处理解耦之后再
串联起来,各自只处理各自相关的职责。如果职责与自己不相关则抛给链上的下一个Handler, 俗称踢
皮球。
责任链模式和建造者模式结合使用
因为责任链模式具备链式结构,而上面代码中,我们看到,负责组装链式结构的角色是
MemberService ,当链式结构较长时,MemberService的工作会非常繁琐,并且MemberService代
码相对臃肿,且后续更改处理者或消息类型时,都必须在MemberService中进行修改,不符合开闭原 则。产生这些问题的原因就是因为链式结构的组装过于复杂,而对于复杂结构的创建,很自然我们就会
想到建造者模式,使用建造者模式,我们完全可以MemberService指定的处理节点对象进行自动链式组装,客户只需指定处理节点对象,其他任何事情都无需关心,并且客户指定的处理节点对象顺序不
同 ,构造出来的链式结构也随之不同。我们来改造一下,修改Handler的代码:
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){ // do { if (this.head == null) { this.head = this.tail = handler; return this; } this.tail.next(handler); this.tail = handler; // }while (false);//真正框架中,如果是双向链表,会判断是否已经到了尾部 return this; } public Handler<T> build(){ return this.head; } } }
然 后 ,修改MemberService的代码:
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)); //用过Netty的人,肯定见过 } }
因为建造者模式要构建的是节点处理者,因此我们把Builder作为Handler的静态内部类,并且因 为客户端无需进行链式组装,因此我们还可以把链式组装方法next。方法设置为private,使 Handler
更加高内聚,代码如下:
public abstract class Handler<T> { protected Handler chain; private void next(Handler handler) { this.chain = handler; } }
通过这个案例,小伙伴们应该已经感受到责任链和建造者结合的精 ITo
七、责任链模式在源码中的体现
首先我们来看责任链模式在JDK中的应用,来看一个J2EE标准中非常常见的Filter类:
public interface Filter { public void init(FilterConfig filterConfig) throws ServletException; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; public void destroy(); }
这个Filter接口的定义非常简单,相当于责任链模型中的Handler抽象角色,那么它是如何形成一 条责任链的呢?我来看另外一个类,其实在doFilte()方法的最后一个参数我们已经看到其类型是
FilterChain类 ,来看它的源码:
public interface FilterChain { public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException; }
FilterChain类中也只定义了一个doFilter()方法,那么它们是怎么串联成一个责任链的呢?实际上 J2EE只是定义了一个规范,具体处理逻辑是由使用者自己来实现。我们来看一个Spring的实现
MockFilterChain类:
public class MockFilterChain implements FilterChain { @Nullable private ServletRequest request; @Nullable private ServletResponse response; private final List<Filter> filters; @Nullable private Iterator<Filter> iterator; public MockFilterChain() { this.filters = Collections.emptyList(); } public MockFilterChain(Servlet servlet) { this.filters = initFilterList(servlet); } public MockFilterChain(Servlet servlet, Filter... filters) { Assert.notNull(filters, "filters cannot be null"); Assert.noNullElements(filters, "filters cannot contain null values"); this.filters = initFilterList(servlet, filters); } private static List<Filter> initFilterList(Servlet servlet, Filter... filters) { Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet)); return Arrays.asList(allFilters); } @Nullable public ServletRequest getRequest() { return this.request; } @Nullable public ServletResponse getResponse() { return this.response; } @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; } public void reset() { this.request = null; this.response = null; this.iterator = null; } private static class ServletFilterProxy implements Filter { private final Servlet delegateServlet; private ServletFilterProxy(Servlet servlet) { Assert.notNull(servlet, "servlet cannot be null"); this.delegateServlet = servlet; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { this.delegateServlet.service(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } @Override public String toString() { return this.delegateServlet.toString(); } } }
它把链条中的所有Filter放到List中 ,然后在调用doFilter。方法时循环迭代List , 也就是说List
中的Filter会顺序执行。
再来看一个在Netty中非常经典的串行化处理Pipeline就采用了责任链设计模式。它底层采用双向 链表的数据结构,将链上的各个处理器串联起来。客户端每一个请求的到来,Netty都认为Pipeline中 的所有的处理器都有机会处理它。因此,对于入栈的请求全部从头节点开始往后传播,一直传播到尾节
点才会把消息释放掉。来看一个Netty的责任处理器接口 ChannelHandler:
public interface ChannelHandler { // 当 handler被添加到真实的上下文中,并且准备处理事件时被调用 // handler被添加进去的回调 void handlerAdded(ChannelHandlerContext var1) throws Exception; // 是 handler被移出的后的回调 void handlerRemoved(ChannelHandlerContext var1) throws Exception; /** @deprecated */ @Deprecated void exceptionCaught(ChannelHandlerContext var1, Throwable var2) throws Exception; @Inherited @Documented @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Sharable { } }
Netty对责任处理接口做了更细粒度的划分,处理器被分成了两种,一种是入栈处理器
ChannellnboundHandler, 另一种是出栈处理器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); ... }
在默认是实现类中将所有的Handler都串成了一个链表:
public class DefaultChannelPipeline implements ChannelPipeline { static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class); final AbstractChannelHandlerContext head; final AbstractChannelHandlerContext tail; ... }
在 Pipeline中的任意一个节点,只要我们不手动的往下传播下去,这个事件就会终止传播在当前节 点。对于入栈数据,默认会传递到尾节点进行回收。如果我们不进行下一步传播,事件就会终止在当前
节点。对于出栈数据把数据写会客户端也意味看事件的终止。
当然在很多安全框架中也会大量使用责任链模式,比如Spring Security. Apache Shiro都会用到
设计模式中的责任链模式,感兴趣的小伙伴可以尝试自己去研究一下。
大部分框架中无论怎么实现,所有的实现都是大同小异的。其实如果我们是站在设计者这个角度看
源码的话,对我们学习源码和编码内功是非常非常有益处的,因为这样,我们可以站在更高的角度来学
习优秀的思想,而不是钻到某一个代码细节里边。我们需要对所有的设计必须有一个宏观概念,这样学
习起来才更加轻松。
八、责任链模式的优缺点
优点:
1、将请求与处理解耦;
2、请求处理者(节点对象)只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转
发给下一级节点对象;
3、 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果;
4、 链路结构灵活,可以通过改变链路结构动态地新增或删减责任;
5、 易于扩展新的请求处理类(节点),符合开闭原则。
缺点:
1、 责任链太长或者处理时间过长,会影响整体性能
2、 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃;
参考文献:策略模式和参考者模式