springboot整合netty(二)

[TOC]java

前言

上一篇講了netty的一個入門的demo;項目上我也把數據處理作好了,就要開始存數據庫了;我用的mybatis框架,若是單獨使用仍是以爲比較麻煩,因此就用了springboot+mybatis+netty;本篇主要講netty與springboot的整合,以及我在這個過程當中遇到的問題,又是怎麼去解決的;git

正文

我在作springboot與netty整合的時候在谷歌,百度找了無數文章,都沒有一篇是本身想要的,也達不到本身所想的目的;github

代碼

1. 新建一個springboot項目,在pom文件中添加netty依賴:

<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>5.0.0.Alpha1</version>
		</dependency>
複製代碼

2.新建netty服務

其實能夠複製上一篇文章的netty的三個服務類,作一些稍微的修改就好了;這裏爲了方便演示,且修都是改好了的,就直接貼出來了;spring

  1. 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();
        }
    }
}

複製代碼
  1. 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註解,若是不加的話會報錯;bootstrap

@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接口springboot

public interface BaseService {
    /** * 測試接口 */
    void test();
}
複製代碼

2.接口實現類BaseServiceImpl:mybatis

@Service
public class BaseServiceImpl implements BaseService {
    @Override
    public void test() {
        System.out.println("調用service服務");
    }
}

複製代碼

4 springboot啓動類

  1. 因爲main方法是靜態方法,netty服務啓動類不是靜態類,在main方法裏面須要用new的方式啓動;
  2. 也能夠將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就能夠了;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服務;以下示例圖:

avatar

問題

  1. springboot啓動類我並無實現CommandLineRunner接口,直接在main方法經過new的方式啓動netty服務
  2. 我實現了CommandLineRunner接口,可是我在run方法中用的new的方式啓動的netty服務或者我在run方法使用注入的方式啓動netty,可是在其餘某個地方調用另外一個類使用了new的方式;
  3. DiscardServerHandler類上爲標記@Sharable類,會報錯誤;

以上總結起來的問題就是我在springboot整合netty的過程當中有其中一處的調用其餘類時使用的方式是new構造的,這樣雖然springboot能啓動,netty也能啓動,可是netty服務中使用new構造的那個類中沒法依賴注入,會報空指針異常;

舉個栗子:在圖中的過程當中,我在ChildChannelHandler類中經過new的方式調用DiscardServerHandler類,其餘的過程都是使用注入的方式調用,就會出現上邊的問題;

在遇到空指針的時候,我把spring託管的bean打印了出來,全部的類都在spring的託管中,可是就是沒法注入,我也一直沒有明白怎麼回事,最後用了一個極端的方法,就是在調用服務時,獲取spring的上下文,而後再根據名字來獲取bean,你谷歌或百度:非託管類調用spring託管類,就能找到不少文章了;雖然用這個方式能解決上述的問題,但老是很差的;

最後的解決辦法:因此類之間的調用都使用spring的依賴注入,別用new的方式來調用或者靜態方法的方式調用

總結

既然項目中用到了spring,那麼類與類之間的調用就用依賴注入,否則會報空指針的問題(就是非託管對象調用spring託管對象);這也算是一個常識性的問題了,只是本身如今才遇到這樣的問題,仍是要踩坑才能遇漲記性啊;這些問題困擾了我兩三天,仍是要有經驗的人帶,若是有經驗的人帶的話,說不幾分鐘就搞定了;

netty的文章到這裏就告一段落了,接下來就是趕項目了;哈哈;

GitHub項目地址:

相關文章
相關標籤/搜索