HowTomcatWorks第三章第一部分就是StringManager工具类.Tomcat使用属性文件管理错误信息.并且把属性文件分散到各个包内,避免单个属性文件过于庞大.为了实现国际化,Tomcat为每个包配置多个不同语言版本的属性文件,它们都以LocalString开头,后面为相应语言的简写.(例如中文为LocalString_zh.properties)StringManager则可以根据传递进来的包名和locale,通过内置的ResourceBundle来加载特定包下的属性文件.
然而继第二章之后,我再次遇到了类加载的问题.下面是Tomcat7中StringManager的构造方法.
getBundle加载失败后为什么要用线程上下文加载器再加载一遍?
为了解决这个疑惑,我首先在网上找到这么一段.
[quote]为什么要引入线程的上下文类加载器?将它引入J2SE并不是纯粹的噱头,由于Sun没有提供充分的文档解释说明这一点,这使许多开发者很糊涂。实际上,上下文类加载器为同样在J2SE中引入的类加载代理机制提供了后门。通常JVM中的类加载器是按照层次结构组织的,目的是每个类加载器(除了启动整个JVM的原初类加载器)都有一个父类加载器。当类加载请求到来时,类加载器通常首先将请求代理给父类加载器。只有当父类加载器失败后,它才试图按照自己的算法查找并定义当前类。
有时这种模式并不能总是奏效。这通常发生在JVM核心代码必须动态加载由应用程序动态提供的资源时。拿JNDI为例,它的核心是由JRE核心类(rt.jar)实现的。但这些核心JNDI类必须能加载由第三方厂商提供的JNDI实现。这种情况下调用父类加载器(原初类加载器)来加载只有其子类加载器可见的类,这种代理机制就会失效。解决办法就是让核心JNDI类使用线程上下文类加载器,从而有效的打通类加载器层次结构,逆着代理机制的方向使用类加载器。[/quote]
通常在默认的情况下工程中src下的所有.java文件经过编译后都存放到bin/classes下,由类路径加载器加载.属性文件也是由类加载器加载到内存中.于是如果直接调用ResourceBundle.getBundle(packageName,locale).ResourceBundle会将调用者的classloader作为默认的classloader,也就是说这个属性文件也将由类路径加载器加载.然而我们可能面临的情况是,属性文件并不在classpath中,这样的话即使给出文件的全限定名,加载器也看不到这个文件.这样就导致了加载失败.
相对的,线程上下文加载器正可以在此时发挥作用.正如引用所说,如果线程上下文加载器默认为系统类加载器,但线程上下文加载器是可以手动指定的.于是我们就可以创建一个自定义类加载器,将属性文件所在的目录设置成参数.然后将这个加载器对象传递到getBundle(packageName, locale, cl)方法中.这时getBundle方法会
顺利加载属性文件,随后进行相应的处理.
这就是目前我对类上下文加载器应用的解读
然而继第二章之后,我再次遇到了类加载的问题.下面是Tomcat7中StringManager的构造方法.
private StringManager(String packageName, Locale locale) {
String bundleName = packageName + ".LocalString";
ResourceBundle bnd = null;
try {
bnd = ResourceBundle.getBundle(bundleName, locale);
} catch (MissingResourceException mre) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl != null) {
try {
bnd = ResourceBundle.getBundle(packageName, locale, cl);
} catch (MissingResourceException mre2) {
// ignore;
}
}
}
bundle = bnd;
if (bundle != null) {
this.locale = bundle.getLocale();
} else {
this.locale = null;
}
}
getBundle加载失败后为什么要用线程上下文加载器再加载一遍?
为了解决这个疑惑,我首先在网上找到这么一段.
[quote]为什么要引入线程的上下文类加载器?将它引入J2SE并不是纯粹的噱头,由于Sun没有提供充分的文档解释说明这一点,这使许多开发者很糊涂。实际上,上下文类加载器为同样在J2SE中引入的类加载代理机制提供了后门。通常JVM中的类加载器是按照层次结构组织的,目的是每个类加载器(除了启动整个JVM的原初类加载器)都有一个父类加载器。当类加载请求到来时,类加载器通常首先将请求代理给父类加载器。只有当父类加载器失败后,它才试图按照自己的算法查找并定义当前类。
有时这种模式并不能总是奏效。这通常发生在JVM核心代码必须动态加载由应用程序动态提供的资源时。拿JNDI为例,它的核心是由JRE核心类(rt.jar)实现的。但这些核心JNDI类必须能加载由第三方厂商提供的JNDI实现。这种情况下调用父类加载器(原初类加载器)来加载只有其子类加载器可见的类,这种代理机制就会失效。解决办法就是让核心JNDI类使用线程上下文类加载器,从而有效的打通类加载器层次结构,逆着代理机制的方向使用类加载器。[/quote]
通常在默认的情况下工程中src下的所有.java文件经过编译后都存放到bin/classes下,由类路径加载器加载.属性文件也是由类加载器加载到内存中.于是如果直接调用ResourceBundle.getBundle(packageName,locale).ResourceBundle会将调用者的classloader作为默认的classloader,也就是说这个属性文件也将由类路径加载器加载.然而我们可能面临的情况是,属性文件并不在classpath中,这样的话即使给出文件的全限定名,加载器也看不到这个文件.这样就导致了加载失败.
相对的,线程上下文加载器正可以在此时发挥作用.正如引用所说,如果线程上下文加载器默认为系统类加载器,但线程上下文加载器是可以手动指定的.于是我们就可以创建一个自定义类加载器,将属性文件所在的目录设置成参数.然后将这个加载器对象传递到getBundle(packageName, locale, cl)方法中.这时getBundle方法会
顺利加载属性文件,随后进行相应的处理.
这就是目前我对类上下文加载器应用的解读