Vaadin 12部署Tomcat 9时ResourceBundle找不到资源包问题求助
问题背景
我在Vaadin 12项目里把多语言资源包labels_es.properties和labels_en.properties放在了\src\main\resources\目录下,然后在MyForm.kt里写了这段代码来加载资源:
val labels = ResourceBundle.getBundle("labels", utf8Control) companion object { val utf8Control = UTF8Control() }
结果把war包部署到Tomcat 9运行时,直接抛出了找不到资源包的错误,错误栈信息如下:
Vaadin 12 ResourceBundle.getBundle
Caused by: java.util.MissingResourceException: Can't find bundle for base name
Caused by: java.util.MissingResourceException: Can't find bundle for base name labels, locale es
at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1564)
at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1387)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:1299)
at com.myproject.MyForm.(MyFormt.kt:47)
... 43 common frames omitted
一步步解决问题
1. 先检查资源文件有没有被正确打包
Tomcat部署war包时,src/main/resources下的文件应该被自动打包到war的WEB-INF/classes目录里。你可以先把部署的war包解压开,看看WEB-INF/classes下面有没有labels_es.properties和labels_en.properties这两个文件:
- 如果找不到,那就是构建工具(Maven或者Gradle)没把资源文件复制进去。Maven默认会自动复制
src/main/resources下的所有文件,但如果你的pom.xml里自定义了resources配置,可能不小心排除了这些文件,得检查一下;Gradle的话,看看sourceSets.main.resources.srcDirs有没有包含src/main/resources,默认也是包含的。 - 另外要注意文件名的大小写,Java的ResourceBundle对大小写特别敏感,比如写成
Labels_es.properties就会找不到,必须严格是labels_es.properties这种格式。
2. 检查你的UTF8Control实现是否正确
你用了自定义的UTF8Control来读取UTF-8编码的properties文件,这个类如果写得有问题,也会导致找不到资源。给你一个经过验证的正确实现,你可以对比调整:
class UTF8Control : ResourceBundle.Control() { override fun newBundle(baseName: String, locale: Locale, format: String, loader: ClassLoader, reload: Boolean): ResourceBundle? { val bundleName = toBundleName(baseName, locale) val resourceName = toResourceName(bundleName, "properties") var bundle: ResourceBundle? = null var stream: InputStream? = null try { stream = if (reload) { loader.getResource(resourceName)?.openConnection()?.run { useCaches = false inputStream } } else { loader.getResourceAsStream(resourceName) } stream?.let { BufferedReader(InputStreamReader(it, Charsets.UTF_8)).use { reader -> bundle = PropertyResourceBundle(reader) } } } catch (e: IOException) { e.printStackTrace() } finally { stream?.close() } return bundle } }
如果你的UTF8Control没有正确处理类加载器或者资源流的读取,就可能出现找不到文件的情况。
3. 给ResourceBundle指定Web应用的类加载器
在Web容器里,每个应用都有自己的类加载器,直接调用不带类加载器的getBundle方法,可能会用系统类加载器去查找资源,而系统类加载器是找不到WEB-INF/classes里的文件的。你可以修改代码,指定当前类的类加载器:
// 加载默认Locale的资源 val labels = ResourceBundle.getBundle("labels", Locale.getDefault(), MyForm::class.java.classLoader, utf8Control) // 或者指定西班牙Locale val labels = ResourceBundle.getBundle("labels", Locale("es"), MyForm::class.java.classLoader, utf8Control)
这样就能确保用Web应用自己的类加载器去查找资源文件了。
4. 添加默认的资源文件
ResourceBundle的机制是,如果找不到对应Locale的资源文件,会去加载默认的labels.properties文件。如果你的项目里没有这个默认文件,当某些情况下找不到对应Locale的文件时,就会抛出异常。建议你添加一个labels.properties文件,哪怕里面是空的,也能避免这种情况发生。
最后总结
按照上面的步骤逐一排查:先确认资源文件是否被正确打包到war里,然后检查UTF8Control的实现,接着修改代码指定类加载器,最后添加默认资源文件。这样应该就能解决找不到资源包的问题了。
内容的提问来源于stack exchange,提问作者Alexei

