背景:
使用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一直为空,可困扰我好久。
特此记录。。。。。。。。。。。。。。。。。
*那些浪费的时间,都是丢在真理路上的金子~~~~!*