我想分享一个从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
,它试图加载资源:
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运行这段代码,它就可以工作。如果我通过命令./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
资源。
1条答案
按热度按时间jtoj6r0c1#
如果你尝试不显式声明runnable变量。由于字体加载逻辑是独立于lambda函数的,我们可以简单地将其直接传递给线程,如下所示:
字符串