前言
上一篇講了netty的一個入門的demo;項目上我也把數據處理做好了,就要開始存數據庫了;我用的mybatis框架,如果單獨使用還是覺得比較麻煩,所以就用了springboot+mybatis+netty;本篇主要講netty與springboot的整合,以及我在這個過程中遇到的問題,又是怎么去解決的;
正文
我在做springboot與netty整合的時候在谷歌,百度找了無數文章,都沒有一篇是自己想要的,也達不到自己所想的目的;
代碼
1. 新建一個springboot項目,在pom文件中添加netty依賴:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha1</version>
</dependency>
2.新建netty服務
其實可以復制上一篇文章的netty的三個服務類,做一些稍微的修改就行了;這里為了方便演示,且修都是改好了的,就直接貼出來了;
- DiscardServer類:
@Component
public class DiscardServer {
@Resource
private ChildChannelHandler childChannelHandler;
public void run(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
System.out.println("准備運行端口:" + port);
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childHandler(childChannelHandler);
//綁定端口,同步等待成功
ChannelFuture f = bootstrap.bind(port).sync();
//等待服務監聽端口關閉
f.channel().closeFuture().sync();
} finally {
//退出,釋放線程資源
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
- ChildChannelHandler類
@Component
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Resource
private DiscardServerHandler discardServerHandler;
public void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(discardServerHandler);
}
}
3.DiscardServerHandler類
特別注意DiscardServerHandler類上需要加@Sharable注解,如果不加的話會報錯;
@Component
@Sharable
public class DiscardServerHandler extends ChannelHandlerAdapter {
@Resource
private BaseService baseService;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
ByteBuf in = (ByteBuf) msg;
System.out.println("傳輸內容是");
System.out.println(in.toString(CharsetUtil.UTF_8));
//這里調用service服務
baseService.test();
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 出現異常就關閉
cause.printStackTrace();
ctx.close();
}
}
3.netty調用所需的服務類
1.BaseService接口
public interface BaseService {
/**
* 測試接口
*/
void test();
}
2.接口實現類BaseServiceImpl:
@Service
public class BaseServiceImpl implements BaseService {
@Override
public void test() {
System.out.println("調用service服務");
}
}
4 springboot啟動類
- 由於main方法是靜態方法,netty服務啟動類不是靜態類,在main方法里面需要用new的方式啟動;
- 也可以將netty服務啟動類改為靜態類,然后調用其他非靜態的類時就得用new方法來構造其他類了;
我也百度到了幾篇文章說實現CommandLineRunner接口,所以我用了springboot啟動類實現CommandLineRunner接口的run方法,然后在run方法里啟動netty服務
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Resource
private DiscardServer discardServer;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
discardServer.run(8080);
}
}
5.測試
寫一個能發送數據的socket就可以了;
發送的數據為:
public static void main(String[] args){
try {
Socket socket=new Socket("localhost",8080);
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter=new PrintWriter(outputStream);
printWriter.write("$tmb00035ET3318/08/22 11:5804029.94,027.25,20.00,20.00$");
printWriter.flush();
socket.shutdownOutput();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
我的測試結果:
傳輸內容是
$tmb00035ET3318/08/22 11:5804029.94,027.25,20.00,20.00$
aaaaa
到這里,netty與springboot的整合就完成了;
我在整合過程中遇到的問題
我使用springboot結合netty的流程
springboot啟動類中啟動netty啟動類(DiscardServer),netty啟動類(DiscardServer)再調用初始化channel類(ChildChannelHandler),然后初始化channel類再調用(DiscardServerHandler)類;然后DiscardServerHandler類再調用service服務;如下示例圖:
問題:
- springboot啟動類我並沒有實現CommandLineRunner接口,直接在main方法通過new的方式啟動netty服務
- 我實現了CommandLineRunner接口,但是我在run方法中用的new的方式啟動的netty服務或者我在run方法使用注入的方式啟動netty,但是在其他某個地方調用另一個類使用了new的方式;
- DiscardServerHandler類上為標記@Sharable類,會報錯誤;
以上總結起來的問題就是我在springboot整合netty的過程中有其中一處的調用其他類時使用的方式是new構造的,這樣雖然springboot能啟動,netty也能啟動,但是netty服務中使用new構造的那個類中無法依賴注入,會報空指針異常;
舉個栗子:在圖中的過程中,我在ChildChannelHandler類中通過new的方式調用DiscardServerHandler類,其他的過程都是使用注入的方式調用,就會出現上邊的問題;
在遇到空指針的時候,我把spring托管的bean打印了出來,所有的類都在spring的托管中,但是就是無法注入,我也一直沒有明白怎么回事,最后用了一個極端的方法,就是在調用服務時,獲取spring的上下文,然后再根據名字來獲取bean,你谷歌或百度:非托管類調用spring托管類,就能找到很多文章了;雖然用這個方式能解決上述的問題,但總是不好的;
最后的解決辦法:所以類之間的調用都使用spring的依賴注入,別用new的方式來調用或者靜態方法的方式調用
總結
既然項目中用到了spring,那么類與類之間的調用就用依賴注入,不然會報空指針的問題(就是非托管對象調用spring托管對象);這也算是一個常識性的問題了,只是自己現在才遇到這樣的問題,還是要踩坑才能遇漲記性啊;這些問題困擾了我兩三天,還是要有經驗的人帶,如果有經驗的人帶的話,說不幾分鍾就搞定了;
netty的文章到這里就告一段落了,接下來就是趕項目了;哈哈;
