我维护一个applet,帮助用户将照片上传到我们的服务. applet jar文件有几个.properties文件:
>> jar -tf applet.jar | grep prop
res/messages.properties
res/messages_ca.properties
res/messages_es.properties
...
这些是在applet初始化期间加载的.
messages = ResourceBundle.getBundle("res.messages");
但是,在回退到.jar中包含的.properties文件之前,此调用会向服务器生成4到5个请求,以查找不在jar文件中的文件.
从服务器错误日志:
[error] File does not exist: /photos/res/messages.class
[error] File does not exist: /photos/res/messages_en.class
[error] File does not exist: /photos/res/messages_en.properties
[error] File does not exist: /photos/res/messages_en_US.class
[error] File does not exist: /photos/res/messages_en_US.properties
ResourceManager.getBundle的文档解释了这就是它的完成方式:
getBundle then iterates over the candidate bundle names to find the first one for which it can instantiate an actual resource bundle. For each candidate bundle name, it attempts to create a resource bundle:
First, it attempts to load a class using the candidate bundle name. If such a class can be found and loaded using the specified class loader, is assignment compatible with ResourceBundle, is accessible from ResourceBundle, and can be instantiated, getBundle creates a new instance of this class and uses it as the result resource bundle.
Otherwise, getBundle attempts to locate a property resource file. It generates a path name from the candidate bundle name by replacing all “.” characters with “/” and appending the string “.properties”. It attempts to find a “resource” with this name using ClassLoader.getResource.
我确信这样做是有充分理由的,但在我看来,对于我来说,对于已知在服务器上不存在的文件应该有五个失败的请求似乎是浪费的.
有没有办法教applet只在.jar文件中查找这些文件?
注意:我不是Java程序员,所以如果有更好的方法来加载属性而不是ResourceManager.getBundle,请告诉我.
解决方法:
Java 1.6引入了ResourceBundle.Control类,如果你不支持Java 1.4,它可能会提供一些帮助.就像你一样,编写自己的捆绑管理器并不是火箭科学.
此演示代码将捆绑加载限制为给定语言集中的属性文件:
public class CustomManager {
private static final Map BUNDLES = new HashMap();
public static ResourceBundle getBundle(String name, Set languages) {
Locale locale = Locale.getDefault();
if (languages.contains(locale.getLanguage())) {
name = name + "_" + locale.getLanguage();
}
synchronized (BUNDLES) {
ResourceBundle bundle = (ResourceBundle) BUNDLES.get(name);
if (bundle == null) {
ClassLoader loader = getContextClassLoader();
bundle = loadBundle(loader, name.replace('.', '/') + ".properties");
BUNDLES.put(name, BUNDLES);
}
return bundle;
}
}
private static ClassLoader getContextClassLoader() {
return Thread.currentThread().getContextClassLoader() != null ? Thread
.currentThread().getContextClassLoader() : CustomManager.class
.getClassLoader() != null ? CustomManager.class.getClassLoader()
: ClassLoader.getSystemClassLoader();
}
private static ResourceBundle loadBundle(ClassLoader loader, String res) {
try { InputStream in = loader.getResourceAsStream(res);
try { return new PropertyResourceBundle(in);
} finally { in.close(); }
} catch (IOException e) { throw new IllegalStateException(e.toString()); }
}
}
此代码模拟调用以检索西班牙语/西班牙语区域设置的字符串:
Set languages = new HashSet(Arrays.asList(new String[] { "es", "ca" }));
Locale.setDefault(new Locale("es", "ES"));
ResourceBundle bundle = CustomManager.getBundle("l10n.res.foo", languages);
System.out.println(bundle.getString("bar"));
由于语言集是es和ca而CustomManager仅支持语言(不是国家代码或变体),因此只能加载以下文件:
l10n/res/foo.properties
l10n/res/foo_es.properties
l10n/res/foo_ca.properties
您希望通过Locale和ClassLoader支持以及您希望管理语言列表的位置获得的复杂程度取决于您.
警告:我认为我没有违反实施的任何安全限制,但我只测试了桌面应用程序中的代码.