« | August 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | | | | |
| 公告 |
不得窥道门,不得悟佛门,不得入窄门,实乃破门。 |
Blog信息 |
blog名称:破门点滴 日志总数:161 评论数量:404 留言数量:-2 访问次数:1421442 建立时间:2004年11月13日 |

| |
[开发笔记]ClassLoader 类路径参考 网上资源, 软件技术 破门 发表于 2004/12/16 0:19:33 |
非常规(类)路径装入到 JVM 的类是由类装入器控制的。JVM 中构建了一个引导程序类装入器,它负责装入基本的 Java 类库类。这个特殊的类装入器有一些专门的特性。首先,它只装入在引导类路径上找到的类。因为这些是可信的系统类,所以引导程序装入器跳过了对常规(不可信)类所做的大量验证。
引导程序不是唯一的类装入器。对于初学者而言,JVM 为装入标准 Java 扩展 API 中的类定义了一个扩展类装入器,并为装入一般类路径上的类(包括应用程序类)定义了一个系统类装入器。应用程序还可以定义它们自己的用于特殊用途(例如运行时类的重新装入)的类装入器。这样添加的类装入器派生自 java.lang.ClassLoader 类(可能是间接派生的),该类对从字节数组构建内部类表示(java.lang.Class 实例)提供了核心支持。每个构造好的类在某种意义上是由装入它的类装入器所“拥有”。类装入器通常保留它们所装入类的映射,从而当再次请求某个类时,能通过名称找到该类。
每个类装入器还保留对父类装入器的引用,这样就定义了类装入器树,树根为引导程序装入器。在需要某个特定类的实例(由名称来标识)时,无论哪个类装入器最初处理该请求,在尝试直接装入该类之前,一般都会先检查其父类装入器。如果存在多层类装入器,那么会递归执行这一步,所以这意味着通常不仅在装入该类的类装入器中该类是可见的,而且对于所有后代类装入器也都是可见的。这还意味着如果一条链上有多个类装入器可以装入某个类,那么该树最上端的那个类装入器会是实际装入该类的类装入器。
在许多环境中,Java 程序会使用多个应用程序类装入器。J2EE 框架就是一个示例。该框架装入的每个 J2EE 应用程序都需要拥有一个独立的类装入器以防止一个应用程序中的类干扰其它应用程序。该框架代码本身也将使用一个或多个其它类装入器,同样用来防止对应用程序产生的或来自应用程序的干扰。整个类装入器集合形成了树状结构的层次结构,在其每个层次上都可装入不同类型的类。
装入器树作为类装入器层次结构的实际示例,图 1 显示了 Tomcat servlet 引擎定义的类装入器层次结构。这里 Common 类装入器从 Tomcat 安装的某个特定目录的 JAR 文件进行装入,旨在用于在服务器和所有 Web 应用程序之间共享代码。Catalina 装入器用于装入 Tomcat 自己的类,而 Shared 装入器用于装入 Web 应用程序之间共享的类。最后,每个 Web 应用程序有自己的装入器用于其私有类。
图 1. Tomcat 类装入器500)this.width=500'>
在这种环境中,跟踪合适的装入器以用于请求新类会很混乱。为此,在 Java 2 平台中将 setContextClassLoader 方法和 getContextClassLoader 方法添加到了 java.lang.Thread 类中。这些方法允许该框架设置类装入器,使得在运行每个应用程序中的代码时可以将类装入器用于该应用程序。
能装入独立的类集合这一灵活性是 Java 平台的一个重要特性。尽管这个特性很有用,但是它在某些情况中会产生混淆。一个令人混淆的方面是处理 JVM 类路径这样的老问题。例如,在图 1 显示的 Tomcat 类装入器层次结构中,由 Common 类装入器装入的类决不能(根据名称)直接访问由 Web 应用程序装入的类。使这些类联系在一起的唯一方法是通过使用这两个类集都可见的接口。在这个例子中,就是包含由 Java servlet 实现的 javax.servlet.Servlet。
无论何种原因在类装入器之间移动代码时都会出现问题。例如,当 J2SE 1.4 将用于 XML 处理的 JAXP API 移到标准分发版中时,在许多环境中都产生了问题,因为这些环境中的应用程序以前是依赖于装入它们自己选择的 XML API 实现的。使用 J2SE 1.3,只要在用户类路径中包含合适的 JAR 文件就可以解决该问题。在 J2SE 1.4 中,这些 API 的标准版现在位于扩展的类路径中,所以它们通常将覆盖用户类路径中出现的任何实现。
使用多个类装入器还可能引起其它类型的混淆。图 2 显示了类身份危机(class identity crisis)的示例,它是在两个独立类装入器都装入一个接口及其相关的实现时产生的危机。即使接口和类的名称和二进制实现都相同,但是来自一个装入器的类的实例不能被认为是实现了来自另一个装入器的接口。图 2 中通过将接口类 I 移至 System 类装入器的空间就可以解除这种混淆。类 A 仍然有两个独立的实例,但它们都实现了同一个接口 I。
图 2. 类身份危机500)this.width=500'>
结束语Java 类定义和 JVM 规范一起为运行时组装代码定义了功能极其强大的框架。通过使用类装入器,Java 应用程序能使用多个版本的类,否则这些类就会引起冲突。类装入器的灵活性甚至允许动态地重新装入已修改的代码,同时应用程序继续执行。
这里,Java 平台灵活性在某种程度上是以启动应用程序时较高的开销作为代价的。在 JVM 可以开始执行甚至最简单的应用程序代码之前,它都必须装入数百个独立的类。相对于频繁使用的小程序,这个启动成本通常使 Java 平台更适合于长时间运行的服务器类型的应用程序。服务器应用程序还最大程度地受益于代码在运行时进行组装这种灵活性,所以对于这种开发,Java 平台正日益受宠也就不足为奇了。 | |
|