大家好,我是杰哥
最近在项目中遇到一个特别奇怪的问题:程序中定时同步数据的操作,执行单元测试可以成功执行,直接启动主程序却无法正常执行(Spring Boot框架、java语言,IDEA开发,采用junit测试框架)
猜测
之前没有遇到过类似问题。根据这个现象,猜测是两者启动应该有什么参数的不同,或者是加载的类不同或者加载顺序有区别导致的。但是理论上来说,两者在启动过程中,Spring
所加载的东西又似乎没有什么区别呀,所以刚开始是有一点摸不着头脑的
对比不同之处
于是便试着开始问题的一步步排查过程
对于看起来比较奇怪
的问题,之前文章中提到过,有一个比较高效的问题排查方法:若存在正确的情况,包括对于偶发性的问题的成功场景,以及不同的运行方式产生正确的结果等,我们可以通过对比产生问题的方案与正确场景的不同之处,通过不同之处
入手,进行问题原因的定位分析
初步进展
对同步方法:save()
方法,进行 debug
方式定位
但进入了不知道多少层的调用,也没有发发现问题何在。只是发现:主程序启动的时候,执行了一次 save()
方法方法,以后就再也不执行了;而单元测试则每次都可以正常执行
日志打印
在 save()
方法执行时,捕获异常,打印异常日志,并打印成功日志
try{
save(data);
}catch(Exception e){
log.error("执行失败,{}",e.getMessage());
}
log.info("执行成功:{}",data);
复制代码
结果,并没有任何日志打印。没有失败日志,也没有成功日志。那就是说明,主程序在启动过程中,在第一次执行save方法时,便出现了异常
但是具体出现了什么异常,暂时还无从得知
一、调整日志级别
具体出现了什么异常呢?默认的日志级别是 info
,应该是没有看到打印出来的异常信息吧,于是我将日志级别调整至 debug
#slf4j设置日志级别
logging.level.root=debug
复制代码
首先启动单元测试,发现基本没有什么多余的报错日志
然后启动主程序,发现的确多了一个报错,而且在不断打印,应该是不断重试的效果
javax.management.InstanceNotFoundException: org.springframework.boot:type=Admin,name=SpringApplication
复制代码
因为没其他辙,所以就直接以这个报错为突破口来分析了
二、分析现象
先来分析一波:
日志提示找不到 Spring Boot
的主类:SpringApplication
那就说明是有什么东西在 Spring Boot
还没有完成类的初始化、自动装配等动作时,就已经想要去加载 Spring Boot
了
顺着这个猜测,脑袋里突然冒出一个大胆的想法,就是启动参数配置有差别
导致的
于是乎,分别看了下 IDEA
对于主程序和单元测试的启动参数配置是否有何不同
1、单元测试的启动配置
2、主程序的启动配置
可以看到,主程序启动比单元测试的启动默认是多了两项配置的,这也许就是问题的原因所在!
当前,对于我们最初的这个问题,目前发现两者的
三、问题解决
于是,采用如下解决步骤:
1) 在IDEA工具栏选择Run,在打开的列表中选中 Edit Configurations
2) 打开的面板左侧 Spring Boot 下选择需要运行的 Application
3) 去掉右侧 Enable launch optimization
、Enable JMX agent
的勾选
4) 重启服务即可!
try{
save(data);
}catch(Exception e){
log.error("执行失败,{}",e.getMessage());
}
log.info("执行成功:{}",data);
复制代码
重启服务,发现那个错误已经不存在了,而 save()
方法执行结束之后
打印出了执行成功的日志
啊哈,那么就是说,问题就这样完美解决了!
目前来看,问题的原因在于:开启了启动优化和 JMX 的代理功能之后,他们会在Spring Boot 完全启动之前就去加载 Spring Boot,顺序发生变化导致某些类或者配置无法正常加载导致的
而关掉了这两项配置,则成功解决问题!
四、总结
好了,问题是不难,问题的最根本原因其实也没有真正查出来,但是也算是多了一个比较奇怪的知识
回顾一下我们解决它的方式,猜测、定位、猜测、日志级别调整、分析、确认
1)先通过表面的现象来寻找具体的现象:save()
方法只进入了一次,并且在方法执行过程中出现问题
2)再考虑可能是什么原因导致这样的问题,猜测可能是过程中出现了问题。但是没有打印日志,换句话说,就是打印了日志,在当前的日志级别中无法看到
3)那么,就调低日志的级别
,试着对比
两者启动时日志的打印区别
还算顺利,我们调整到 debug
级别日志以后,立刻发现了其不同之处:使用主程序启动时,多打印了一个异常的堆栈信息,然后问题原因就落在了这个异常信息中
4)转向另一个明显问题的解决
通过解决这个异常,也就解决了问题本身
分享这个问题,主要是记录一下使用 IDEA 进行 Spring Boot 框架的 java 程序开发,会存在默认启动参数配置而导致奇怪问题出现的坑(当然其他框架或者使用编辑器可能也会存在类似问题);也想要分享给大家,遇到类似问题
的一种解决方案,避免浪费太多宝贵时间
还有一点,就是想要再次强调一下日志打印级别掌握
的重要性,工作中的确是可以通过 debug
的方式快速定位问题出现的地方,但是前提是你特别清楚了当前程序的执行过程和执行逻辑
当程序较为复杂时,通过调整日志级别来定位问题的效率还是更高一些的
嗯,就这样。每天学习一点,时间会见证你的强大~
欢迎大家关注们的公众号,一起持续性学习吧~