ChannelHandler(管道處理器)其工作模式類似于Java Servlet過濾器,負責對I/O事件或者I/O操作進行攔截處理。采用事件的好處是,ChannelHandler可以選擇自己感興趣的事件進行處理,也可以對不感興趣的事件進行透傳或者終止。
ChannelHandler接口
基于ChannelHandler接口,使用者可以友善實作自己的業務,比如記錄日志、編解碼、資料過濾等。ChannelHandler接口定義如下:
package io.netty.channel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public interface ChannelHandler {
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
}
}
ChannelHandler接口定義比如簡單,隻有三個方法:
- handlerAdded方法在ChannelHandler被添加到實際上下文中并準備好處理事件後調用。
- handlerRemoved方法在ChannelHandler從實際上下文中移除後調用,表明它不再處理事件。
- exceptionCaught方法會在抛出Throwable類後調用。
還有一個Sharable注解,該注解用于表示多個ChannelPipeline可以共享同一個ChannelHandler。
正式因為ChannelHandler接口過于簡單,我們在實際開發中,不會直接實作ChannelHandler接口,是以,Netty提供了ChannelHandlerAdapter抽象類。
ChannelHandlerAdapter抽象類
ChannelHandlerAdapter抽象類核心代碼如下:
package io.netty.channel;
import io.netty.util.internal.InternalThreadLocalMap;
import java.util.Map;
import java.util.WeakHashMap;
public abstract class ChannelHandlerAdapter implements ChannelHandler {
boolean added;
public boolean isSharable() {
Class<?> clazz = getClass();
Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
Boolean sharable = cache.get(clazz);
if (sharable == null) {
sharable = clazz.isAnnotationPresent(Sharable.class);
cache.put(clazz, sharable);
}
return sharable;
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// NOOP
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// NOOP
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.fireExceptionCaught(cause);
}
}
ChannelHandlerAdapter對exceptionCaught方法做了實作,并提供了isSharable方法。需要注意的是,ChannelHandlerAdapter是抽象類,使用者可以自由的選擇是否要覆寫ChannelHandlerAdapter類的實作。如果對某個方法感興趣,直接覆寫掉這個方法即可,這樣代碼就變得簡單清晰。
ChannelHandlerAdapter抽象類提供了兩個子類ChannelInboundHandlerAdapter、ChannelOutboundHandlerAdapter用于針對出站事件、入站事件的進行處理。其中ChannelInboundHandlerAdapter實作了ChannelInboundHandler接口,而ChannelOutboundHandlerAdapter實作了ChannelOutboundHandler接口。
在實際開發過程中,我們的自定義的ChannelHandler多數是繼承自ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter類或者是這兩個類的子類。比如在前面章節中所涉及的編解碼器ByteToMessageDecoder、MessageToMessageDecoder、MessageToByteEncoder、MessageToMessageEncoder等,就是這兩個類的子類。