免费预览已结束,剩余1页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
线程上下文类加载器问题:何时使用Thread.getContextClassLoader()?这是一个很常见的问题,但答案却很难回答。这个问题通常在需要动态加载类和资源的系统编程时会遇到。总的说来动态加载资源时,往往需要从三种类加载器里选择:系统或说程序的类加载器、当前类加载器、以及当前线程的上下文类加载器。在程序中应该使用何种类加载器呢?系统类加载器通常不会使用。此类加载器处理启动应用程序时classpath指定的类,可以通过ClassLoader.getSystemClassLoader()来获得。所有的ClassLoader.getSystemXXX()接口也是通过这个类加载器加载的。一般不要显式调用这些方法,应该让其他类加载器代理到系统类加载器上。由于系统类加载器是JVM最后创建的类加载器,这样代码只会适应于简单命令行启动的程序。一旦代码移植到EJB、Web应用或者Java Web Start应用程序中,程序肯定不能正确执行。因此一般只有两种选择,当前类加载器和线程上下文类加载器。当前类加载器是指当前方法所在类的加载器。这个类加载器是运行时类解析使用的加载器,Class.forName(String)和Class.getResource(String)也使用该类加载器。代码中X.class的写法使用的类加载器也是这个类加载器。线程上下文类加载器在Java 2(J2SE)时引入。每个线程都有一个关联的上下文类加载器。如果你使用new Thread()方式生成新的线程,新线程将继承其父线程的上下文类加载器。如果程序对线程上下文类加载器没有任何改动的话,程序中所有的线程将都使用系统类加载器作为上下文类加载器。Web应用和Java企业级应用中,应用服务器经常要使用复杂的类加载器结构来实现JNDI(Java命名和目录接口)、线程池、组件热部署等功能,因此理解这一点尤其重要。为什么要引入线程的上下文类加载器?将它引入J2SE并不是纯粹的噱头,由于Sun没有提供充分的文档解释说明这一点,这使许多开发者很糊涂。实际上,上下文类加载器为同样在J2SE中引入的类加载代理机制提供了后门。通常JVM中的类加载器是按照层次结构组织的,目的是每个类加载器(除了启动整个JVM的原初类加载器)都有一个父类加载器。当类加载请求到来时,类加载器通常首先将请求代理给父类加载器。只有当父类加载器失败后,它才试图按照自己的算法查找并定义当前类。有时这种模式并不能总是奏效。这通常发生在JVM核心代码必须动态加载由应用程序动态提供的资源时。拿JNDI为例,它的核心是由JRE核心类(rt.jar)实现的。但这些核心JNDI类必须能加载由第三方厂商提供的JNDI实现。这种情况下调用父类加载器(原初类加载器)来加载只有其子类加载器可见的类,这种代理机制就会失效。解决办法就是让核心JNDI类使用线程上下文类加载器,从而有效的打通类加载器层次结构,逆着代理机制的方向使用类加载器。顺便提一下,XML解析API(JAXP)也是使用此种机制。当JAXP还是J2SE扩展时,XML解析器使用当前累加载器方法来加载解析器实现。但当JAXP成为J2SE核心代码后,类加载机制就换成了使用线程上下文加载器,这和JNDI的原因相似。好了,现在我们明白了问题的关键:这两种选择不可能适应所有情况。一些人认为线程上下文类加载器应成为新的标准。但这在不同JVM线程共享数据来沟通时,就会使类加载器的结构乱七八糟。除非所有线程都使用同一个上下文类加载器。而且,使用当前类加载器已成为缺省规则,它们广泛应用在类声明、Class.forName等情景中。即使你想尽可能只使用上下文类加载器,总是有这样那样的代码不是你所能控制的。这些代码都使用代理到当前类加载器的模式。混杂使用代理模式是很危险的。更为糟糕的是,某些应用服务器将当前类加载器和上下文类加器分别设置成不同的ClassLoader实例。虽然它们拥有相同的类路径,但是它们之间并不存在父子代理关系。想想这为什么可怕:记住加载并定义某个类的类加载器是虚拟机内部标识该类的组成部分,如果当前类加载器加载类X并接着执行它,如JNDI查找类型为Y的数据,上下文类加载器能够加载并定义Y,这个Y的定义和当前类加载器加载的相同名称的类就不是同一个,使用隐式类型转换就会造成异常。这种混乱的状况还将在Java中存在很长时间。在J2SE中还包括以下的功能使用不同的类加载器:l JNDI使用线程上下文类加载器l Class.getResource()和Class.forName()使用当前类加载器l JAXP使用上下文类加载器l java.util.ResourceBundle使用调用者的当前类加载器l URL协议处理器使用tocol.handler.pkgs系统属性并只使用系统类加载器。l Java序列化API缺省使用调用者当前的类加载器这些类加载器非常混乱,没有在J2SE文档中给以清晰明确的说明。该如何选择类加载器?如若代码是限于某些特定框架,这些框架有着特定加载规则,则不要做任何改动,让框架开发者来保证其工作(比如应用服务器提供商,尽管他们并不能总是做对)。如在Web应用和EJB中,要使用Class.gerResource来加载资源。在其他情况下,需要考虑使用下面的代码,这是作者本人在工作中发现的经验:public abstract class ClassLoaderResolver/* This method selects the best classloader instance to be used for* class/resource loading by whoever calls this method. The decision* typically involves choosing between the callers current, thread context,* system, and other classloaders in the JVM and is made by the link IClassLoadStrategy* instance established by the last call to link #setStrategy.* return classloader to be used by the caller null indicates the* primordial loader */public static synchronized ClassLoader getClassLoader ()final Class caller = getCallerClass (0);final ClassLoadContext ctx = new ClassLoadContext (caller);return s_strategy.getClassLoader (ctx);public static synchronized IClassLoadStrategy getStrategy ()return s_strategy;public static synchronized IClassLoadStrategy setStrategy (final IClassLoadStrategy strategy)final IClassLoadStrategy old = s_strategy;s_strategy = strategy;return old;/* A helper class to get the call context. It subclasses SecurityManager* to make getClassContext() accessible. An instance of CallerResolver* only needs to be created, not installed as an actual security* manager.*/private static final class CallerResolver extends SecurityManagerprotected Class getClassContext ()return super.getClassContext (); / End of nested class/* Indexes into the current method call context with a given* offset.*/private static Class getCallerClass (final int callerOffset) return CALLER_RESOLVER.getClassContext () CALL_CONTEXT_OFFSET +callerOffset;private static IClassLoadStrategy s_strategy; / initialized in private static final int CALL_CONTEXT_OFFSET = 3; / may need to change if this class is redesignedprivate static final CallerResolver CALLER_RESOLVER; / set in statictry/ This can fail if the current SecurityManager does not allow/ RuntimePermission (createSecurityManager):CALLER_RESOLVER = new CallerResolver ();catch (SecurityException se)throw new RuntimeException (ClassLoaderResolver: could not create CallerResolver: + se);s_strategy = new DefaultClassLoadStrategy (); / End of class.可通过调用ClassLoaderResolver.getClassLoader()方法来获取类加载器对象,并使用其ClassLoader的接口来加载类和资源。此外还可使用下面的ResourceLoader接口来取代ClassLoader接口:public abstract class ResourceLoader/* see java.lang.ClassLoader#loadClass(java.lang.String)*/public static Class loadClass (final String name)throws ClassNotFoundExceptionfinal ClassLoader loader = ClassLoaderResolver.getClassLoader (1);return Class.forName (name, false, loader);/* see java.lang.ClassLoader#getResource(java.lang.String)*/ public static URL getResource (final String name)final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);if (loader != null)return loader.getResource (name);elsereturn ClassLoader.getSystemResource (name);. more methods . / End of class决定应该使用何种类加载器的接口是IClassLoaderStrategy:public interface IClassLoadStrategyClassLoader getClassLoader (ClassLoadContext ctx); / End of interface为了帮助IClassLoadStrategy做决定,给它传递了个ClassLoadContext对象作为参数:public class ClassLoadContextpublic final Class getCallerClass ()return m_caller;ClassLoadContext (final Class caller)m_caller = caller;private final Class m_caller; / End of classClassLoadContext.getCallerClass()返回的类在ClassLoaderResolver或ResourceLoader使用,这样做的目的是让其能找到调用类的类加载器(上下文加载器总是能通过Thread.currentThread().getContextClassLoader()来获得)。注意调用类是静态获得的,因此这个接口不需现有业务方法增加额外的Class参数,而且也适合于静态方法和类初始化代码。具体使用时,可以往这个上下文对象中添加具体部署环境中所需的其他属性。上面代码看起来很像Strategy设计模式,其思想是将“总是使用上下文类加载器”或者“总是使用当前类加载器”的决策同具体实现逻辑分离开。往往设计之初是很难预测何种类加载策略是合适的,该设计能够让你可以后来修改类加载策略。这儿有一个缺省实现,应该可以适应大部分工作场景:public class DefaultClassLoadStrategy implements IClassLoadStrategypublic ClassLoader getClassLoader (final ClassLoadContext ctx)final ClassLoader callerLoader = ctx.getCallerClass ().getClassLoader ();final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader ();ClassLoader result;/ If callerLoader and contextLoader are in a parent-child/ relationship, always choose the child:if (isChild (contextLoader, callerLoader)result = callerLoader;else if (isChild (callerLoader, contextLoader)result = contextLoader;else/ This else branch could be merged into the previous one,/ but I show it here to emphasize the amb
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 药品购销员安全演练竞赛考核试卷含答案
- 标本保管员操作水平模拟考核试卷含答案
- 教育观念进化
- 《openEuler系统管理与服务部署》课件 项目一 -02-openEuler操作系统概述
- 2025湖北东风资产管理有限公司招聘2人笔试历年参考题库附带答案详解
- 雅安文投中医药大健康产业发展有限公司公开招聘综合(党群)部门负责人笔试排名及笔试历年参考题库附带答案详解
- 2025湖南邵阳市新宁县城镇农村建设投资有限公司招聘综合笔试历年参考题库附带答案详解
- 2025年山东泰山药业集团有限公司招聘(21人)笔试历年参考题库附带答案详解
- 2025四川绵阳市平武县兴帮农业发展集团有限公司招聘笔试综合笔试历年参考题库附带答案详解
- 心理咨询师实操技能与职业发展规划
- 2025年复合翼无人机气动外形报告
- 2025年中国上市公司高端制造业发展报告
- 2025年报关员《海关法规》真题解析
- 日式服务礼仪培训
- 上下游贸易合同范本
- (2025年)煤矿井下电工培训试题及答案
- 专题04 电化学原理的综合应用(高效培优讲义)(解析版)2026年高考化学一轮复习高效培优系列(全国通.用)
- 糖化血红蛋白教学课件
- 水果代采服务合同范本
- 2025年及未来5年市场数据中国船用螺旋桨轴行业发展监测及投资战略数据分析研究报告
- 皮沙发维修合同协议书
评论
0/150
提交评论