java
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.fireExceptionCaught(cause);
}
调用ctx.fireExceptionCaught(cause); 来进行异常的向下传播,ctx是ChannelHandlerContext。因为pipeline中的每个节点是以ChannelHandlerContext形式构成的双向链表。
AbstractChannelHandlerContext 是顶级抽象,每个ChannelHandlerContext内部都持有一个指针指向下一个节点和上一个节点。
java
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
implements ChannelHandlerContext, ResourceLeakHint {
volatile AbstractChannelHandlerContext next;
volatile AbstractChannelHandlerContext prev;
}
ctx.fireExceptionCaught(cause);这个方法在把异常向下传播。这个方法显然需要定义在抽象类AbstractChannelHandlerContext中。因为每一个节点都有权利来决定是否向下传播,现在这个设计定义在父级抽象类中更合适。
父类中定义了如下代码:
java
@Override
public ChannelHandlerContext fireExceptionCaught(final Throwable cause) {
invokeExceptionCaught(next, cause);
return this;
}
static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) {
ObjectUtil.checkNotNull(cause, "cause");
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeExceptionCaught(cause);
} else {
try {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeExceptionCaught(cause);
}
});
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to submit an exceptionCaught() event.", t);
logger.warn("The exceptionCaught() event that was failed to submit was:", cause);
}
}
}
}
上面的代码看出,实际调用是父级的静态抽象方法:
static void invokeExceptionCaught(final AbstractChannelHandlerContext next, final Throwable cause) 这个方法接收两个参数 第一个 就是:下一个要传播的节点next, 第二个就是:传播的异常。显然next的取值来自于双向链表的下一个指针。也就是最上面的volatile AbstractChannelHandlerContext next;
最终都会调用到:每一个节点的invokeExceptionCaught
invokeExceptionCaught也是定义在抽象类中的,并且是private方法。
java
private void invokeExceptionCaught(final Throwable cause) {
if (invokeHandler()) {
try {
handler().exceptionCaught(this, cause);
} catch (Throwable error) {
if (logger.isDebugEnabled()) {
logger.debug(
"An exception {}" +
"was thrown by a user handler's exceptionCaught() " +
"method while handling the following exception:",
ThrowableUtil.stackTraceToString(error), cause);
} else if (logger.isWarnEnabled()) {
logger.warn(
"An exception '{}' [enable DEBUG level for full stacktrace] " +
"was thrown by a user handler's exceptionCaught() " +
"method while handling the following exception:", error, cause);
}
}
} else {
fireExceptionCaught(cause);
}
}
整个这个private中的逻辑 invokeHandler()是先检查是否handler被移除了,是否还有效,如果有效就调用handler的handler().exceptionCaught(this, cause)。
权限收口: 将核心执行逻辑设为 private,是为了防止子类破坏"检查-执行-保护"的固定流水线流程。
抽象类处理通用的框架逻辑,不让用户自己私自修改,防止流程上被破坏,只是给用户留了handler中的exceptionCaught用来决定怎么处理异常。
异常的传播,牢牢的掌握在AbstractChannelHandlerContext这个类中。AbstractChannelHandlerContext负责整个流程的契约。防止 "子类破坏了父类的契约"
总结
1.异常从pipeline中的当前节点,一直向后传播,一直到tail节点。不区分inboundHandler还是outboundHandler
2.如果handler不重写 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
ChannelInboundHandlerAdapter的默认实现就是直接向下传播。
java
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.fireExceptionCaught(cause);
}
3.整个流程都封装在抽象类中了,防止开发者破坏整个pipeline的流转流程