netty5的@SKIP注解的用法
netty5新增了一个注解 @SKIP 代码如下
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface Skip { // no value }
这个是干什么用的呢?
简单来说 ,netty5 在合并了 channelInbound channelOutbound 事件以后,对事件的分工更加精细化了 。
原来是 一部分事件分配到inboundHandelr 一部分分配到outBoundHandler中 。
现在 直接通过ChannelHandlerAdapter 把所有的事件加入到一个适配器里面 。然后给每个方法默认都加上这个注解(@skip)
public class ChannelHandlerAdapter implements ChannelHandler { // Not using volatile because it's used only for a sanity check. boolean added; /** * Return {@code true} if the implementation is {@link Sharable} and so can be added * to different {@link ChannelPipeline}s. */ public boolean isSharable() { /** * Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a * {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different * {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of * {@link Thread}s are quite limited anyway. * * See <a href="See https://github.com/netty/netty/issues/2289">#2289</a>. */ 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; } /** * Do nothing by default, sub-classes may override this method. */ @Skip @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // NOOP } /** * Do nothing by default, sub-classes may override this method. */ @Skip @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // NOOP } /** * Calls {@link ChannelHandlerContext#fireExceptionCaught(Throwable)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.fireExceptionCaught(cause); } /** * Calls {@link ChannelHandlerContext#fireChannelRegistered()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelRegistered(); } /** * Calls {@link ChannelHandlerContext#fireChannelUnregistered()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelUnregistered(); } /** * Calls {@link ChannelHandlerContext#fireChannelActive()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelActive(); } /** * Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelInactive(); } /** * Calls {@link ChannelHandlerContext#fireChannelRead(Object)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ctx.fireChannelRead(msg); } /** * Calls {@link ChannelHandlerContext#fireChannelReadComplete()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelReadComplete(); } /** * Calls {@link ChannelHandlerContext#fireUserEventTriggered(Object)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { ctx.fireUserEventTriggered(evt); } /** * Calls {@link ChannelHandlerContext#fireChannelWritabilityChanged()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelWritabilityChanged(); } /** * Calls {@link ChannelHandlerContext#bind(java.net.SocketAddress, ChannelPromise)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { ctx.bind(localAddress, promise); } /** * Calls {@link ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { ctx.connect(remoteAddress, localAddress, promise); } /** * Calls {@link ChannelHandlerContext#disconnect(ChannelPromise)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { ctx.disconnect(promise); } /** * Calls {@link ChannelHandlerContext#close(ChannelPromise)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { ctx.close(promise); } /** * Calls {@link ChannelHandlerContext#deregister(ChannelPromise)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { ctx.deregister(promise); } /** * Calls {@link ChannelHandlerContext#read()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void read(ChannelHandlerContext ctx) throws Exception { ctx.read(); } /** * Calls {@link ChannelHandlerContext#write(Object)} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { ctx.write(msg, promise); } /** * Calls {@link ChannelHandlerContext#flush()} to forward * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Skip @Override public void flush(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } }
这样 默认的handelr没有任何事件,全部都是skip跳过了。
但是事件是流入还是流出通过另一种方式体现出来了,AbstractChannelHandlerContext 中 定义了每个事件表示流入还是流出的,这里面就是关键所在了。请看下面定义
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint { // This class keeps an integer member field 'skipFlags' whose each bit tells if the corresponding handler method // is annotated with @Skip. 'skipFlags' is retrieved in runtime via the reflection API and is cached. // The following constants signify which bit of 'skipFlags' corresponds to which handler method: static final int MASK_HANDLER_ADDED = 1; static final int MASK_HANDLER_REMOVED = 1 << 1; private static final int MASK_EXCEPTION_CAUGHT = 1 << 2; private static final int MASK_CHANNEL_REGISTERED = 1 << 3; private static final int MASK_CHANNEL_UNREGISTERED = 1 << 4; private static final int MASK_CHANNEL_ACTIVE = 1 << 5; private static final int MASK_CHANNEL_INACTIVE = 1 << 6; private static final int MASK_CHANNEL_READ = 1 << 7; private static final int MASK_CHANNEL_READ_COMPLETE = 1 << 8; private static final int MASK_CHANNEL_WRITABILITY_CHANGED = 1 << 9; private static final int MASK_USER_EVENT_TRIGGERED = 1 << 10; private static final int MASK_BIND = 1 << 11; private static final int MASK_CONNECT = 1 << 12; private static final int MASK_DISCONNECT = 1 << 13; private static final int MASK_CLOSE = 1 << 14; private static final int MASK_DEREGISTER = 1 << 15; private static final int MASK_READ = 1 << 16; private static final int MASK_WRITE = 1 << 17; private static final int MASK_FLUSH = 1 << 18; private static final int MASKGROUP_INBOUND = MASK_EXCEPTION_CAUGHT | MASK_CHANNEL_REGISTERED | MASK_CHANNEL_UNREGISTERED | MASK_CHANNEL_ACTIVE | MASK_CHANNEL_INACTIVE | MASK_CHANNEL_READ | MASK_CHANNEL_READ_COMPLETE | MASK_CHANNEL_WRITABILITY_CHANGED | MASK_USER_EVENT_TRIGGERED; private static final int MASKGROUP_OUTBOUND = MASK_BIND | MASK_CONNECT | MASK_DISCONNECT | MASK_CLOSE | MASK_DEREGISTER | MASK_READ | MASK_WRITE | MASK_FLUSH;
netty在加载相关处理器的事件(都继承自ChannelHandler)的时候 比如encoderHandler...
会初始化这个事件所以成的所有方法组成一个通过与运算组成的int结果集,当然进行其他运算的时候会直接转换成二进制进行与运算。下面的代码 就是
为了生成该handler没有继承的方法集合(包含skip注解的方法)
static int skipFlags0(Class<? extends ChannelHandler> handlerType) { int flags = 0; try { if (isSkippable(handlerType, "handlerAdded")) { flags |= MASK_HANDLER_ADDED; } if (isSkippable(handlerType, "handlerRemoved")) { flags |= MASK_HANDLER_REMOVED; } if (isSkippable(handlerType, "exceptionCaught", Throwable.class)) { flags |= MASK_EXCEPTION_CAUGHT; } if (isSkippable(handlerType, "channelRegistered")) { flags |= MASK_CHANNEL_REGISTERED; } if (isSkippable(handlerType, "channelUnregistered")) { flags |= MASK_CHANNEL_UNREGISTERED; } if (isSkippable(handlerType, "channelActive")) { flags |= MASK_CHANNEL_ACTIVE; } if (isSkippable(handlerType, "channelInactive")) { flags |= MASK_CHANNEL_INACTIVE; } if (isSkippable(handlerType, "channelRead", Object.class)) { flags |= MASK_CHANNEL_READ; } if (isSkippable(handlerType, "channelReadComplete")) { flags |= MASK_CHANNEL_READ_COMPLETE; } if (isSkippable(handlerType, "channelWritabilityChanged")) { flags |= MASK_CHANNEL_WRITABILITY_CHANGED; } if (isSkippable(handlerType, "userEventTriggered", Object.class)) { flags |= MASK_USER_EVENT_TRIGGERED; } if (isSkippable(handlerType, "bind", SocketAddress.class, ChannelPromise.class)) { flags |= MASK_BIND; } if (isSkippable(handlerType, "connect", SocketAddress.class, SocketAddress.class, ChannelPromise.class)) { flags |= MASK_CONNECT; } if (isSkippable(handlerType, "disconnect", ChannelPromise.class)) { flags |= MASK_DISCONNECT; } if (isSkippable(handlerType, "close", ChannelPromise.class)) { flags |= MASK_CLOSE; } if (isSkippable(handlerType, "deregister", ChannelPromise.class)) { flags |= MASK_DEREGISTER; } if (isSkippable(handlerType, "read")) { flags |= MASK_READ; } if (isSkippable(handlerType, "write", Object.class, ChannelPromise.class)) { flags |= MASK_WRITE; } if (isSkippable(handlerType, "flush")) { flags |= MASK_FLUSH; } } catch (Exception e) { // Should never reach here. PlatformDependent.throwException(e); } return flags; }
好了 。每个handler都有自己的skipflags了,再联合上面的 上面的代码就能判断 哪些是流入的函数要执行 的。哪些是流出的函数要执行的。比如 ,
我们业务处理器里面要写出数据的时候一般调用 ctx.writeAndFlush(byteBuf);这个时候 请看里面的代码 :
@Override public ChannelFuture writeAndFlush(Object msg) { return writeAndFlush(msg, newPromise()); } //-------------------------/// @Override public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { AbstractChannelHandlerContext next; next = findContextOutbound(); ReferenceCountUtil.touch(msg, next); next.invoker().invokeWrite(next, msg, promise); next = findContextOutbound(); next.invoker().invokeFlush(next); return promise; } //------------------/// private AbstractChannelHandlerContext findContextOutbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.prev; } while ((ctx.skipFlags & MASKGROUP_OUTBOUND) == MASKGROUP_OUTBOUND); return ctx; }
最后一个方法请看
要找流出的方法 ,通过
ctx.skipFlags & MASKGROUP_OUTBOUND) == MASKGROUP_OUTBOUND 判断 如果handler包含流出的方法 ,就会执行 。