点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者 | 陈凯玲

来源 | https://url.cn/5UWhBvB

一个spring boot开发的项目,spring boot版本是1.5.7,携带的spring版本是4.1.3。开发反馈,突然在本地启动不起来了,表象特征就是在本地IDEA上运行时,进程卡住也不退出,应用启动时加载相关组件的日志也不输出。症状如下图:

问题分析

因为没有有用的日志信息,所以不能从日志这个层面上排查问题。但是像这种没有输出日志的话,一般情况下,肯定是程序内部启动流程卡在什么地方了,只能通过打印下当前线程堆栈信息了解下。一般情况下,在服务器环境,我们会使用java工具包中的jstack 工具来查看:如jstack pid(应用java进程)。

但是,在IDEA本地开发的话,IDEA内置了一个工具,可以直接查看当前应用的线程上线文信息,如:

注意下面那个箭头指向的像照相机一样的图标,故图思意,就是打印当前线程快照的的意思。点击后,就出现了右边那些线程上下文信息了,可以看到有很多的线程,我们主要关注下main线程,线程状态确实是waiting的,接着点击箭头所指向的main线程,可以看到如下内容:

  1. "main@1" prio=5 tid=0x1 nid=NA waiting

  2. java.lang.Thread.State: WAITING

  3. at sun.misc.Unsafe.park(Unsafe.java:-1)

  4. at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)

  5. at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)

  6. at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)

  7. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)

  8. at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)

  9. at org.springframework.boot.autoconfigure.BackgroundPreinitializer.onApplicationEvent(BackgroundPreinitializer.java:63)

  10. at org.springframework.boot.autoconfigure.BackgroundPreinitializer.onApplicationEvent(BackgroundPreinitializer.java:45)

  11. at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)

  12. at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:158)

  13. at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)

  14. at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)

  15. at org.springframework.boot.context.event.EventPublishingRunListener.finished(EventPublishingRunListener.java:115)

  16. at org.springframework.boot.SpringApplicationRunListeners.callFinishedListener(SpringApplicationRunListeners.java:79)

  17. at org.springframework.boot.SpringApplicationRunListeners.finished(SpringApplicationRunListeners.java:72)

  18. at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:745)

  19. at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)

  20. at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134)

  21. - locked <0xea6> (a java.util.concurrent.atomic.AtomicBoolean)

  22. at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:175)

  23. at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:98)

  24. at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:64)

  25. at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)

  26. at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)

  27. at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)

  28. at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)

  29. at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74)

  30. at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)

  31. at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:325)

  32. at org.springframework.boot.SpringApplication.run(SpringApplication.java:296)

  33. at cn.keking.project.customerManagement.KekingCustomerManagement.main(KekingCustomerManagement.java:36)

可以看到是通过CountDownLatch.await()阻塞了线程,接着看下面那行,所在代码块如下:

  1. private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);

  2. @Override

  3. public void onApplicationEvent(SpringApplicationEvent event) {

  4. if (event instanceof ApplicationEnvironmentPreparedEvent) {

  5. if (preinitializationStarted.compareAndSet(false, true)) {

  6. performPreinitialization();

  7. }

  8. }

  9. if (event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) {

  10. try {

  11. preinitializationComplete.await();

  12. }

  13. catch (InterruptedException ex) {

  14. Thread.currentThread().interrupt();

  15. }

  16. }

  17. }

这是spring boot中的一个安全初始化资源的一个类,代码所示为监听SpringApplicationEvent事件,可以肯定的是,它的逻辑执行到了 preinitializationComplete.await();这里,导致了线程阻塞了。正常情况下,spring会触发ApplicationEnvironmentPreparedEvent事件完成资源的初始化,这里先不深究Spring为什么这么做,主要通过程序逻辑看下为什么卡这里了,在preinitializationComplete.await();所在行打个断点,看下event对象里的信息,如下:

原来event是一个Spring上下文初始化失败的异常事件对象,对象里包含了具体的异常信息,如箭头所指,关键异常信息如:

NoSuchMethodError:"org.springframework.util.ObjectUtils.unwrapOptional(Ljava/lang/Object;)Ljava/lang/Object;"

假设问题

通过上面的分析,基本定位到Spring boot应用启动卡住这个表象背后的真实原因了,而且也定位到了异常信息。

出现NoSuchMethodError异常,是因为调用方法的时候,找不到方法了。一般出现在两个有关联的jar包,但是版本对不上了,也就是常说的jar版本依赖冲突。查看了下,ObjectUtils是spring-core包里的一个类,当前的4.1.3版本确实没有这个unwrapOptional方法,spring-core-5.x的版本才新增了这个方法。因为之前的依赖是没有问题,为什么现在spring上下文会调用5.x的版本的方法呢?

所以先假设近期有开发在pom.xml里添加了新的的依赖,导致了这个问题。

小心求证

有了找问题的方向就好办了,因为代码都是git管理维护的,所以查看下pom.xml文件近期的提交记录即可,查看后,确实发现了近期对pom.xml有改动,添加了一个依赖:

  1. <dependency>

  2. <groupId>org.springframework</groupId>

  3. <artifactId>spring-context</artifactId>

  4. <version>5.1.6.RELEASE</version>

  5. </dependency>

这里还涉及到一点Maven依赖优先级的问题,在pom.xml里直接这样添加的依赖优先于其他jar中pom.xml依赖的,也就是说,即使spring boot1.5.7自带了Spring-context.4.1.3,但是这样指定后,应用最后依赖的还是5.1.6的版本。具体的Maven依赖关系,可以参考我的博文《关于Maven的使用,这些你都了解了么?》。结合之前的分析,八九不离十了就是因为加了这个依赖导致的问题,spring-context.5.1.6配合spring-core.4.1.3肯定得出问题啊。直接移除这个依赖,然后启动系统一切正常,日志打印了Spring加载上线文的信息。

问题总结

定位这个问题的关键在于要了解java中线程堆栈的知识,在没有足够异常日志情况下通过线程快照排查问题。在定位到问题后,如NoSuchMethodError这样的异常,需要平时的经验积累来假设问题的真实原因,然后在追本溯源验明问题所在根本原因。找问题本质一定要这种循序渐进的思路。举例,出现这种问题,如果你直接去搜索引擎搜:“Spring boot应用启动卡住了”,是搜不出来什么东西的,但是当你发现了是由于jar冲突。去搜索引擎搜索:

“NoSuchMethodError:"org.springframework.util.ObjectUtils.unwrapOptional(Ljava/lang/Object;)Ljava/lang/Object;"”。就会有很多的内容,很容易解决问题。

本文通过OpenWrite的Markdown转换工具发布

关注我,回复“加群”加入各种主题讨论群

  • IntelliJ IDEA 2019.3发布,诟病的2019.2版本终成过去式

  • Spring Cloud Hoxton发布,Spring Boot 2.2.x不再孤单

  • 聊聊前后端分离接口规范

  • Token ,Cookie、Session傻傻分不清楚?

  • 使用 LocalDateTime 而不是 Date

朕已阅 

记一次 Spring Boot 项目启动卡住问题排查记录相关推荐

  1. 记一次spring boot项目启动失败

    最近在macos下启动spring boot 项目的时候启动失败,报错信息如下: org.postgresql.util.PSQLException: 尝试连线已失败.at org.postgresq ...

  2. Spring Boot项目启动的几种方式

    Spring Boot项目启动的几种方式 方式一:右击启动或者点击intellij右上角的启动按钮 我们访问下浏览器看一下效果 方式二:利用maven启动 我们先进入到项目文件下,然后执行命令   m ...

  3. Whitelabel Error Page : spring boot项目启动后,无法访问@RequestMapping标注的请求

    springboot项目创建后,会自动生成一个标注有@SpringBootApplication注解的类,直接执行该类的main方法即可启动web项目,毕竟springboot已经内置了tomcat容 ...

  4. Spring Boot 项目启动原理彻底解剖分析

    文章目录 一.场景介绍 二.项目搭建 三.解体 JAR 包 四.原理分析 一.场景介绍 spring-boot 项目搭建以后启动方式一般有两种: 源码方式启动 @SpringBootApplicati ...

  5. Spring Boot项目启动流程

    概述 用过Spring Boot的应该都知道,在项目启动入口的主类main()方法里,一句简简单单的 SpringApplication.run( ... ); 便开启了项目的启动运行之路. 本文我们 ...

  6. 解决 idea 运行 Spring Boot 项目启动慢的问题

    同事win10启动Spring Boot工程只要3秒左右,我的启动要30秒.开始以为是CPU差距太大,后来才觉得不是这样! 解决方案 hostname命令查看自己的 hostname 注意修改 hos ...

  7. 2. Spring Boot项目启动原理初探

    SpringBoot从宏观上说,就是对spring容器进行了一层包装.它内部的入口是利用 SpringApplication类的static的 run 方法进行启动的,调用的图: 上图中的这些方法都位 ...

  8. Spring Boot相关知识(二) Spring Boot项目启动的执行顺序和原理

    2019独角兽企业重金招聘Python工程师标准>>> 1. 环境,程序入口 @SpringBootApplication public class Application {pub ...

  9. Spring Boot 项目 启动 端口经常被占用 彻底解决方案

    作为已经使用了21H1的用户,再等待win11推送前,先开启了hyper-v,捣鼓了一下开启hyper-v的virtualbox. 万万没想到,21h1更新后,开启本地开发的项目时,本地端口也遇到了占 ...

最新文章

  1. 提升 DevOps 效率,试试 ChatOps 吧!
  2. 查看python安装路径-Mac查看Python安装路径和版本
  3. 如何选购晨检机器人_扫地机器人好不好用?如何选购?答案在这
  4. 【django】项目开发准备【1】
  5. python爬虫 selenium模块的学习
  6. 一个“普通人”的秋招算法岗上岸心得
  7. pos机改造迷你打印机_小票打印机如何自动弹出钱箱
  8. JQuery选择器跨frame和层次选择
  9. Nginx-配置https虚拟服务(访问http时自动跳转https)
  10. 1.7 Go语言和其它编程语言的对比
  11. HTTP并发测试工具
  12. 看图写英语作文关于计算机,终于懂了看图写英语作文模板
  13. Mac上命令行安装证书p12文件及描述文件mobileprovision
  14. 深入理解TTL 与 CMOS 电路
  15. 百度 嵌入式Linux软件研发工程师面试记录
  16. 计算机关机键 自动重启,我的电脑总是关机后自动重启怎么办?
  17. Spring AOP 之 通知、连接点、切点、切面
  18. Excel 计算两个日期间相差的天数、月数或年数 DATEDIF函数
  19. @TableLogic注解
  20. mysql性能监控 调优_MySQL管理之道:性能调优、高可用与监控(第2版)

热门文章

  1. shellcode中变形bindshell的实现
  2. winsock setsockopt 详解
  3. COOKIE格式与读写相关
  4. 读秦小波《设计模式之禅》 -- 工厂模式
  5. 深入理解 C 指针阅读笔记 -- 第三章
  6. Linux2.6内核中链表的实现
  7. Spark编程基础(Python版)
  8. java实现请求发送_java实现响应重定向发送post请求操作示例
  9. linux 中断机制的处理过程
  10. js 浅拷贝直接赋值_js的浅拷贝和深拷贝的简单理解和使用方法