一 前言
springboot 如何集成netty實現mapper調用不為null的問題讓好多讀者都頭疼過,知識追尋者發了一點時間做了個基本入門集成應用給讀者們指明條正確的集成方式,我相信,只要你有netty入門應用知識和spring框架基本知識,這篇文章對你將收益終身。隨手點贊謝謝,如果是知識追尋者的忠粉記得分享喲。
二 pom.xml
來看看知識追尋者引入了哪些依賴
- netty-all 所有netty相關的包,知識追尋者這邊用的非發布版本,讀者可以自行更替
- postgresql驅動依賴,用什么數據庫都沒關系,讀者可以換成mysql驅動
- mybatis-spring-boot-starte 集成mybatis需要用到的啟動器
- druid-spring-boot-starte 當前主流性能較好的連接池,這篇文章沒用到
- spring-boot-starter-test 測試類,這篇文章應該沒用到測試
- lombok 開發神器,節省代碼開發量
<!-- springboot start 父類 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<!-- web start配置 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha1</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
</dependencies>
三 NettyServer
- 首先我們這邊使用的固定監聽端口8080
- 配置兩個線程組
parentGroup
,childGroup
- 注入childChannelHandler 具體可以看第四節
- 定義了空參init的方法,標注注解@PostConstruct,這邊需要對這個注解進行一個詳細介紹;在依賴注入完成初始化后,如果一個方法有該注解就將會被執行;那到底什么時候執行呢?是在類進入service之前,被該注解標注的方法會被執行,並且只會執行一次;在使用這個注解時還有一個注意點就是所有的類都必須支持依賴注入,否則會報錯,一般情況下僅支持空參;
- init方法中就是netty的輔助啟動,配置處理類,進行同步阻塞操作;
- 最后看下destory()方法,在該方法上面標注了注解@PreDestroy;@PreDestroy表示一種回調的信息通知機制,即當實例在容器中被移除的時候會做出通知;通常我們會用來做一些關閉資源句柄等操作;
/**
* @Author lsc
* <p>netty server 端 </p>
*/
@Component
public class NettyServer {
private Integer port = 8080;
// 配置線程組 實質是 reactor線程組
NioEventLoopGroup parentGroup = new NioEventLoopGroup();
// 配置線程組
NioEventLoopGroup childGroup = new NioEventLoopGroup();
@Autowired
ChildChannelHandler childChannelHandler;
@PostConstruct
public void init() throws Exception{
// 啟動 NIO
ServerBootstrap serverBootstrap = new ServerBootstrap();
//
serverBootstrap.group(parentGroup,childGroup)
.channel(NioServerSocketChannel.class)// 相當於 ServerSocketChannel
.option(ChannelOption.SO_BACKLOG,1024)//TCP參數 1024 個隊列
.childHandler(childChannelHandler);// 處理事件
// 綁定端口 同步阻塞等待同步成功 channelFuture 異步操作通知回調
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
// 同步阻塞等待服務監聽端口關閉
channelFuture.channel().closeFuture().sync();
}
@PreDestroy
public void destory() throws InterruptedException {
// 關閉資源
parentGroup.shutdownGracefully().sync();
childGroup.shutdownGracefully().sync();
}
}
四 ChildChannelHandler
- ChildChannelHandler 類繼承ChannelInitializer
- 注入NettyServerHandler,具體看第五節
- 實現initChannel 方法並且在方法用獲得pipeline注入nettyServerHandler;
/**
* @Author lsc
* <p> </p>
*/
@Component
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Autowired
NettyServerHandler nettyServerHandler;
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//
pipeline.addLast(nettyServerHandler);
}
}
五 NettyServerHandler
- NettyServerHandler 繼承ChannelHandlerAdapter
- 分別實現channelRead,channelReadComplete, exceptionCaught 方法
- 注入 NettyMapper ,自定義的mapper
- 重點在 channelRead 中 讀取監聽8080客戶端發送的數據,然后寫入zszxz-66666構建響應
- 如果mapper為空 會打印
can you believe that the mapper is null
- 最后執行回調處理;
/**
* @Author lsc
* <p> 處理類 </p>
*/
@Slf4j
@Component
public class NettyServerHandler extends ChannelHandlerAdapter {
@Autowired
NettyMapper mapper;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("-----handler-------");
// 轉為字節緩沖區
ByteBuf buf = (ByteBuf)msg;
// 字節數組
byte[] bytes = new byte[buf.readableBytes()];
// 緩沖區數據讀入字節數組
buf.readBytes(bytes);
// 編碼轉為字符串
String body = (new String(bytes, "UTF-8"));
System.out.println(" get the data from client : " + body);
// 構造響應數據
String responseData = "zszxz-66666";
// 數據寫入緩沖區
ByteBuf resp = Unpooled.copiedBuffer(responseData.getBytes());
// 寫入數據響應
ChannelFuture channelFuture = ctx.writeAndFlush(resp);
if (mapper==null){
System.out.println("can you believe that the mapper is null");
}
List<Map> user = mapper.getUser();
System.out.println(user);
// 回調處理
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()){
log.info("success");
}else {
log.error("error");
}
}
});
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// 寫入 seocketChannel
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 異常關閉資源句柄
ctx.close();
}
}
六 mapper
6.1 mapper接口
mapper接口中定義了一個查詢方法,獲得用戶;
@Mapper
@Repository
public interface NettyMapper {
List<Map> getUser();
}
6.2 mapper映射器
查詢所有學生
<mapper namespace="com.zszxz.netty.mapper.NettyMapper">
<select id="getUser" resultType="map">
select * from "student"
</select>
</mapper>
七 application.yml
spring:
datasource:
druid:
#本地
url: jdbc:postgresql://localhost:5432/springboot
username: postgres
password: 123456
driver-class-name: org.postgresql.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
mapUnderscoreToCamelCase: true
八 啟動類
/**
* @Author lsc
* <p> 知識追尋者netty系列</p>
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
九 執行結果
- 發送了知識追尋者好棒棒
- 響應 zszxz-66666
十 結語
最后祝賀讀者們新年快樂,預防流感病毒,出門帶口罩,不去人流量大的地方,吃熟肉,勤洗手;理性對待網上關於新型流感病毒,一起為中國加油,為武漢加油;過年也不要忘記學習喲,知識追尋者要貫徹每天都學習的方針,堅定不移的追隨讀者的步伐;