一、前言
前面博客大部分介紹了基於EMQ中間件,通信協議使用的是MQTT,而傳輸的數據為純文本數據,采用JSON格式。這種方式,大部分一看就知道是熟悉Web開發、軟件開發的人喜歡用的方式。由於我也是做web軟件開發的,也是比較喜歡這種方式。阿里的物聯網平台,也是推薦這種方式。但是,但是做慣硬件開發,嵌入式開發就比較喜歡用裸TCP-Socket連接。采用的是二進制協議。基於此大部分應用場合為了兼容舊設備,就需要單獨開發一個TCP服務器的網關。這里使用以前學過的,也是比較流行的Netty框架。
話不多說,下面就開始了。
二、協議
| 定義 |
描述 |
|
| 啟動符‘@@’ (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.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 }

