1、前言html
前面博客大部分介紹了基於EMQ中間件,通訊協議使用的是MQTT,而傳輸的數據爲純文本數據,採用JSON格式。這種方式,大部分一看就知道是熟悉Web開發、軟件開發的人喜歡用的方式。因爲我也是作web軟件開發的,也是比較喜歡這種方式。阿里的物聯網平臺,也是推薦這種方式。可是,可是作慣硬件開發,嵌入式開發就比較喜歡用裸TCP-Socket鏈接。採用的是二進制協議。基於此大部分應用場合爲了兼容舊設備,就須要單獨開發一個TCP服務器的網關。這裏使用之前學過的,也是比較流行的Netty框架。java
話很少說,下面就開始了。web
2、協議spring
定義apache |
描述bootstrap |
|
啓動符‘@@’緩存 (2字節)服務器 |
數據包的第1、2字節,爲固定值 64,64。網絡 |
|
控制單元架構
|
業務流水號 (2字節) |
數據包的第3、4字節。發送/確認模式下,業務流水號由發送端在發送新的數據包時按順序加一,確認方按發送包的業務流水號返回;請求/應答模式下,業務流水號由請求端在發送新的請求命令時按順序加一,應答方按請求包的業務流水號返回。低字節傳輸在前。業務流水號是一個2字節的正整數,由通訊雙方第一次創建網絡鏈接時肯定,初始值爲0。業務流水號由業務發起方(業務發起方指發送/確認模式下的發送端或者請求/應答模式下的請求端)獨立管理。業務發起方負責業務流水號的分配和回收,保證在業務存續期間業務流水號的惟一性。 |
協議版本號 (2字節) |
協議版本號包含主版本號(第5字節)和用戶版本號(第6字節)。主版本號爲固定值1,用戶版本號由用戶自行定義。 |
|
時間標籤 (6字節) |
數據包的第7~12字節,爲數據包發出的時間,具體定義表2。 |
|
源地址 (6字節) |
數據包的第13~18字節,爲數據包的源地址(監控中心或用戶信息傳輸裝置地址)。低字節傳輸在前。 |
|
目的地址 (6字節) |
數據包的第19~24字節,爲數據包的目的地址(監控中心或用戶信息傳輸裝置地址)。低字節傳輸在前。 |
|
應用數據單元長度 (2字節) |
數據包的第25、26字節,爲應用數據單元的長度,長度不該大於1024;低字節傳輸在前。 |
|
命令字節 (1字節) |
數據包的第27字節,爲控制單元的命令字節,具體定義見表3。 |
|
應用數據單元 (最大1024字節) |
應用數據單元,基本格式見表3,對於確認/否定等命令包,此單元可爲空。 |
|
校驗和 (1字節) |
控制單元中各字節數據(第3~第27字節)及應用數據單元的算術校驗和,捨去8位以上的進位位後所造成的1字節二進制數。 |
|
結束符‘##’ (2字節) |
爲固定值 35,35。 |
上面這個是本次須要處理的二進制數據格式。
3、代碼部分
3.0 Pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-parent</artifactId> 8 <version>2.1.1.RELEASE</version> 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.wunaozai.iot.nettyplatform</groupId> 12 <artifactId>NettyPlatform</artifactId> 13 <version>0.0.1-SNAPSHOT</version> 14 <name>IoTNettyPlatForm</name> 15 <description>基於自定義協議,使用Netty,物聯網通訊平臺</description> 16 17 <properties> 18 <java.version>1.8</java.version> 19 </properties> 20 21 <dependencies> 22 <dependency> 23 <groupId>org.springframework.boot</groupId> 24 <artifactId>spring-boot-starter</artifactId> 25 </dependency> 26 27 <!-- https://mvnrepository.com/artifact/io.netty/netty-all --> 28 <dependency> 29 <groupId>io.netty</groupId> 30 <artifactId>netty-all</artifactId> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework.boot</groupId> 34 <artifactId>spring-boot-configuration-processor</artifactId> 35 <optional>true</optional> 36 </dependency> 37 38 <!-- web項目必要的依賴 --> 39 <dependency> 40 <groupId>org.springframework.boot</groupId> 41 <artifactId>spring-boot-starter-web</artifactId> 42 </dependency> 43 44 <!-- 熱啓動devtools --> 45 <dependency> 46 <groupId>org.springframework.boot</groupId> 47 <artifactId>spring-boot-devtools</artifactId> 48 <optional>true</optional> 49 <scope>true</scope> 50 </dependency> 51 52 <dependency> 53 <groupId>org.springframework.boot</groupId> 54 <artifactId>spring-boot-starter-test</artifactId> 55 <scope>test</scope> 56 </dependency> 57 </dependencies> 58 59 <build> 60 <plugins> 61 <plugin> 62 <groupId>org.springframework.boot</groupId> 63 <artifactId>spring-boot-maven-plugin</artifactId> 64 <configuration> 65 <fork>true</fork> 66 </configuration> 67 </plugin> 68 </plugins> 69 </build> 70 71 </project>
3.1 SmartIotProtocol.java
這個主要對通訊協議模型進行簡單封裝
1 package com.wunaozai.iot.nettyplatform.code; 2 3 /** 4 * 自定義協議 5 * @author Administrator 6 * @see https://www.cnblogs.com/sidesky/p/6913109.html 7 */ 8 public class SmartIotProtocol { 9 10 /** 11 * 協議最短長度 30 字節 12 */ 13 public static int MIN_LEN = 30; 14 15 /** 16 * 數據包啓動符號 @@ 17 */ 18 public static short START = 25700; 19 20 /** 21 * 業務流水號 22 */ 23 private short flowid; 24 /** 25 * 主版本 26 */ 27 private byte version_major; 28 /** 29 * 次版本 30 */ 31 private byte version_minor; 32 /** 33 * 秒 34 */ 35 private byte second; 36 /** 37 * 分鐘 38 */ 39 private byte minute; 40 /** 41 * 小時 42 */ 43 private byte hour; 44 /** 45 * 日 46 */ 47 private byte day; 48 /** 49 * 月 50 */ 51 private byte month; 52 /** 53 * 年 54 */ 55 private byte year; 56 /** 57 * 數據包的源地址 58 */ 59 private byte[] src; 60 /** 61 * 數據包的目的地址 62 */ 63 private byte[] dest; 64 /** 65 * 應用數據單元長度 長度不該大於1024;低字節傳輸在前 66 */ 67 private short data_len; 68 /** 69 * 命令字節 爲控制單元的命令字節 70 */ 71 private byte cmd; 72 /** 73 * 應用數據單元 對於確認/否定等命令包,此單元可爲空 74 */ 75 private byte[] data; 76 /** 77 * 校驗和 控制單元中各字節數據(第3~第27字節)及應用數據單元的算術校驗和,捨去8位以上的進位位後所造成的1字節二進制數 78 */ 79 private byte checksum; 80 /** 81 * 協議結束符號 ## 82 */ 83 public static short END = 13621; 84 85 /** 86 * 打印調試信息 87 */ 88 public void printDebugInfo(){ 89 System.out.println("---------完整數據包開始------------"); 90 System.out.println("|開始標誌: " + printHexShort(START)); 91 System.out.println("|業務流水: " + printHexShort(flowid) + "\tFlowID:" + flowid); 92 System.out.println("|協議版本: " + printHexByte(version_major) + printHexByte(version_minor)); 93 System.out.println("|時間標籤: " + "20" + year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second); 94 System.out.println("|源地址 : " + printHexBytes(src)); 95 System.out.println("|目的地址: " + printHexBytes(dest)); 96 System.out.println("|數據長度: " + data_len); 97 System.out.println("|命令字節: " + printHexByte(cmd)); 98 System.out.println("|應用數據: " + printHexBytes(data)); 99 System.out.println("|校驗字節: " + printHexByte(checksum)); 100 System.out.println("|結束標誌: " + printHexShort(END)); 101 System.out.println("---------------------------------"); 102 } 103 private String printHexByte(byte b){ 104 return String.format("%02X", b); 105 } 106 private String printHexBytes(byte[] bytes){ 107 String str = ""; 108 for(int i=0; i<bytes.length; i++){ 109 str += String.format("%02X", bytes[i]); 110 } 111 return str; 112 } 113 private String printHexShort(int s){ 114 byte[] bytes = hexShort(s); 115 return printHexBytes(bytes); 116 } 117 private byte[] hexShort(int s){ 118 byte[] bytes = new byte[2]; 119 bytes[0] = (byte)((s << 24) >> 24); 120 bytes[1] = (byte)((s << 16) >> 24); 121 return bytes; 122 } 123 private byte[] hexInt(int n){ 124 byte[] bytes = new byte[4]; 125 bytes[3] = (byte) ((n ) >> 24); 126 bytes[2] = (byte) ((n << 8) >> 24); 127 bytes[1] = (byte) ((n << 16) >> 24); 128 bytes[0] = (byte) ((n << 24) >> 24); 129 return bytes; 130 } 131 132 public short getFlowid() { 133 return flowid; 134 } 135 public void setFlowid(short flowid) { 136 this.flowid = flowid; 137 } 138 public byte getVersion_major() { 139 return version_major; 140 } 141 public void setVersion_major(byte version_major) { 142 this.version_major = version_major; 143 } 144 public byte getVersion_minor() { 145 return version_minor; 146 } 147 public void setVersion_minor(byte version_minor) { 148 this.version_minor = version_minor; 149 } 150 public byte getSecond() { 151 return second; 152 } 153 public void setSecond(byte second) { 154 this.second = second; 155 } 156 public byte getMinute() { 157 return minute; 158 } 159 public void setMinute(byte minute) { 160 this.minute = minute; 161 } 162 public byte getHour() { 163 return hour; 164 } 165 public void setHour(byte hour) { 166 this.hour = hour; 167 } 168 public byte getDay() { 169 return day; 170 } 171 public void setDay(byte day) { 172 this.day = day; 173 } 174 public byte getMonth() { 175 return month; 176 } 177 public void setMonth(byte month) { 178 this.month = month; 179 } 180 public byte getYear() { 181 return year; 182 } 183 public void setYear(byte year) { 184 this.year = year; 185 } 186 public byte[] getSrc() { 187 return src; 188 } 189 public void setSrc(byte[] src) { 190 this.src = src; 191 } 192 public byte[] getDest() { 193 return dest; 194 } 195 public void setDest(byte[] dest) { 196 this.dest = dest; 197 } 198 public short getData_len() { 199 return data_len; 200 } 201 public void setData_len(short data_len) { 202 this.data_len = data_len; 203 } 204 public byte getCmd() { 205 return cmd; 206 } 207 public void setCmd(byte cmd) { 208 this.cmd = cmd; 209 } 210 public byte[] getData() { 211 return data; 212 } 213 public void setData(byte[] data) { 214 this.data = data; 215 } 216 public byte getChecksum() { 217 return checksum; 218 } 219 public void setChecksum(byte checksum) { 220 this.checksum = checksum; 221 } 222 223 }
3.2 SmartIotDecoder.java
解碼器,這個是本次的重點,這個解碼器最主要是解決TCP粘包拆包問題,若是有不清楚的,要重點理解一下。
1 package com.wunaozai.iot.nettyplatform.code; 2 3 import java.util.List; 4 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 8 import io.netty.buffer.ByteBuf; 9 import io.netty.channel.ChannelHandlerContext; 10 import io.netty.handler.codec.ByteToMessageDecoder; 11 12 /** 13 * 自定義協議解析 14 * @author Administrator 15 * 16 */ 17 public class SmartIotDecoder extends ByteToMessageDecoder { 18 19 20 private static final Logger log = LoggerFactory.getLogger(SmartIotDecoder.class); 21 22 @Override 23 protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception { 24 log.debug("啓動解碼器..."); 25 log.debug("目前數據緩存大小: " + buffer.readableBytes()); 26 // 刻度長度必須大於基本最小長度 27 if(buffer.readableBytes() >= SmartIotProtocol.MIN_LEN){ 28 log.debug("符合最小長度,進行解析"); 29 //防止socket字節流攻擊、客戶端傳來的數據過大,這裏須要對數據進行過濾掉 30 if(buffer.readableBytes() >= 4096){ 31 buffer.skipBytes(buffer.readableBytes()); 32 return ; 33 } 34 35 //記錄包頭開始位置 36 int beginReader = 0; 37 while(true){ 38 beginReader = buffer.readerIndex(); //記錄包頭開始位置 39 buffer.markReaderIndex(); //標記包頭開始index 40 //讀取協議開始標誌 41 if(buffer.readShort() == SmartIotProtocol.START){ 42 break; //若是是開始標記,那麼就結束查找 43 } 44 45 //若是找不到包頭,這裏要一個一個字節跳過 46 buffer.resetReaderIndex(); 47 buffer.readByte(); 48 49 //當跳事後,若是數據包又不符合長度的,結束本次協議解析 50 if(buffer.readableBytes() < SmartIotProtocol.MIN_LEN){ 51 return ; 52 } 53 } 54 55 short flowid = buffer.readShort(); 56 byte version_major = buffer.readByte(); 57 byte version_minor = buffer.readByte(); 58 byte second = buffer.readByte(); 59 byte minute = buffer.readByte(); 60 byte hour = buffer.readByte(); 61 byte day = buffer.readByte(); 62 byte month = buffer.readByte(); 63 byte year = buffer.readByte(); 64 byte[] src = new byte[6]; 65 src[0] = buffer.readByte(); 66 src[1] = buffer.readByte(); 67 src[2] = buffer.readByte(); 68 src[3] = buffer.readByte(); 69 src[4] = buffer.readByte(); 70 src[5] = buffer.readByte(); 71 byte[] dest = new byte[6]; 72 dest[0] = buffer.readByte(); 73 dest[1] = buffer.readByte(); 74 dest[2] = buffer.readByte(); 75 dest[3] = buffer.readByte(); 76 dest[4] = buffer.readByte(); 77 dest[5] = buffer.readByte(); 78 short data_len = buffer.readShort(); 79 if(buffer.readableBytes() < data_len + 4){ 80 //還原讀指針 81 buffer.readerIndex(beginReader); 82 return ; 83 } 84 byte cmd = buffer.readByte(); 85 byte[] data = null; 86 if(data_len > 0){ 87 //讀取應用數據單元 88 data = new byte[data_len]; 89 buffer.readBytes(data); 90 } 91 92 byte checksum = buffer.readByte(); 93 short end = buffer.readShort(); 94 95 if(end == SmartIotProtocol.END){ 96 log.debug("完成解析,並輸出."); 97 SmartIotProtocol iot = new SmartIotProtocol(); 98 iot.setFlowid(flowid); 99 iot.setVersion_major(version_major); 100 iot.setVersion_minor(version_minor); 101 iot.setSecond(second); 102 iot.setMinute(minute); 103 iot.setHour(hour); 104 iot.setDay(day); 105 iot.setMonth(month); 106 iot.setYear(year); 107 iot.setSrc(src); 108 iot.setDest(dest); 109 iot.setData_len(data_len); 110 iot.setCmd(cmd); 111 if(data_len > 0){ 112 iot.setData(data); 113 }else{ 114 iot.setData(null); 115 } 116 iot.setChecksum(checksum); 117 out.add(iot); 118 } 119 } 120 } 121 122 }
3.3 SmartIotEncoder.java
相對於解碼,這個編碼器,就相對簡單了,按照協議,一個byte一本byte進行發送便可。
1 package com.wunaozai.iot.nettyplatform.code; 2 3 import io.netty.buffer.ByteBuf; 4 import io.netty.channel.ChannelHandlerContext; 5 import io.netty.handler.codec.MessageToByteEncoder; 6 7 /** 8 * 自定義協議數據解析 9 * @author Administrator 10 * 11 */ 12 public class SmartIotEncoder extends MessageToByteEncoder<SmartIotProtocol> { 13 14 @Override 15 protected void encode(ChannelHandlerContext ctx, SmartIotProtocol msg, ByteBuf out) throws Exception { 16 //寫入消息SmartIot具體內容 17 out.writeShort(SmartIotProtocol.START); 18 out.writeShort(msg.getFlowid()); 19 out.writeByte(msg.getVersion_major()); 20 out.writeByte(msg.getVersion_minor()); 21 out.writeByte(msg.getSecond()); 22 out.writeByte(msg.getMinute()); 23 out.writeByte(msg.getHour()); 24 out.writeByte(msg.getDay()); 25 out.writeByte(msg.getMonth()); 26 out.writeByte(msg.getYear()); 27 out.writeBytes(msg.getSrc()); 28 out.writeBytes(msg.getDest()); 29 out.writeShort(msg.getData_len()); 30 out.writeByte(msg.getCmd()); 31 out.writeBytes(msg.getData()); 32 out.writeByte(msg.getChecksum()); 33 out.writeShort(SmartIotProtocol.END); 34 } 35 36 }
3.4 SmartIotHandler.java
這個是工程裏面的主要業務操做類,用戶Handler處理全部業務操做,這裏也能夠理解爲是一個入口、網關。全部命令都從這裏進行分發到子模塊。
1 package com.wunaozai.iot.nettyplatform.code; 2 3 import java.net.InetSocketAddress; 4 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 8 import io.netty.channel.ChannelHandlerContext; 9 import io.netty.channel.SimpleChannelInboundHandler; 10 11 /** 12 * 服務Handler 處理 13 * @author Administrator 14 * 15 */ 16 public class SmartIotHandler extends SimpleChannelInboundHandler<SmartIotProtocol> { 17 18 19 private static final Logger log = LoggerFactory.getLogger(SmartIotHandler.class); 20 21 @Override 22 protected void channelRead0(ChannelHandlerContext ctx, SmartIotProtocol iot) 23 throws Exception { 24 log.info("收到設備數據包: " + iot.getFlowid()); 25 iot.printDebugInfo(); 26 ctx.write("ok"); 27 } 28 29 @Override 30 public void channelActive(ChannelHandlerContext ctx) throws Exception { 31 InetSocketAddress socket = (InetSocketAddress) ctx.channel().remoteAddress(); 32 String ip = socket.getAddress().getHostAddress(); 33 log.info("收到客戶端IP: " + ip); 34 } 35 36 @Override 37 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 38 ctx.close(); 39 } 40 }
3.5 NettyServerInitializer.java
這個就是初始化本次Netty框架中,使用的編解碼器,還有對應的處理類。
1 package com.wunaozai.iot.nettyplatform.config; 2 3 import com.wunaozai.iot.nettyplatform.code.SmartIotDecoder; 4 import com.wunaozai.iot.nettyplatform.code.SmartIotEncoder; 5 import com.wunaozai.iot.nettyplatform.code.SmartIotHandler; 6 7 import io.netty.channel.ChannelInitializer; 8 import io.netty.channel.ChannelPipeline; 9 import io.netty.channel.socket.SocketChannel; 10 11 /** 12 * 服務器初始化 13 * @author Administrator 14 * 15 */ 16 public class NettyServerInitializer extends ChannelInitializer<SocketChannel> { 17 18 @Override 19 protected void initChannel(SocketChannel ch) throws Exception { 20 // ChannelPipeline pipeline = ch.pipeline(); 21 // //自定義切割符 22 // //ByteBuf delimiter = Unpooled.copiedBuffer(new byte[] {16}); 23 // ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes()); 24 // 25 // pipeline.addLast(new DelimiterBasedFrameDecoder(8192, delimiter)); 26 // pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); 27 // pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); 28 // pipeline.addLast(new NettyServerHandler()); 29 30 ChannelPipeline pipeline = ch.pipeline(); 31 //添加自定義編解碼器 32 pipeline.addLast(new SmartIotEncoder()); 33 pipeline.addLast(new SmartIotDecoder()); 34 //處理網絡IO 35 pipeline.addLast(new SmartIotHandler()); 36 } 37 38 }
3.6 NettyServer.java
Netty功能的入口類,全部Netty框架初始化步驟都在這裏進行簡單處理。
1 package com.wunaozai.iot.nettyplatform.config; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.stereotype.Component; 6 7 import io.netty.bootstrap.ServerBootstrap; 8 import io.netty.channel.ChannelFuture; 9 import io.netty.channel.ChannelOption; 10 import io.netty.channel.EventLoopGroup; 11 import io.netty.channel.nio.NioEventLoopGroup; 12 import io.netty.channel.socket.nio.NioServerSocketChannel; 13 import io.netty.handler.logging.LogLevel; 14 import io.netty.handler.logging.LoggingHandler; 15 16 /** 17 * Netty 服務器 18 * @author Administrator 19 * 20 */ 21 @Component 22 public class NettyServer { 23 24 private static final Logger log = LoggerFactory.getLogger(NettyServer.class); 25 26 private int port = 7777; 27 28 public void run(){ 29 EventLoopGroup bossGroup = new NioEventLoopGroup(); 30 EventLoopGroup workerGroup = new NioEventLoopGroup(); 31 try { 32 ServerBootstrap serverBootstrap = new ServerBootstrap(); 33 serverBootstrap.group(bossGroup, workerGroup); 34 serverBootstrap.channel(NioServerSocketChannel.class); 35 serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024); 36 serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); 37 serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true); 38 serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); 39 serverBootstrap.childHandler(new NettyServerInitializer()); 40 // 綁定端口,開始接收進來的鏈接 41 ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); 42 log.info("netty服務啓動: [port:" + port + "]"); 43 // 等待服務器socket關閉 44 channelFuture.channel().closeFuture().sync(); 45 } catch (Exception e) { 46 log.error("Netty 服務啓動失敗: " + e.getMessage()); 47 }finally { 48 bossGroup.shutdownGracefully(); 49 workerGroup.shutdownGracefully(); 50 } 51 } 52 }
3.7 IotNettyPlatFormApplication.java
這個是Spring Boot項目的入口函數。在這裏調用Netty的入口函數。
1 package com.wunaozai.iot.nettyplatform; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.boot.SpringApplication; 6 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 import org.springframework.context.annotation.ComponentScan; 8 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 10 import com.wunaozai.iot.nettyplatform.config.NettyServer; 11 12 @SpringBootApplication 13 public class IoTNettyPlatFormApplication { 14 15 private static final Logger log = LoggerFactory.getLogger(IoTNettyPlatFormApplication.class); 16 17 18 public static void main(String[] args) { 19 SpringApplication.run(IoTNettyPlatFormApplication.class, args); 20 run(); 21 } 22 23 private static NettyServer nettyServer = new NettyServer(); 24 25 private static void run(){ 26 Thread thread = new Thread(new Runnable() { 27 @Override 28 public void run() { 29 nettyServer.run(); 30 } 31 }); 32 thread.start(); 33 } 34 35 }
我這裏經過在@SpringBootApplication 這裏調用NettyServer。同時還有其餘方式:
1) 經過實現ApplicationListener
1 import org.slf4j.Logger; 2 import org.slf4j.LoggerFactory; 3 import org.springframework.context.ApplicationListener; 4 import org.springframework.context.event.ContextRefreshedEvent; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 項目初始化 9 * @author wunaozai 10 * @date 2018-05-24 11 */ 12 @Component 13 public class OnStartListener implements ApplicationListener<ContextRefreshedEvent> { 14 15 private static final Logger log = LoggerFactory.getLogger(OnStartListener.class); 16 17 @Override 18 public void onApplicationEvent(ContextRefreshedEvent arg0) { 19 log.info("Run on Start Listener."); 20 } 21 22 }
2) 經過實現CommandLineRunner
1 import org.slf4j.Logger; 2 import org.slf4j.LoggerFactory; 3 import org.springframework.boot.CommandLineRunner; 4 import org.springframework.core.annotation.Order; 5 import org.springframework.stereotype.Component; 6 7 /** 8 * 項目啓動時初始化資源<br> 9 * 如 一些初始化操做,提早加載加密證書,初始化線程池等 10 * @author wunaozai 11 * @date 2018-05-24 12 */ 13 @Component 14 @Order(value = 1) //執行順序 15 public class Runner implements CommandLineRunner { 16 17 private static final Logger log = LoggerFactory.getLogger(Runner.class); 18 19 @Override 20 public void run(String... args) throws Exception { 21 log.info("The Runner start to Initialize."); 22 } 23 24 }
3、協議測試
4、簡單架構
因爲引入了自定義協議,因此須要對原先的流程進行簡單的改造,下面這個圖是某項目的架構圖。
參考資料:
http://www.javashuo.com/article/p-vguedyda-bo.html