在计划的作业执行期间,在某些ClassPathResources任务执行期间找不到ClassPathResources(而同时运行的其他任务找到相同的模板)。
在添加了一些跟踪之后,我发现当问题发生时,当前线程的ClassLoader与往常不一样:
org.springframework.boot.loader.LaunchedURLClassLoader
:找到资源,确定jdk.internal.loader.ClassLoaders$AppClassLoader
:资源notfound,出现问题
下面是在提供了catch邮件发送方法的Spring singleton bean上获取ClassLoader的输出,在出现问题的catch块中:
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?
那么,我们如何确保在所有新线程上始终使用相同的ClassLoader(此处为LaunchedURLClassLoader)?
在我的例子中,它发生在由@Scheduled
任务启动的@Retryable @Async
方法上。
如果有人有同样的问题,我很高兴听到你如何处理它。
OpenJDK 17、Sping Boot 2.7.17(Spring Framework 5.3.30)出现问题。
使用Sping Boot 默认值@EnableScheduling
和@EnableAsync
以及@EnableRetry
编辑:与ForkJoin池相关的问题:
- JDK-8184335: Release Note: Fork/Join common pool threads return the system class loader as their thread context class loader
- SO: CompletableFuture / ForkJoinPool Set Class Loader的
- Spring Boot: Java 11 and ForkJoinPool.commonPool() class loading issue #15737的
- Spring Boot: Asynchronous deserialization performed by Hazelcast may fail due to the wrong ClassLoader being used #24836的
1条答案
按热度按时间wyyhbhjk1#
这个问题是在添加了一个新的调度作业后出现的,该作业使用了并行流,最终在其终端forEach中启动了tasks。
由ForkJoinPool创建的线程(根据JDK-8184335,AppClassLoader作为上下文类加载器)最终使用ThreadPoolExecutor添加新线程以执行JQuery任务。
由于这些线程可能会被重用,因此应用程序触发的随后的TRAC任务将在这些线程上运行,从而导致某些任务由于线程上的contextClassLoader错误而失败。
替代解决方案:
@Async
方法)-从而确保为执行P2P方法创建的所有线程都将LaunchedURLClassLoader作为contextClassLoader