`
seahb
  • 浏览: 69819 次
  • 性别: Icon_minigender_1
  • 来自: 0
文章分类
社区版块
存档分类
最新评论

我什么时候应该使用Thread.getContextClassLoader()?

阅读更多

这个问题经常出现在编写框架代码 , 需要动态加载很多类和资源的时候 . 通常当你需要动态加载资源的时候 , 你至少有三个 ClassLoader 可以选择 :

²        系统类加载器或叫作应用类加载器 (system classloader or application classloader)

²        当前类加载器

²        当前线程类加载器

上面的问题指的是最后一种类加载器 . 哪种类加载器是正确的选择呢 ?

第一种选择可以很容易地排除 : 系统类加载器 (system classloader). 这个类加载器处理 -classpath 下的类加载工作 , 可以通过 ClassLoader.getSystemClassLoader() 方法调用 . ClassLoader 下所有的 getSystemXXX() 的静态方法都是通过这个方法定义的 . 在你的代码中 , 你应该尽量少地调用这个方法 , 以其它的类加载器作为代理 . 否则你的代码将只能工作在简单的命令行应用中 , 这个时候系统类加载器 (system classloader) 是 JVM 最后创建的类加载器 . 一但你把代码移到 EJB, Web 应用或 Java Web Start 应用中 , 一定会出问题 .

      所以我们来看第二种选择 : 当前上下文环境下的类加载器 . 根据定义 , 当前类加载器就是你当前方法所属的类的加载器 . 在运行时类之间动态联编 , 及调用 Class.forName,() Class.getResource() 等类似方法时 , 这个类加载器会被隐含地使用 . It is also used by syntactic constructs like X.class class literals.

    线程上下文类型加载器是在Java 2平台上被引入的. 每一个线程都有一个类加载器与之对应(除非这个线程是被本地代码创建的). 这个类加载器是通过Thread.setContextClassLoaser()方法设置的. 如果你不在线程构造后调用这个方法, 这个线程将从它的父线程中继承相应的上下文类加载器. 如果在整个应用中你不做任何特殊设置, 所有的线程将都以系统类加载器(system classloader)作为自己的线程上下文类加载器. 自从Web和J2EE应用服务器使用成熟的类加载器机制来实现诸如JNDI, 线程池, 组件热部署等功能以来, 这种在整个应用中不做任何线程类加载器设置的情况就很少了.

    为什么线程上下文类加载器存在于如此重要的位置呢? 这个概念在J2SE中的引入并不引人注目. 很多开发人员对这一概念迷惑的原因是Sun公司在这方面缺乏适当的指引和文档.

    事实上, 上下文类加载器提供了类加载机制的后门, 这一点也在J2SE中被引入了. 通常, 在JVM中的所有类加载器被组织成了有继承层次的结构, 每一个类加载器(除了引导JVM的原始类加载器)都有一个父加载器. 每当被请示加载类时, 类加载器都会首先请求其父类加载器, 只有当父类加载器不能加载时, 才会自己进行类加载.

   有时候这种类加载的顺序安排不能正常工作, (此处的意思是:正常情况下都是从子类加载器到根类加载器请求,万一有根类里需要加载子类时,这种顺序就不能满足要求,就要有一条反向的通道,即得到子类加载器,这样就用到了thread context classloader,因为通过thread.getcontextclassloader()可以得到子类加载器).通常当必须动态加载应用程序开发人员提供的资源的时候. 以JNDI为例: 它的内容(从J2SE1.3开始)就在rt.jar中的引导类中实现了, 但是这些JNDI核心类需要动态加载由独立厂商实现并部署在应用程序的classpath下的JNDI提供者. 这种情况就要求一个父classloader(本例, 就是引导类加载器)去加载对于它其中一个子classloader(本例, 系统类加载器)可见的类. 这时通常的类加载代理机制不能实现这个要求. 解决的办法(workaround)就是, 让JNDI核心类使用当前线程上下文的类加载器, 这样, 就基本的类加载代理机制的相反方向建立了一条有效的途径.

    另外, 上面一段可能让你想起一些其它的事情: XML解析Java API(JAXP). 是的, 当JAXP只是J2SE的扩展进, 它很自然地用当前类加载器来引导解析器的实现. 而当JAXP被加入到J2SE1.4的核心类库中时, 它的类加载也就改成了用当前线程类加载器, 与JNDI的情况完全类似(也使很多程序员很迷惑). 明白为什么我说来自Sun的指导很缺乏了吧?

   在以上的介绍之后, 我们来看关键问题: 这两种选择(当前类加载器和当前线程类加载器)都不是在所有环境下都适用. 有些人认为当前线程类加载器应该成为新的标准策略. 但是, 如果这样, 当多个线程通过共享数据进行交互的时, 将会呈现出一幅极其复杂的类加载的画面, 除非它们全部使用了同一个上下文的类加载器. 进一步说, 在某些遗留下来的解决方案中, 委派到当前类加载器的方法已经是标准. 比如对Class.forName(String)的直接调用(这也是我为什么推荐尽量避免对这个方法进行调用的原因). 即使你努力去只调用上下文相关的类加载器, 仍然会有一些代码会不由你控制. 这种不受控制的类加载委派机制是混入是很危险的.

    更严重的问题, 某些应用服务器把环境上下文及当前类加载器设置到不同的类加载器实例上, 而这些类加载器有相同的类路径但却没有委派机制中的父子关系. 想想这为什么十分可怕. 要知道类加载器定义并加载的类实例会带有一个JVM内部的ID号. 如果当前类加载器加载一个类X的实例, 这个实例调用JNDI查找类Y的实例, 些时的上下文的类加载器也可以定义了加载类Y实例. 这个类Y的定义就与当前类加载器看到的类Y的定义不同. 如果进行强制类型转换, 则产生异常.

   这种混乱的情况还将在Java中存在一段时间. 对于那些需要动态加载资源的J2SE的API, 我们来猜想它们的类加策略. 例如:

Ø         JNDI 使用线程上下文类加载器

Ø         Class.getResource() 和Class.forName()使用当前类加载器

Ø         JAXP(J2SE 1.4 及之后)使用线程上下文类加载器

Ø         java.util.ResourceBundle 使用调用者的当前类加载器

Ø         URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only

Ø         Java 序列化API默认使用调用者当前的类加载器

这些类及资源的加载策略问题, 肯定是J2SE领域中文档最及说明最缺乏的部分了.

分享到:
评论

相关推荐

    Spring Boot读取resources目录文件方法详解

    使用getContextClassLoader().getResourceAsStream()可以自动管理文件路径,但需要使用Thread.currentThread().getContextClassLoader()方法来获取ClassLoader对象。希望本文能够对大家的学习和工作产生一定的参考...

    Hbase 基本操作类

    String krbStr=Thread.currentThread().getContextClassLoader().getResource("krb").getFile(); //获取用户票据hezhong路径(hezhong为给合众分配的用户配置文件) String keyStr=Thread.currentThread()...

    ThreadDemo3.rar

    `Thread.getContextClassLoader()`返回当前线程的上下文类加载器,可以使用`Thread.setContextClassLoader(ClassLoader cl)`来设置。上下文类加载器主要用于类的加载,尤其是在使用动态加载和插件系统时。 6. **...

    Java的ThreadContext类加载器的实现

    例如,在JDBC编程中,我们需要加载JDBC驱动程序时,可能需要使用Thread.currentThread().getContextClassLoader()获取的类加载器来加载驱动程序的类。这样可以确保正确地加载驱动程序的类。 Java中的类加载器具有...

    JDK8-Nashorn-BUG:Nashorn中的错误

    JDK版本: 1.8.0_131 错误说明: 当构造来获取Nashorn引擎时,发送给构造函数的ClassLoader不会用于构造... * 3: invokevirtual java/lang/Thread.getContextClassLoader:()Ljava/lang/ClassLoader; * 6: astore_0

    JAVA中如何得到文件路径.pdf

    2. **使用`Thread.currentThread().getContextClassLoader().getResource("")`**: - 获取当前线程上下文类加载器的资源路径。 - 示例代码: ```java System.out.println(Thread.currentThread()....

    IOUtils组件,搭配本文项目专用

    这段代码首先通过`Thread.currentThread().getContextClassLoader().getResource()`或`getResourceAsStream()`方法找到类路径下的资源,然后使用IOUtils的`toString()`方法将输入流转换为字符串,这样就能方便地读取...

    javaweb 读取 classes 下的文件

    这里,`Thread.currentThread().getContextClassLoader()`获取当前线程的类加载器,`getResourceAsStream()`方法用于找到资源并返回一个输入流。 2. **使用`ServletContext`** 在Servlet环境中,可以使用`...

    java读取项目中的文件(src目录下)

    例如,`Thread.currentThread().getContextClassLoader().getResourceAsStream("filename")`可以找到并打开`src`目录下的文件。这种方法允许你在打包后的JAR或WAR文件中正确地访问资源。 6. **处理资源路径**: - ...

    jdbc 资料大全

    InputStream inStream = Thread.currentThread() .getContextClassLoader().getResourceAsStream( "application.properties"); properties.load(inStream); dataSource = BasicDataSourceFactory....

    Java路径问题解决方案汇集

    Java路径中的空格问题 ...  Thread.currentThread().getContextClassLoader().getResource().getPath();等多种相似方式获得的路径,不能被FileReader()和FileWriter()直接应用,原因是URL对空格,特

    java或wed解决路径问题

    推荐使用`Thread.currentThread().getContextClassLoader().getResource("")`来获取当前classpath的绝对路径的URI表示。 在处理Web应用中的路径时,`HttpServletRequest`对象的`getRealPath("/")`或`getRealPath("/...

    Jar包中如何正确地加载资源文件博文中用到的测试项目源码

    你可以通过`Thread.currentThread().getContextClassLoader()`或`Class.getResourceAsStream()`来获取`ClassLoader`实例,然后使用`getResourceAsStream()`方法加载资源文件。例如: ```java InputStream in = ...

    java中相对路径与绝对路径的问题

    此外,我们还可以使用 `Thread.currentThread().getContextClassLoader().getResource("")` 来获取当前类的相对路径。 效劳器中的 Java 类获得当前路径 在效劳器中的 Java 类中,我们可以使用 `WebApplication 的...

    URLClass工程之间反射机制的实现

    1. 创建`URLClassLoader`对象:使用`URL[] urls`数组和父类加载器(通常是`Thread.currentThread().getContextClassLoader()`)构造一个新的`URLClassLoader`实例。 ```java URL[] classPathUrls = { new URL("file...

    在web和j2se中两个不同环境下 读取配置文件 备忘

    在上述代码中,我们使用了`Thread.currentThread().getContextClassLoader()`来获取当前线程的类加载器,然后通过`getResourceAsStream()`方法找到并读取配置文件。这种方法适用于动态加载的Web环境,因为它会根据...

    web中的路径问题

    - `Thread.currentThread().getContextClassLoader().getResource("/relative/path")`:获取当前线程上下文类加载器下的相对路径。 - 这种方式也适用于获取类路径下的资源。 - **通过系统属性获取路径**: - `...

    java路径获得几种方法.txt

    System.out.println(Thread.currentThread().getContextClassLoader().getResource("").getPath()); // 输出当前类加载器的路径 System.out.println(Thread.currentThread().getContextClassLoader().getResource...

    关于java和web工程项目中各种路径的获取总结借鉴.pdf

    通过`Thread.currentThread().getContextClassLoader()`获取当前线程的类加载器,然后使用`getResource("")`获取当前类加载器的资源路径。 2. **方法二** ```java String p1 = JdomParse.class .getClassLoader...

    JettyWebSocketTest2

    问题是当从@onOpen 方法和@onMessage 方法调用代码时, Thread.currentThread().getContextClassLoader() 是不同的。 在 onMessage 中,上下文类加载器将是 org.codehaus.plexus.classworlds.realm.ClassRealm 而...

Global site tag (gtag.js) - Google Analytics