责任链模式,对象行为模式,每一个对象对其下家引用,形成一条责任链,业务请求在链上传递,直到某一条件形成,责任承担者处理后 结束。标准的责任链是将只有链上的某一个对象对请求进行直接处理,然而实际应用中,往往是链上的多个责任对象对请求处理进行一次预处理,这样一来可以实现多个应用场景,如过滤,日志,访问控制等。从这种角度上看,类似于AOP的模型。
责任链 简单结构
每个承担者 引用下一个承担者的对象。形成一条处理链。
介绍几个应用场景
1、 netty 4
1)pipeline的构造
channel中封装了 一个 Pipeline,
DefaultChannelPipeline //封装了一个条handlerContext的 责任链
而该pipeline中默认一个tail,head,而后根据业务逻辑,会添加用户指定的handler,而添加时会将handler包装成context. 而后会加入到head为 首部,tail会尾部的链表中。
//封装成Context中
public ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
synchronized(this) {
this.checkDuplicateName(name);
DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
this.addLast0(name, newCtx);
return this;
}
}
//加入到双向链表中
private void addLast0(String name, AbstractChannelHandlerContext newCtx) {
checkMultiplicity(newCtx);
AbstractChannelHandlerContext prev = this.tail.prev;
newCtx.prev = prev;
newCtx.next = this.tail;
prev.next = newCtx;
this.tail.prev = newCtx;
this.name2ctx.put(name, newCtx);
this.callHandlerAdded(newCtx);
}
也就是将所有的handler(业务的处理逻辑) 封装到了 AbstractChannelHandlerContext ,每个context又有 prev和 next的 引用。形成了一条双向链表,双向的目的在于 netty中对于消息的 接收(in)与发送(out)。主要的成员变量如下
volatile AbstractChannelHandlerContext next; volatile AbstractChannelHandlerContext prev; private final boolean inbound; private final boolean outbound; private final AbstractChannel channel; private final DefaultChannelPipeline pipeline; private final String name;
ChannelHandler handler(); //父类成员
2)业务的执行
而业务的触发来自channel对事件的处理,如bind,register,read等都会通过pipeline中的方法,将业务引导到链中。如下是chanel注册后的关键逻辑
private void register0(ChannelPromise promise) { try { if(!promise.setUncancellable() || !this.ensureOpen(promise)) { return; } boolean t = this.neverRegistered; AbstractChannel.this.doRegister(); this.neverRegistered = false; AbstractChannel.this.registered = true; this.safeSetSuccess(promise); AbstractChannel.this.pipeline.fireChannelRegistered(); if(t && AbstractChannel.this.isActive()) { AbstractChannel.this.pipeline.fireChannelActive(); } } catch (Throwable var3) { this.closeForcibly(); AbstractChannel.this.closeFuture.setClosed(); this.safeSetFailure(promise, var3); } }
而调用 pipeline的 fireChannl** 的方法时,而后在pipeline中
public ChannelPipeline fireChannelActive() { this.head.fireChannelActive(); if(this.channel.config().isAutoRead()) { this.channel.read(); } return this; }
依次逻辑将请求传递到context中。
public ChannelHandlerContext fireChannelActive() { final AbstractChannelHandlerContext next = this.findContextInbound();//在链表中,找到下一个context,也就是当前context的next EventExecutor executor = next.executor(); if(executor.inEventLoop()) { next.invokeChannelActive(); } else { executor.execute(new OneTimeTask() { public void run() { next.invokeChannelActive(); } }); } return this; }
private void invokeChannelActive() { try { ((ChannelInboundHandler)this.handler()).channelActive(this);// 执行handler的 业务逻辑。 } catch (Throwable var2) { this.notifyHandlerException(var2); } }
而后每个handler根据定义的逻辑,执行,每handler根据定义,一般会继承 adapter,adapter会调用下一,然后tail结束。 一般要重写下面的方法。因为在执行handler的 channle**的 方法里,参数是当前的context(上面介绍 其next和prev的引用),如此一来就会形成一条处理链。
public void channelRegistered(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelRegistered(); }
2、Tomcat Filter
Tomcat大家都非常熟悉,如果要定义filter通常是在web.xml中定义。然后当我们定义了多个filter时,具体是如何实现的呢?
Tomcat中 类似 于Netty的实现,也有一个chain,在Tomcat中是
ApplicationFilterChain ,这个chain 的初始化是在 ApplicationFilterConfig对象数组,而这个 config封装了 Filter,也就是业务逻辑。
同样filter中的doFilter方法中,要将chain做为参数传递进行,其要作为 链执行下去的 必要条件。
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
3、motan中的 Provider链
motan是新浪微博的rpc框架,其中核心业务是封装成provider对象。
而provider执行的前后 ,增加了一个类似 于责任链的逻辑,完成了一些日志、访问、计数等功能的filter。
首先定义好配置文件,根据其得到filters,首先这里先介绍下filter的filter 方法,其参数有一个为 Caller,而provider实现了该caller。如此意图大家就明白了。如下是日志filter的逻辑,根据需求会在其前后加一些业务逻辑,如访问控制 、日志等。
@Override
public Response filter(Caller<?> caller, Request request) {
boolean needLog = caller.getUrl().getBooleanParameter(URLParamType.accessLog.getName(), URLParamType.accessLog.getBooleanValue());
if (needLog) {
long t1 = System.currentTimeMillis();
boolean success = false;
try {
Response response = caller.call(request);
success = true;
return response;
} finally {
long consumeTime = System.currentTimeMillis() - t1;
logAccess(caller, request, consumeTime, success);
}
} else {
return caller.call(request);
}
}
如下的逻辑就是核心的 provier 链中,每个内部类Provider 是为了封装filter,而privider的方法中调用filter的方法,将下一个provider作为参数传递。从而完成一个处理链。
private <T> Provider<T> decorateWithFilter(Provider<T> provider, URL url) {
List<Filter> filters = getFilters(url, MotanConstants.NODE_TYPE_SERVICE);//从配置中得到Filter
if (filters == null || filters.size() == 0) {
return provider;
}
Provider<T> lastProvider = provider;
for (Filter filter : filters) {
final Filter f = filter;
final Provider<T> lp = lastProvider;
lastProvider = new Provider<T>() {
@Override
public Response call(Request request) {
return f.filter(lp, request);
}
。。。。。。
}
}
而上面的代码结构中,用到了匿名内部类。在类中所引用的外部的局部变量中,必须用final来修饰。因为闭包,引用外部的参数,但是编译器只支持值copy,而不支持 引用的copy,所以强制 的限制 参数必须用final ,使变量不可变。内外保持变量同步。
https://www.zhihu.com/question/21395848 -- 内部类详解
总结:
从以上的实际 应用中,都不是纯的责任链。每一个链上的对象并不是直接把 责任推给下方,而是会经过一定的逻辑处理,而后传递给下方处理, 而另一个 通过 责任的直接承担者,并不会直接引用 下一个 承担者,而是封装到一个对象中,该对象来引用一些其它对象,解耦,每个责任只需 业务逻辑,总的业务要一个chain串联起来,而chain中的对象由一个包装了承担者的wrap对象组成。
http://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html