Netty 內置編解碼器
1. 前言
上節我們講解了 Netty 的內置編碼器以及自定義編碼器,本節主要講解 Netty 提供的幾個核心編解碼器的抽象類,主要是 MessageToByteEncoder、ByteToMessageDecoder、SimpleChannelInboundHandler。
2. 學習目的
Netty 官方也是考慮到了如何減輕開發人員的繁瑣、重復性的工作,因此,它內置了一些好用的編解碼器抽象,讓我們更加便捷的自定義自己想要的編解碼器。
通過本節學習,我們需要掌握以下幾點
- 有哪些編解碼器的抽象;
- 它們的核心原理是什么。
3. 類關系圖

4. MessageToByteEncoder
從字面意思上可知,它主要是把消息內容轉換成 Byte,也就是說是編碼。使用非常的簡單,繼承 MessageToByteEncoder 可以很容易的開發一個 Handler。
實例:
public class MyEncoder extends MessageToByteEncoder<BaseBean> {
protected void encode(ChannelHandlerContext channelHandlerContext,
BaseBean baseBean,
ByteBuf byteBuf) throws Exception {
//1.把“數據”轉換成字節數組
byte[] bytes= JSON.toJSONBytes(baseBean);
//2.把字節數組往ByteBuf容器寫
byteBuf.writeBytes(bytes);
}
}
ch.pipeline().addLast(new MyEncoder());
源碼:保留核心代碼
public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
//類型匹配器
private final TypeParameterMatcher matcher;
//構造函數
protected MessageToByteEncoder(boolean preferDirect) {
//初始化
this.matcher = TypeParameterMatcher.find(this, MessageToByteEncoder.class, "I");
this.preferDirect = preferDirect;
}
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
ByteBuf buf = null;
if (this.acceptOutboundMessage(msg)) {
//類型判斷通過,則處理
I cast = msg;
//創建ByteBuf
buf = this.allocateBuffer(ctx, msg, this.preferDirect);
//調用抽象方法(由子類實現)
this.encode(ctx, cast, buf);
} else {
//類型判斷不通過,則往下流轉
ctx.write(msg, promise);
}
}
//抽象方法
protected abstract void encode(ChannelHandlerContext var1, I var2, ByteBuf var3) throws Exception;
}
5. ByteToMessageDecoder
從字面上我們也很容易猜到它的作用,主要是把 Byte 類型的數據轉換成對應實體,也稱之為解碼。使用非常的簡單。
實例:
public class MyDecoder extends ByteToMessageDecoder {
//把ByteBuf反序列化,并且添加到List里面
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) {
//1.定義byte[],長度為ByteBuf可讀長度
byte[] bytes=new byte[byteBuf.readableBytes()];
//2.往byte[]讀取數據
byteBuf.readBytes(bytes);
//3.對象流
ByteArrayInputStream is=new ByteArrayInputStream(bytes);
ObjectInputStream iss=new ObjectInputStream(is);
User user=(User)iss.readObject();
//4.關閉流
is.close();
iss.close();
//5.添加到集合
list.add(user);
}
}
繼承了 ByteToMessageDecoder 這個類之后,我們只需要實現一下 decode () 方法,這里的 in 大家可以看到,傳遞進來的時候就已經是 ByteBuf 類型,所以我們不再需要強轉,第三個參數是 List 類型,我們通過往這個 List 里面添加解碼后的結果對象,就可以自動實現結果往下一個 handler 進行傳遞,我們就實現了解碼的邏輯 handler。
6. SimpleChannelInboundHandler
前面講解 ChannelHandler 多業務情況下的時候,我們講解到了 SimpleChannelInboundHandler,它的核心作用是自動判斷數據格式類型,并且轉發給對應的 Handler 來處理。
一般來說,Netty 開發的應用如果很復雜的時候,那么應該如何處理呢?通常有三種方案。
6.1 方案一
對反序列化后的結果進行類型判斷,不同的類型做不同的業務處理。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//對反序列化后的結果進行類型判斷,不同的類型做不同的業務處理
if(msg instanceof LoginReqBean){
login((LoginReqBean) msg,ctx.channel());
}else if(msg instanceof MsgReqBean){
sendMsg((MsgReqBean)msg,ctx.channel());
}
}
這種模式比較簡單,但是通過 if else 邏輯進行邏輯的處理,當我們要處理的指令越來越多的時候,代碼會顯得越來越臃腫。
6.2 方案二
判斷是否是自己應該處理,如果不是,則手工往下流轉。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof LoginReqBean){
//業務處理
}else{
//往下流轉
ctx.fireChannelRead(msg);
}
}
6.3 方案三
使用 SimpleChannelInboundHandler 來簡化我們的指令處理邏輯。
SimpleChannelInboundHandler 使用非常簡單,我們在繼承這個類的時候,給他傳遞一個泛型參數,然后在 channelRead0 () 方法里面,我們不用再通過 if 邏輯來判斷當前對象是否是本 handler 可以處理的對象,也不用強轉,不用往下傳遞本 handler 處理不了的對象,這一切都已經交給父類 SimpleChannelInboundHandler 來實現了,我們只需要專注于我們要處理的業務邏輯即可。
實例:
public class LoginReqHandler extends SimpleChannelInboundHandler<LoginReqBean> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, LoginReqBean loginReqBean){
//登錄邏輯
}
}
public class MsgReqHandler extends SimpleChannelInboundHandler<MsgReqBean> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MsgReqBean msgReqBean){
//消息發送邏輯
}
}
源碼:只保留核心部分
public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter {
//類型匹配器
private final TypeParameterMatcher matcher;
protected SimpleChannelInboundHandler(boolean autoRelease) {
//初始化類型匹配器
this.matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");
}
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (this.acceptInboundMessage(msg)) {
//類型校驗通過通過,則調用抽象方法(子類去實現)
this.channelRead0(ctx, msg);
} else {
//類型校驗不通過,則往下流轉
ctx.fireChannelRead(msg);
}
}
//抽象方法,由自定義業務類去實現
protected abstract void channelRead0(ChannelHandlerContext var1, I var2) throws Exception;
}
7. 小結
本節學習主要掌握以下知識點
- 基于 MessageToByteEncoder,我們可以實現自定義編碼,而不用關心 ByteBuf 的創建,不用每次向寫數據的時候,都手工進行編碼;
- 基于 ByteToMessageDecoder,我們可以實現自定義解碼,而不用關心 ByteBuf 的強轉和 解碼結果的傳遞;
- 基于 SimpleChannelInboundHandler,我們可以實現根據數據格式來判斷由哪個 Handler 去處理,不需要手工
if else判斷,不需要手動傳遞對象,做到了真正關心業務邏輯的處理; - 其實,這三種 Handler 也是有各自的應用場景,
ByteToMessageDecoder和MessageToByteEncoder是用來封裝解碼器和編碼器,SimpleChannelInboundHandler則是用于業務邏輯的簡化開發。
曼巴out ·
2025 imooc.com All Rights Reserved |