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