spring contextClassLoader意外地在CNOOC方法上更改

mepcadol  于 2024-01-05  发布在  Spring
关注(0)|答案(1)|浏览(132)

在计划的作业执行期间,在某些ClassPathResources任务执行期间找不到ClassPathResources(而同时运行的其他任务找到相同的模板)。
在添加了一些跟踪之后,我发现当问题发生时,当前线程的ClassLoader与往常不一样:

  • org.springframework.boot.loader.LaunchedURLClassLoader:找到资源,确定
  • jdk.internal.loader.ClassLoaders$AppClassLoader:资源notfound,出现问题

下面是在提供了catch邮件发送方法的Spring singleton bean上获取ClassLoader的输出,在出现问题的catch块中:

  1. getClass().getClassLoader(): org.springframework.boot.loader.LaunchedURLClassLoader@17a7cec2
  2. currentThread().getContextClassLoader(): jdk.internal.loader.ClassLoaders$AppClassLoader@5cb0d902
  3. ClassUtils.getDefaultClassLoader(): jdk.internal.loader.ClassLoaders$AppClassLoader@5cb0d902
  4. 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池相关的问题:

wyyhbhjk

wyyhbhjk1#

这个问题是在添加了一个新的调度作业后出现的,该作业使用了并行流,最终在其终端forEach中启动了tasks。
由ForkJoinPool创建的线程(根据JDK-8184335,AppClassLoader作为上下文类加载器)最终使用ThreadPoolExecutor添加新线程以执行JQuery任务。
由于这些线程可能会被重用,因此应用程序触发的随后的TRAC任务将在这些线程上运行,从而导致某些任务由于线程上的contextClassLoader错误而失败。
替代解决方案:

  • 隔离并行流,使其不会创建可重用的线程(通过不使用共享ThreadPoolTaskExecutor启动@Async方法)-从而确保为执行P2P方法创建的所有线程都将LaunchedURLClassLoader作为contextClassLoader
  • 分解URL并使用其“自然”main方法而不是执行URL来启动应用程序,以摆脱LaunchedURLClassLoader(请参阅https://stackoverflow.com/a/77639527/1458562

相关问题