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

问题描述 投票:0回答:1

我想分享一个从 Java 8、Tomcat 8 和 Spring Boot 2 升级到 Java 17、Tomcat 10 和 Spring Boot 3 后开始发生的问题。

问题是现在我们无法再从类路径加载某些字体文件。

我们在

fonts
中有一个
src/main/resources
文件夹。当我们编译项目时,该文件夹被复制到
WEB-INF/classes
,这是Tomcat的正确文件夹。

查看 Tomcat 迁移指南 我看不到任何特定于资源更改的信息来解释这一点。

部署就像以前一样完成:我们将项目编译为

.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
下的文件应该可用。根据文档

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
,它尝试加载资源:

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

在第 40 行,我们使用

Thread
:

运行它
new Thread(runnable).start(); // this works

在第 44 行,我们使用

ExecutorService
:

运行它
executorService.submit(runnable).get(); // this works

在第 51 行,我们使用

ForkJoinPool
:

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

如果我直接从 IDE 运行此代码,它就可以工作。 如果我通过命令生成

.war
文件:
./gradlew clean build -x test
然后将文件部署到Tomcat 10,不起作用。

总结一下:

  1. 它适用于 Java 8、Tomcat 8 和 Spring Boot 2。
  2. 更新到 Java 17、Tomcat 10 和 Spring Boot 3 后不再有效。
  3. 仅当尝试从
    Thread
    内部
    ForkJoinPool
    加载资源以及使用 Tomcat 运行时,才会发生该错误。

所以,我不知道发生了什么变化,现在 Tomcat 在从

classpath
运行线程时不再能够从
ForkJoinPool
加载资源。

我尝试过使用一些不同的方法来加载资源: 使用 Spring 的

ResourceUtils
类,也使用
getClass().getClassLoader()
,但我遇到了同样的问题。

预期结果是当应用程序部署到 Tomcat 10 并且线程在

classpath
内运行时能够加载
ForkJoinPool
资源。

java spring spring-boot tomcat tomcat10
1个回答
0
投票

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

@Override
    public void run(ApplicationArguments args) {

        try {

            String fontPath = resourceLoader.getResource("classpath:fonts/font.ttf").getFile().getPath();
            logger.info("fontPath: {}", fontPath);

            logger.info("---> Load font using thread");
            new Thread(() -> loadFont(fontPath)).start();

            Thread.sleep(2000);

            logger.info("---> Load font using executor service");
            executorService.submit(() -> loadFont(fontPath));

            logger.info("---> Load font using fork join pool");
            THREAD_POOL.submit(() -> loadFont(fontPath));

        } catch (InterruptedException | IOException e) {
            logger.error("Error loading font", e);
        } finally {
            executorService.shutdown();
            THREAD_POOL.shutdown();
        }

    }

    private void loadFont(String fontPath) {
        try {
            logger.info("Loading font: {}", fontPath);
        } catch (Exception e) {
            logger.error("Error loading font", e);
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.