对于涉及到多语言用户使用的应用,国际化是一条必经之路。Tomcat也不例外,做为一款成熟且成功的开源软件,其用户量巨大,受到全球各国的工程师喜爱。
我们在阅读其源码时,一定见到过类似下面这样的内容:
throw new IllegalArgumentException( sm.getString("coyoteConnector.parseBodyMethodNoTrace") );
其中这句 sm.getString("coyoteConnector.parseBodyMethodNoTrace")
里的 sm ,就是实现国际化的核心 StringManager 。 在许多类中,我们都会发现这样的字段声明:
/**
* The string manager for this package.
* /
protected static final StringManager sm = StringManager.getManager(Connector.class);
此时,会得到一个当前Locale对应的StringManager。注意这里保证每个包只对应一个manager。单例的实现可以参考前面的文章:
来看看你貌似熟悉的单例模式
public static final synchronized StringManager getManager(
String packageName, Locale locale)
{
Map<Locale,StringManager> map = managers.get(packageName);
if (map == null) {
map = new LinkedHashMap<Locale,StringManager>(LOCALE_CACHE_SIZE, 1, true);
managers.put(packageName, map);
}
StringManager mgr = map.get(locale);
if (mgr == null) {
mgr = new StringManager(packageName, locale);
map.put(locale, mgr);
}
return mgr;
}
资源文件的载入是使用 ResourceBundle 实现的,文件是对应的 packageName +LocalStrings
private StringManager(String packageName, Locale locale) {
String bundleName = packageName + ".LocalStrings";
ResourceBundle bnd = null;
try {
bnd = ResourceBundle.getBundle(bundleName, locale);
} catch (MissingResourceException ex) {
}
bundle = bnd;
if (bundle != null) {
Locale bundleLocale = bundle.getLocale();
if (bundleLocale.equals(Locale.ROOT)) {
this.locale = Locale.ENGLISH;
} else {
this.locale = bundleLocale;
}
} else {
this.locale = null;
}
}
这块有两个可以关注的点:
ResourceBundle
MessageFormat
其中 ResourceBundle
会根据指定的Locale加载对应的资源文件信息,我们一般资源文件都是xxx_fr.properties/xxx_es.properties这种形式,bundle的locale会自动匹配。
而 MessageFormat
则会自动替换信息中的占位符,例如资源文件中有类似这样的内容:
Could not contact {0}:{1}. Tomcat may not be running.
其中 {0}:{1} 就是占位符,在实际使用时,会被真实的值替换。而背后的实现,正是MessageFormat.使用时,就直接传入实际值即可,如下:
log.error(sm.getString("catalina.stopServer.connectException",
s.getAddress(),
String.valueOf(s.getPort())));`
在需要信息提示时,调用方法 getString
来获取,注意catch块中的注释,比较有意思。
public String getString(String key) {
if (key == null){
String msg = "key may not have a null value";
throw new IllegalArgumentException(msg);
}
String str = null;
try {
// Avoid NPE if bundle is null and treat it like an MRE
if (bundle != null) {
str = bundle.getString(key);
}
} catch (MissingResourceException mre) {
//bad: shouldn't mask an exception the following way:
// str = "[cannot find message associated with key '" + key +
// "' due to " + mre + "]";
// because it hides the fact that the String was missing
// from the calling code.
//good: could just throw the exception (or wrap it in another)
// but that would probably cause much havoc on existing
// code.
//better: consistent with container pattern to
// simply return null. Calling code can then do
// a null check.
str = null;
}
return str;
}
以上,即为Tomcat国际化的实现方式,对于我们的应用开发,可以用来参考。
数据源连接池的原理及Tomcat中的应用
详解集群内Session高可用的实现原理
Java并发编程相关概念及注意事项
线程池的原理
修改JSP文件实时生效的秘密
扫描或长按下方二维码,共同成长!