java 中 ChannelHandler的用法詳解
java 中 ChannelHandler的用法詳解
前言:
ChannelHandler處理一個I/O event或者攔截一個I/O操作,在它的ChannelPipeline中將其遞交給相鄰的下一個handler。
通過繼承ChannelHandlerAdapter來代替
因為這個接口有許多的方法需要實現(xiàn),你或許希望通過繼承ChannelHandlerAdapter來代替。
context對象
一個ChannelHandler和一個ChannelHandlerContext對象一起被提供。一個ChannelHander通過一個context對象和其所屬的那個ChannelPipeline進行交互。使用context對象,ChannelHandler可以向上或者向下傳遞events,動態(tài)地修改pipeline,或者存儲與handler相關(guān)的信息(使用AttributeKeys)。
狀態(tài)管理
一個ChannelHandler經(jīng)常需要存儲一些狀態(tài)相關(guān)的信息。最簡單和推薦的方法是使用成員變量:
public interface Message {
// your methods here
}
public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
private boolean loggedIn;
@Override
protected void messageReceived(ChannelHandlerContext ctx, Message message) {
Channel ch = e.getChannel();
if (message instanceof LoginMessage) {
authenticate((LoginMessage) message);
loggedIn = true;
} else (message instanceof GetDataMessage) {
if (loggedIn) {
ch.write(fetchSecret((GetDataMessage) message));
} else {
fail();
}
}
}
...
}
因為handler實例有一個狀態(tài)變量專注于一個連接,你必須為每一個handler實例創(chuàng)建一個新的handler實例,來避免競態(tài)的情況以至于未認證的客戶端可以獲得機密的信息:
// Create a new handler instance per channel.
// See ChannelInitializer.initChannel(Channel).
public class DataServerInitializer extends ChannelInitializer<Channel> {
@Override
public void initChannel(Channel channel) {
channel.pipeline().addLast("handler", new DataServerHandler());
}
}
使用AttributeKeys
雖然使用成員變量來保存一個handler的狀態(tài)是被推薦的,然而,由于一些原因你或許不想創(chuàng)建很多的handler實例。在這種情況下,你可以使用附在ChannelHandlerContext上的AttributeKeys:
public interface Message {
// your methods here
}
@Sharable
public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
private final AttributeKey<Boolean> auth =
AttributeKey.valueOf("auth");
@Override
protected void messageReceived(ChannelHandlerContext ctx, Message message) {
Attribute<Boolean> attr = ctx.attr(auth);
Channel ch = ctx.channel();
if (message instanceof LoginMessage) {
authenticate((LoginMessage) o);
attr.set(true);
} else (message instanceof GetDataMessage) {
if (Boolean.TRUE.equals(attr.get())) {
ch.write(fetchSecret((GetDataMessage) o));
} else {
fail();
}
}
}
...
}
現(xiàn)在handler的狀態(tài)被附在了ChannelHandlerContext上了,你可以添加同樣的Handler實例到不同的pipeline上:
public class DataServerInitializer extends ChannelInitializer<Channel> {
private static final DataServerHandler SHARED = new DataServerHandler();
@Override
public void initChannel(Channel channel) {
channel.pipeline().addLast("handler", SHARED);
}
}
@Sharable 注解
在上面使用AttributeKey的例子中,你應該已經(jīng)注意到了@Sharable注解。
如果一個ChannelHandler被注解為@Sharable,那意味著你可以只創(chuàng)建一個handler實例,并把它添加到一個或多個ChannelPipeline中多次,并不用考慮競態(tài)的情況。
如果這個注解沒有指定,你就只能為每次需要添加到pipeline中的handler,每次創(chuàng)建一個新的實例。因為它有非共享的狀態(tài),比如:成員變量。
如有疑問請留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
Spring 單元測試中如何進行 mock的實現(xiàn)
這篇文章主要介紹了Spring 單元測試中如何進行 mock的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12
java?SpringBoot注解@Async不生效的解決方法
大家好,本篇文章主要講的是java?SpringBoot注解@Async不生效的解決方法,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下2022-01-01
很多人竟然不知道Java線程池的創(chuàng)建方式有7種
本文主要介紹了很多人竟然不知道Java線程池的創(chuàng)建方式有7種,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-07-07
解決IDEA的maven項目中沒有新建Servlet文件的選項問題
這篇文章主要介紹了IDEA的maven項目中沒有新建Servlet文件的選項問題及解決方法,本文給大家分享問題原因就解決方法,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09

