innetty在netty中,我们需要传递各种类型的消息。这些消息可以是字符串、数组或自定义对象。不同的对象可能需要相互转换,所以需要一个可以自由转换的转换器。为了统一编码规则,方便用户扩展,netty提供了消息之间相互转换的框架。本文将讲解这个框架的具体实现。框架介绍Netty提供了三个类用于消息与消息之间的转换。这三个类都是抽象类,分别是MessageToMessageDecoder、MessageToMessageEncoder和MessageToMessageCodec。先来看下他们的定义:publicabstractclassMessageToMessageEncoderextendsChannelOutboundHandlerAdapterpublicabstractclassMessageToMessageDecoderextendsChannelInboundHandlerAdapterpublicabstractclassMessageToMessageCodecextendsChannelDuMessageEncoderMessageToMessageDecoder继承自ChannelOutboundHandlerAdapter,负责向channel中写入消息。MessageToMessageDecoder继承自ChannelInboundHandlerAdapter,负责从channel读取消息。MessageToMessageCodec继承自ChannelDuplexHandler,它是一个双向处理程序,可以从通道读取消息或将消息写入通道。有了这三个抽象类,我们再来看这三个类的具体实现。MessageToMessageEncoder首先看消息编码器MessageToMessageEncoder,编码器中最重要的方法是write,看write的实现:try{if(acceptOutboundMessage(msg)){out=CodecOutputList.newInstance();@SuppressWarnings("unchecked")I??cast=(I)msg??;尝试{编码(ctx,投射,输出);}最后{ReferenceCountUtil.release(cast);}if(out.isEmpty()){thrownewEncoderException(StringUtil.simpleClassName(this)+"必须产生至少一条消息。");}}else{ctx.write(msg,promise);}}catch(EncoderExceptione){抛出e;}赶上(Throwablet){抛出新的编码器异常(t);}finally{if(out!=null){try{finalintsizeMinusOne=out.大小()-1;如果(sizeMinusOne==0){ctx.写(出。getUnsafe(0),承诺);}elseif(sizeMinusOne>0){if(promise==ctx.voidPromise()){writeVoidPromise(ctx,out);}else{writePromiseCombiner(ctx,out,promise);}}}最后{out.recycle();}}}}write方法接受一个需要转换的原始对象msg,以及一个表示通道读写进度的ChannelPromise。首先会对msg进行类型判断,在acceptOutboundMessage中实现。publicbooleanacceptOutboundMessage(Objectmsg)throwsException{returnmatcher.match(msg);}这里的matcher是一个TypeParameterMatcher对象,它是在MessageToMessageEncoder构造函数中初始化的一个属性:这里我是要匹配的消息类型。如果不匹配,继续调用ctx.write(msg,promise);将消息写入通道而不进行任何转换以供下一个处理程序调用。如果匹配成功,会调用核心的encode方法:encode(ctx,cast,out);注意encode方法是MessageToMessageEncoder中的一个抽象方法,需要用户在继承类中进行扩展。encode方法实际上是将msg对象转换成需要转换的对象,然后添加到out中。这个out是一个list对象,具体是一个CodecOutputList对象,作为一个list,out是一个可以存储多个对象的list。那么什么时候输出到通道呢?不用担心,在write方法的末尾有一个finally代码块。在此代码块中,out将被写入通道。因为out是一个List,out里面的一些对象可能会写入成功,所以这里需要特殊处理。先判断out中是否只有一个对象,如果是对象,则直接写入通道。如果out中的对象不止一个,则分两种情况。第一种情况是传入的promise是voidPromise,然后调用writeVoidPromise方法。什么是voidPromise?我们知道Promise有多个状态,通过promise的状态变化我们可以知道数据写入的状态。对于voidPromise,它只关心一个失败状态,其他状态不关心。如果用户关心promise的其他状态,会调用writePromiseCombiner方法将多个对象的状态合并为一个promise和return。实际上,在writeVoidPromise和writePromiseCombiner中,out中的对象被一个一个取出来写入到channel中,所以会产生多个promise,需要合并promises:voidPromise=ctx.voidPromise();对于(inti=0;iextendsChannelDuplexHandlerMessageToMessageCodec继承自ChannelDuplexHandler,接收两个泛型参数:INBOUND_IN和OUTBOUND_IN。它定义了两个TypeParameterMatcher,用于过滤inboundMsg和outboundMsg:outboundMsgMatcher=TypeParameterMatcher.find(this,MessageToMessageCodec.class,"OUTBOUND_IN");}分别实现channelRead和write方法读写消息:@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)throwsException{decoder.channelRead(ctx,msg);}@Overridepublicvoidwrite(ChannelHandlerContextctx,Objectmsg,ChannelPromisepromise)throwsException{encoder.write(ctx,msg,promise);这里的decoder和encoder其实就是我们前面提到的MessageToMessageDecoder和MessageToMessageEncoder:rnMessageToMessageCodec.this.acceptOutboundMessage(msg);}@Override@SuppressWarnings("unchecked")protectedvoidencode(ChannelHandlerContextctx,Objectmsg,List