背景:
使用Springboot整合Netty寫了一個TCP實現客戶端服務端通信接收主板信息,然後需要將設備實時發送的檢測數據等關鍵信息存儲到數據庫,也是爲了能最快利用mybatis框架實現數據訪問,然後在TCP服務器消息處理時,需要寫數據庫,直接調用DAO層,編譯報錯。改爲調用Service層,編譯正常,運行到調用的地方,報空指針異常,跟蹤到異常位置,發現service爲空,也就是按照之前controller層通過
@Autowired注入service層失效。
解決方案:
1.不要用mybatis,使用原生的jdbc連接數據庫進行存儲
代碼如下:
blic class MessageHandler extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(MessageHandler.class);
/**
* 本方法用於讀取客戶端發送的信息
* @param ctx
* @param msg
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("輸出接收過來的信息: "+msg);
//將msg進行入庫操作
//基礎的jdbc連接操作,這裏省去基礎的連接方法
String str;
// 傳遞sql語句
Statement stt;
Connection conn = null;
String sql = "insert into test_db(datas) values ('"+msg+"')"; //寫SQL
try {
conn = mysqlimages.getConn(); //一個連接數據庫的方法,這就不貼了,很簡單的
//獲取Statement對象
stt = conn.createStatement();
//執行sql語句
stt.executeUpdate(sql);
logger.info(">>>插入數據庫成功");
str = Const.SECCESS;
}catch (Exception e) {
logger.error("<<<插入數據錯誤--"+e.getMessage());
str = Const.ERROR;
}
這種方法可以實現,但是不推薦,本來這裏數據量就大,再用jdbc沒有連接池將會造成業務阻塞netty本身的worker工作線程。
所以推薦使用下一種方法:
2.使用@PostConstruct靜態初始化spring的成員變量
代碼如下:
channelRead方法:
//調用線程池處理大數據量問題
ExecutorService executor = Executors.newFixedThreadPool(5);
int num=0;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println();
log.info("加載客戶端報文......");
log.info("【" + ctx.channel().id() + "】" + " :" + msg);
/**
* 下面可以解析數據,保存數據,生成返回報文,將需要返回報文寫入write函數
*/
num++;
System.out.println(num);//輸出當前已經接收過來的條數
//引入異步業務線程池的方式,避免長時間業務耗時業務阻塞netty本身的worker工作線程
executor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
log.info("收到服務端發來的方法請求了--------------------------------------------");
ServerHandler handler = new ServerHandler();//這個類在下面
handler.test(msg.toString());
return null;
}
});
//響應客戶端
this.channelWrite(ctx.channel().id(), msg);
}
@Component
public class ServerHandler extends IoHandlerAdapter {
@Autowired
private ITest2StaticService test2StaticService;// 注入service方法
private static ServerHandler serverHandler;
@PostConstruct //通過@PostConstruct實現初始化bean之前進行的操作
//在初始化的時候初始化靜態對象和它的靜態成員變量healthDataService,原理是拿到service層bean對象,靜態存儲下來,防止被釋放。
public void init() {
serverHandler = this;
serverHandler.test2StaticService = this.test2StaticService;
// 初使化時將已靜態化的testService實例化
}
//測試調用
public void test(String msg) {
Test2Static test2Static = new Test2Static();
test2Static.setBb(Double.valueOf(msg));
test2Static.setCreateTime(LocalDateTime.now());
System.out.println("1111111111111111");
boolean b = serverHandler.test2StaticService.save(test2Static);
System.out.println("---! "+b);
}
}
IoHandlerAdapter類 所用到的maven座標:
<!-- https://mvnrepository.com/artifact/org.apache.mina/mina-core -->
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.1.3</version>
</dependency>
2.說明:
將需要調用Spring的Service層的類通過@Component註解爲組件加載;
同樣通過@Autowired獲取Service層的Bean對象;
爲類聲明一個靜態變量,方便下一步存儲bean對象;
劃重點:通過註解@PostConstruct ,在初始化的時候初始化靜態對象和它的靜態成員變量healthDataService,原理是拿到service層bean對象,靜態存儲下來,防止被釋放。
找了好久,終於找到這個方法,本來以爲很簡單,卻總是怎麼也寫不進去數據庫。調用service一直爲空,可困擾我好久。
特此記錄。。。。。。。。。。。。。。。。。
*那些浪費的時間,都是丟在真理路上的金子~~~~!*