spring 当尝试从ForkJoinPool内的类路径加载资源时,Tomcat 10会抛出FileNotFoundException错误

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

我想分享一个从Java 8、Tomcat 8和Sping Boot 2升级到Java 17、Tomcat 10和Sping Boot 3后开始发生的问题。
问题是,现在我们不能再从类路径加载一些字体文件。
我们在src/main/resources中有一个fonts文件夹。当我们编译项目时,该文件夹被复制到WEB-INF/classes,这是Tomcat的正确文件夹。
查看Tomcatx 1 e0f1x,我看不到任何特定于资源更改的内容来解释这一点。
部署过程与以前一样:我们将项目编译为.war文件,然后将其保存到Tomcat webapps文件夹中。因此,在该文件夹中,我们有WEB-INF/classes文件夹,其中包含将加载到类路径的所有应用程序资源,包括fonts文件夹。
为了加载资源,我们使用Spring的ResouceLoader。示例:
String fontPath = resourceLoader.getResource(“classpath:fonts/font.ttf”).getFile().getPath();
ResouceLoader由Spring通过依赖注入提供:
private final ResourceLoader resourceLoader;
所以,在更新之前,这工作得很好。所以,我看了Tomcat 10文档,看看我们放置资源的方式是否改变了,看起来它仍然是一样的;也就是说,WEB-INF/classes下的文件应该是可用的。根据documentation
WebappX — A class loader is created for each web application that is deployed in a single Tomcat instance. All unpacked classes and resources in the /WEB-INF/classes directory of your web application… are made visible to this web application…
我创建了一个示例应用程序来演示这个问题:tomcat10-test
在它里面,我们有一个类AppStartupRunner来演示这个问题。
在第16行,我们实现了ApplicationRunner接口,因此这段代码将在启动时运行。在第28行,我们声明了一个Runnable,它试图加载资源:

  1. Runnable runnable = () -> {
  2. try {
  3. String fontPath = resourceLoader.getResource("classpath:fonts/font.ttf").getFile().getPath();
  4. logger.info("fontPath: {}", fontPath);
  5. } catch (IOException e) {
  6. logger.error("error load font thread", e);
  7. throw new RuntimeException(e);
  8. }
  9. };

字符串
在第40行,我们使用Thread运行它:

  1. new Thread(runnable).start(); // this works


在第44行,我们使用ExecutorService运行它:

  1. executorService.submit(runnable).get(); // this works


在第51行,我们使用ForkJoinPool运行它:

  1. THREAD_POOL.submit(runnable).get(); // Throws FileNotFoundException exception when running with Tomcat


如果我直接从IDE运行这段代码,它就可以工作。如果我通过命令./gradlew clean build -x test生成.war文件,然后将该文件部署到Tomcat 10,它就不能工作。
总结一下:
1.它与Java 8,Tomcat 8和Sping Boot 2一起工作。
1.更新到Java 17、Tomcat 10和Sping Boot 3后,它不再工作。
1.仅当尝试从ForkJoinPool内部的Thread加载资源以及使用Tomcat运行时才会发生此错误。
所以,我不知道是什么改变了,现在Tomcat在从ForkJoinPool运行线程时不再能够从classpath加载资源。
我尝试使用一些不同的方法来加载资源:使用Spring的ResourceUtils类,也使用getClass().getClassLoader(),但我得到了同样的问题。
预期的结果是,当应用程序部署到Tomcat 10并且线程在ForkJoinPool中运行时,能够加载classpath资源。

jtoj6r0c

jtoj6r0c1#

如果你尝试不显式声明runnable变量。由于字体加载逻辑是独立于lambda函数的,我们可以简单地将其直接传递给线程,如下所示:

  1. @Override
  2. public void run(ApplicationArguments args) {
  3. try {
  4. String fontPath = resourceLoader.getResource("classpath:fonts/font.ttf").getFile().getPath();
  5. logger.info("fontPath: {}", fontPath);
  6. logger.info("---> Load font using thread");
  7. new Thread(() -> loadFont(fontPath)).start();
  8. Thread.sleep(2000);
  9. logger.info("---> Load font using executor service");
  10. executorService.submit(() -> loadFont(fontPath));
  11. logger.info("---> Load font using fork join pool");
  12. THREAD_POOL.submit(() -> loadFont(fontPath));
  13. } catch (InterruptedException | IOException e) {
  14. logger.error("Error loading font", e);
  15. } finally {
  16. executorService.shutdown();
  17. THREAD_POOL.shutdown();
  18. }
  19. }
  20. private void loadFont(String fontPath) {
  21. try {
  22. logger.info("Loading font: {}", fontPath);
  23. } catch (Exception e) {
  24. logger.error("Error loading font", e);
  25. }
  26. }

字符串

展开查看全部

相关问题