Spring Boot异步定时任务中上下文类加载器切换导致资源找不到的问题排查与解决咨询
Spring Boot异步定时任务中上下文类加载器切换导致资源找不到的问题排查与解决咨询
最近碰到一个挺棘手的问题,想跟大家请教下有没有类似的经历,以及你们是怎么解决的。
问题现象
在执行定时任务的时候,部分异步任务会找不到ClassPathResources,但同一时间运行的其他任务却能正常找到同一个模板。
排查细节
我加了一些日志跟踪后发现,出问题的时候,当前线程的类加载器和平时不一样:
- 正常情况:使用
org.springframework.boot.loader.LaunchedURLClassLoader,资源能正常找到 - 异常情况:切换成了
jdk.internal.loader.ClassLoaders$AppClassLoader,资源找不到
下面是出问题时,在捕获异常的代码块里,获取提供异步发邮件方法的Spring单例Bean的类加载器相关输出:
getClass().getClassLoader(): org.springframework.boot.loader.LaunchedURLClassLoader@17a7cec2 currentThread().getContextClassLoader(): jdk.internal.loader.ClassLoaders$AppClassLoader@5cb0d902 ClassUtils.getDefaultClassLoader(): jdk.internal.loader.ClassLoaders$AppClassLoader@5cb0d902 ClassPathResource.getClassLoader(): jdk.internal.loader.ClassLoaders$AppClassLoader@5cb0d902
核心疑问
- 为什么有时候类加载器会从Spring的
LaunchedURLClassLoader切换到JDK内部的AppClassLoader? - 怎么确保所有新创建的线程都始终使用同一个类加载器(这里就是
LaunchedURLClassLoader)?
问题场景
这个问题出现在标注了@Retryable、@Async的方法中,而这些方法是由@Scheduled定时任务触发的。
环境信息
- OpenJDK 17
- Spring Boot 2.7.17(对应Spring Framework 5.3.30)
- 使用Spring Boot默认配置,已开启
@EnableScheduling、@EnableAsync和@EnableRetry
补充发现
后来发现这个问题和ForkJoin池有关:
- 查过JDK相关问题记录,Fork/Join公共池线程会把系统类加载器作为线程上下文类加载器返回
- 也看到Spring Boot社区里有类似问题案例,比如异步反序列化因为类加载器错误导致失败的情况
如果有朋友遇到过同款问题,麻烦分享下你们的处理方案,非常感谢!
备注:内容来源于stack exchange,提问作者Patrice Blanchardie
相关产品推荐
相关产品推荐

