﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-菠萝三国-随笔分类-java</title><link>http://www.blogjava.net/boluobn/category/29160.html</link><description>大江东去,浪淘尽...</description><language>zh-cn</language><lastBuildDate>Sat, 26 Jan 2008 12:06:32 GMT</lastBuildDate><pubDate>Sat, 26 Jan 2008 12:06:32 GMT</pubDate><ttl>60</ttl><item><title>URLClassLoader加载class到当前线程类加载器（转）</title><link>http://www.blogjava.net/boluobn/archive/2008/01/26/177872.html</link><dc:creator>菠萝</dc:creator><author>菠萝</author><pubDate>Sat, 26 Jan 2008 01:51:00 GMT</pubDate><guid>http://www.blogjava.net/boluobn/archive/2008/01/26/177872.html</guid><wfw:comment>http://www.blogjava.net/boluobn/comments/177872.html</wfw:comment><comments>http://www.blogjava.net/boluobn/archive/2008/01/26/177872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/boluobn/comments/commentRss/177872.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/boluobn/services/trackbacks/177872.html</trackback:ping><description><![CDATA[我们知道，Java利用ClassLoader将类载入内存，并且在同一应用中，可以有很多个ClassLoader，通过委派机制，把装载的任务传递给上级的装载器的，依次类推，直到启动类装载器（没有上级类装载器）。如果启动类装载器能够装载这个类，那么它会首先装载。如果不能，则往下传递。当父类为null时，JVM内置的类(称为:bootstrap class loader)就会充当父类。想想眼下的越来越多用XML文件做配置文件或者是描述符、部署符。其实这些通过XML文档描述的配置信息最终都要变成Java类，基实都是通过ClassLoader来完成的。URLClassLoader是ClassLoader的子类，它用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。也就是说，通过URLClassLoader就可以加载指定jar中的class到内存中。 <br />
下面来看一个例子，在该例子中，我们要完成的工作是利用URLClassLoader加载jar并运行其中的类的某个方法。
<p>首先我们定义一个接口，使所有继承它的类都必须实现action方法，如下：</p>
<p>&nbsp; public&nbsp;&nbsp; interface&nbsp; ActionInterface&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp; String action();<br />
} <br />
完成后将其打包为testInterface.jar文件。</p>
<p>接下来新建一工程，为了编译通过，引入之前打好的testInterface.jar包。并创建TestAction类，使它实现ActionInterface接口。如下：</p>
<p><br />
&nbsp; public&nbsp;&nbsp; class&nbsp; TestAction&nbsp; implements&nbsp; ActionInterface&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp; String action()&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; " com.mxjava.TestAction.action " ;<br />
&nbsp;&nbsp;&nbsp; } <br />
} <br />
&nbsp;<br />
完成后将其打包为test.jar，放在c盘根目录下。下面要做的就是利用URLClassLoader加载并运行TestAction的action方法，并将返回的值打印在控制台上。</p>
<p>新建一工程，引入testInterface.jar包。并创建一可执行类（main方法），在其中加入如下代码：</p>
<p>&nbsp;URL url&nbsp; =&nbsp;&nbsp; new&nbsp; URL(&#8220;file:C: / test.jar&#8221;);<br />
URLClassLoader myClassLoader&nbsp; =&nbsp;&nbsp; new&nbsp; URLClassLoader( new&nbsp; URL[]&nbsp; { url } );<br />
Class myClass&nbsp; =&nbsp; myClassLoader.loadClass(&#8220;com.mxjava.TestAction&#8221;);<br />
ActionInterface action&nbsp; =&nbsp; (ActionInterface)myClass.newInstance();<br />
System.out.println(action.action()); <br />
　　在上面的例子中，首先利用URLClassLoader加载了C:\test.jar包，将其中的com.mxjava.TestAction类载入内存，将其强制转型为testInterface包中的ActionInterface类型，最后调用其action方法，并打印到控制台中。</p>
<p>　　执行程序后，在控制台上如期打印出我们想要的内容。但是，事情并没有那么简单，当我们将该代码移动web应用中时，就会抛出异常。原来，Java为我们提供了三种可选择的ClassLoader：<br />
1. 系统类加载器或叫作应用类加载器 (system classloader or application classloader)<br />
2. 当前类加载器<br />
3. 当前线程类加载器</p>
<p>　　在上例中我们使用javac命令来运行该程序，这时候使用的是系统类加载器 (system classloader)。这个类加载器处理 -classpath下的类加载工作，可以通过ClassLoader.getSystemClassLoader()方法调用。 ClassLoader 下所有的 getSystemXXX()的静态方法都是通过这个方法定义的。在代码中，应该尽量少地调用这个方法，以其它的类加载器作为代理。否则代码将只能工作在简单的命令行应用中。当在web应用中时，服务器也是利用ClassLoader来加载class的，由于ClassLoader的不同，所以在强制转型时JVM认定不是同一类型。（在JAVA中，一个类用其完全匹配类名(fully qualified class name)作为标识，这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此，如果一个名为Pg的包中，有一个名为Cl的类，被类加载器KlassLoader的一个实例kl1加载，Cl的实例，即C1.class在JVM中表示为(Cl, Pg, kl1)。这意味着两个类加载器的实例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的，被它们所加载的类也因此完全不同，互不兼容的。）为了能够使程序正确运行，我们首要解决的问题就是，如何将URLClassLoader加载的类，同当前ClassLoader保持在同一类加载器中。解决方法很简单，利用java提供的第三种ClassLoader—当前线程类加载器即可。jdk api文档就会发现，URLClassLoader提供了三种构造方式：</p>
<p>&nbsp;// 使用默认的委托父 ClassLoader 为指定的 URL 构造一个新 URLClassLoader。&nbsp; <br />
&nbsp;URLClassLoader(URL[] urls) <br />
// 为给定的 URL 构造新 URLClassLoader。&nbsp; <br />
URLClassLoader(URL[] urls, ClassLoader parent) <br />
// 为指定的 URL、父类加载器和 URLStreamHandlerFactory 创建新 URLClassLoader。 <br />
&nbsp;URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory)&nbsp; <br />
接下来要做的就是，在构造URLClassLoader时，将当前线程类加载器置入即可。如下：</p>
<p>URLClassLoader myClassLoader&nbsp; =&nbsp;&nbsp; new&nbsp; URLClassLoader( new&nbsp; URL[]&nbsp; { url } , Thread.currentThread().getContextClassLoader()); <br />
总结：<br />
　　Java是利用ClassLoader来加载类到内存的，ClassLoader本身是用java语言写的，所以我们可以扩展自己的ClassLoader。利用URLClassLoader可以加载指定jar包中的类到内存。在命行上利用URLClassLoader加载jar时，是使用系统类加载器来加载class的，所以在web环境下，就会出错。这是因为JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识的。我们只要利用URLClassLoader的第二种构造方法并传入当前线程类加载器即可解决。</p>
<img src ="http://www.blogjava.net/boluobn/aggbug/177872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/boluobn/" target="_blank">菠萝</a> 2008-01-26 09:51 <a href="http://www.blogjava.net/boluobn/archive/2008/01/26/177872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>了解Java ClassLoader</title><link>http://www.blogjava.net/boluobn/archive/2008/01/25/177748.html</link><dc:creator>菠萝</dc:creator><author>菠萝</author><pubDate>Fri, 25 Jan 2008 05:52:00 GMT</pubDate><guid>http://www.blogjava.net/boluobn/archive/2008/01/25/177748.html</guid><wfw:comment>http://www.blogjava.net/boluobn/comments/177748.html</wfw:comment><comments>http://www.blogjava.net/boluobn/archive/2008/01/25/177748.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/boluobn/comments/commentRss/177748.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/boluobn/services/trackbacks/177748.html</trackback:ping><description><![CDATA[<h2><a id="homepage1_HomePageDays_DaysList_ctl02_DayItem_DayList_ctl00_TitleUrl" href="http://www.cnblogs.com/leo-cnblogs/archive/2007/11/21/967379.html"></a>&nbsp;</h2>
<div class="postbody">【原文地址：https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/】<br />
1.介绍<br />
2.ClassLoader的结构<br />
3.Compiling ClassLoader<br />
4.java2 中ClassLoader的变动<br />
5.源代码<br />
---------------------------------------------------------------------------<br />
<br />
<strong>第一章 介绍</strong><br />
<br />
<font face="Verdana, Arial, Helvetica" size="4"><strong>什么是 ClassLoader<br />
<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p>在流行的商业化编程语言中，Java 语言由于在 Java 虚拟机 (JVM) 上运行而显得与众不同。这意味着已编译的程序是一种特殊的、独立于平台的格式，并非依赖于它们所运行的机器。在很大程度上，这种格式不同于传统的可执行程序格式。 </p>
<p>与 C 或 C++ 编写的程序不同，Java 程序并不是一个可执行文件，而是由许多独立的类文件组成，每一个文件对应于一个 Java 类。 </p>
<p>此外，这些类文件并非立即全部都装入内存，而是根据程序需要装入内存。ClassLoader 是 JVM 中将类装入内存的那部分。 </p>
<p>而且，Java ClassLoader 就是用 Java 语言编写的。这意味着创建您自己的 ClassLoader 非常容易，不必了解 JVM 的微小细节。 <br />
</p>
</font><font face="Verdana, Arial, Helvetica" size="4"><strong>为什么编写 ClassLoader?</strong></font><br x="7" />
<br x="7" />
<font face="Verdana, Arial, Helvetica" size="2">
<p>如果 JVM 已经有一个 ClassLoader，那么为什么还要编写另一个呢？问得好。缺省的 ClassLoader 只知道如何从本地文件系统装入类文件。不过这只适合于常规情况，即已全部编译完 Java 程序，并且计算机处于等待状态。 </p>
<p>但 Java 语言最具新意的事就是 JVM 可以非常容易地从那些非本地硬盘或从网络上获取类。例如，浏览者可以使用定制的 ClassLoader 从 Web 站点装入可执行内容。 </p>
<p>有许多其它方式可以获取类文件。除了简单地从本地或网络装入文件以外，可以使用定制的 ClassLoader 完成以下任务： </p>
<ul>
    <li>在执行非置信代码之前，自动验证数字签名
    <li>使用用户提供的密码透明地解密代码
    <li>动态地创建符合用户特定需要的定制化构建类 </li>
</ul>
任何您认为可以生成 Java 字节码的内容都可以集成到应用程序中。</font><font face="Verdana, Arial, Helvetica" size="2">
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>定制 ClassLoader 示例</strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>如果使用过 JDK 或任何基于 Java 浏览器中的 Applet 查看器，那么您差不多肯定使用过定制的 ClassLoader。 </p>
<p>Sun 最初发布 Java 语言时，其中最令人兴奋的一件事是观看这项新技术是如何执行在运行时从远程的 Web 服务器装入的代码。（此外，还有更令人兴奋的事 -- Java 技术提供了一种便于编写代码的强大语言。）更一些令人激动的是它可以执行从远程 Web 服务器通过 HTTP 连接发送过来的字节码。 </p>
<p>此项功能归功于 Java 语言可以安装定制 ClassLoader。Applet 查看器包含一个 ClassLoader，它不在本地文件系统中寻找类，而是访问远程服务器上的 Web 站点，经过 HTTP 装入原始的字节码文件，并把它们转换成 JVM 内的类。 </p>
<p>浏览器和 Applet 查看器中的 ClassLoaders 还可以做其它事情：它们支持安全性以及使不同的 Applet 在不同的页面上运行而互不干扰。 </p>
<p>Luke Gorrie 编写的 Echidna 是一个开放源码包，它可以使您在单个虚拟机上运行多个 Java 应用程序。（请参阅<a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/j-classloader-7-3.shtml">进一步了解和参考资料</a>。）它使用定制的 ClassLoader，通过向每个应用程序提供该类文件的自身副本，以防止应用程序互相干扰。 <br />
</p>
<br />
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>我们的 ClassLoader 示例</strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>了解了 ClassLoader 如何工作以及如何编写 ClassLoader 之后，我们将创建称作 CompilingClassLoader (CCL) 的 Classloader。CCL 为我们编译 Java 代码，而无需要我们干涉这个过程。它基本上就类似于直接构建到运行时系统中的 "make" 程序。 </p>
<p><strong>注：</strong>进一步了解之前，应注意在 JDK 版本 1.2 中已改进了 ClassLoader 系统的某些方面（即 Java 2 平台）。本教程是按 JDK 版本 1.0 和 1.1 写的，但也可以在以后的版本中运行。</p>
<p><a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/j-classloader-5-1.shtml">Java 2 中 ClassLoader 的变动</a>描述了 Java 版本 1.2 中的变动，并提供了一些详细信息，以便修改 ClassLoader 来利用这些变动。 </p>
</font>
<p>&nbsp;</p>
<p><br />
</p>
</font>
<p>&nbsp;</p>
<p>------------------------------------------------------------------------------------------------------<br />
</p>
</font><strong>第二章.ClassLoader的结构</strong><br />
<br />
<font face="Verdana, Arial, Helvetica" size="4"><strong style="background-color: highlight"><br />
<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p>ClassLoader 的基本目标是对类的请求提供服务。当 JVM 需要使用类时，它根据名称向 ClassLoader 请求这个类，然后 ClassLoader 试图返回一个表示这个类的 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Class</code> 对象。 </p>
<p>通过覆盖对应于这个过程不同阶段的方法，可以创建定制的 ClassLoader。 </p>
<p>在本章的其余部分，您会学习 Java ClassLoader 的关键方法。您将了解每一个方法的作用以及它是如何适合装入类文件这个过程的。您也会知道，创建自己的 ClassLoader 时，需要编写什么代码。 </p>
<p>在下一章中，您将会利用这些知识来使用我们的 ClassLoader 示例 -- CompilingClassLoader。 <br />
</p>
<br />
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>方法 <code style="font-size: 18px; font-family: Courier New,Courier,monospace">loadClass</code></strong></font></p>
<p><br />
<font face="Verdana, Arial, Helvetica" size="4"><strong><code style="font-size: 18px; font-family: Courier New,Courier,monospace"></code></strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p><code style="font-size: 12px; font-family: Courier New,Courier,monospace">ClassLoader.loadClass()</code> 是 ClassLoader 的入口点。其特征如下： </p>
<p>&nbsp;</p>
<blockquote><font face="Courier">Class loadClass( String name, boolean resolve ); </font></blockquote>
<p><code style="font-size: 12px; font-family: Courier New,Courier,monospace">name</code> 参数指定了 JVM 需要的类的名称，该名称以包表示法表示，如 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Foo</code> 或 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">java.lang.Object</code>。 </p>
<p><code style="font-size: 12px; font-family: Courier New,Courier,monospace">resolve</code> 参数告诉方法是否需要解析类。在准备执行类之前，应考虑类解析。并不总是需要解析。如果 JVM 只需要知道该类是否存在或找出该类的超类，那么就不需要解析。 </p>
<p>在 Java 版本 1.1 和以前的版本中，<code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 方法是创建定制的 ClassLoader 时唯一需要覆盖的方法。（<a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/j-classloader-5-1.shtml">Java 2 中 ClassLoader 的变动</a>提供了关于 Java 1.2 中 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findClass()</code> 方法的信息。） <br />
</p>
<br />
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>方法 <code style="font-size: 18px; font-family: Courier New,Courier,monospace">defineClass</code></strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p><code style="font-size: 12px; font-family: Courier New,Courier,monospace">defineClass</code> 方法是 ClassLoader 的主要诀窍。该方法接受由原始字节组成的数组并把它转换成 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Class</code> 对象。原始数组包含如从文件系统或网络装入的数据。 </p>
<p><code style="font-size: 12px; font-family: Courier New,Courier,monospace">defineClass</code> 管理 JVM 的许多复杂、神秘和倚赖于实现的方面 -- 它把字节码分析成运行时数据结构、校验有效性等等。不必担心，您无需亲自编写它。事实上，即使您想要这么做也不能覆盖它，因为它已被标记成最终的。 <br />
</p>
<br />
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>方法 <code style="font-size: 18px; font-family: Courier New,Courier,monospace">findSystemClass</code></strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p><code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code> 方法从本地文件系统装入文件。它在本地文件系统中寻找类文件，如果存在，就使用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">defineClass</code> 将原始字节转换成 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Class</code> 对象，以将该文件转换成类。当运行 Java 应用程序时，这是 JVM 正常装入类的缺省机制。（<a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/j-classloader-5-1.shtml">Java 2 中 ClassLoader 的变动</a>提供了关于 Java 版本 1.2 这个过程变动的详细信息。） </p>
<p>对于定制的 ClassLoader，只有在尝试其它方法装入类之后，再使用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code>。原因很简单：ClassLoader 是负责执行装入类的特殊步骤，不是负责<em>所有</em>类。例如，即使 ClassLoader 从远程的 Web 站点装入了某些类，仍然需要在本地机器上装入大量的基本 Java 库。而这些类不是我们所关心的，所以要 JVM 以缺省方式装入它们：从本地文件系统。这就是 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code> 的用途。 </p>
<p>其工作流程如下： </p>
<p>&nbsp;</p>
<ul>
    <li>请求定制的 ClassLoader 装入类。
    <li>检查远程 Web 站点，查看是否有所需要的类。
    <li>如果有，那么好；抓取这个类，完成任务。
    <li>如果没有，假定这个类是在基本 Java 库中，那么调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code>，使它从文件系统装入该类。 </li>
</ul>
<p>在大多数定制 ClassLoaders 中，首先调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code> 以节省在本地就可以装入的许多 Java 库类而要在远程 Web 站点上查找所花的时间。然而，正如，在下一章节所看到的，直到确信能自动编译我们的应用程序代码时，才让 JVM 从本地文件系统装入类。 <br />
</p>
<br />
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>方法 <code style="font-size: 18px; font-family: Courier New,Courier,monospace">resolveClass</code></strong></font></p>
<p><font size="+0"><font face="Verdana, Arial, Helvetica" size="2">正如前面所提到的，可以不完全地（不带解析）装入类，也可以完全地（带解析）装入类。当编写我们自己的 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 时，可以调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">resolveClass</code>，这取决于 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 的 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">resolve</code> 参数的值。</font></font></p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>方法 <code style="font-size: 18px; font-family: Courier New,Courier,monospace">findLoadedClass</code></strong></font></p>
<p><font size="+0"><font face="Verdana, Arial, Helvetica" size="2"><code style="font-size: 12px; font-family: Courier New,Courier,monospace">findLoadedClass</code> 充当一个缓存：当请求 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 装入类时，它调用该方法来查看 ClassLoader 是否已装入这个类，这样可以避免重新装入已存在类所造成的麻烦。应首先调用该方法。</font></font></p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>组装</strong></font></p>
</font>
<p>&nbsp;</p>
</font>
<p>&nbsp;</p>
</font>
<p>&nbsp;</p>
</font>
<p>让我们看一下如何组装所有方法。 </p>
<p>我们的 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 实现示例执行以下步骤。（这里，我们没有指定生成类文件是采用了哪种技术 -- 它可以是从 Net 上装入、或者从归档文件中提取、或者实时编译。无论是哪一种，那是种特殊的神奇方式，使我们获得了原始类文件字节。） </p>
<p>&nbsp;</p>
<ul>
    <li>调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findLoadedClass</code> 来查看是否存在已装入的类。<br x="7" />
    <br x="7" />
    <li>如果没有，那么采用那种特殊的神奇方式来获取原始字节。<br x="7" />
    <br x="7" />
    <li>如果已有原始字节，调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">defineClass</code> 将它们转换成 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Class</code> 对象。<br x="7" />
    <br x="7" />
    <li>如果没有原始字节，然后调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code> 查看是否从本地文件系统获取类。<br x="7" />
    <br x="7" />
    <li>如果 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">resolve</code> 参数是 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">true</code>，那么调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">resolveClass</code> 解析 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Class</code> 对象。<br x="7" />
    <br x="7" />
    <li>如果还没有类，返回 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">ClassNotFoundException</code>。<br x="7" />
    <br x="7" />
    <li>否则，将类返回给调用程序。 </li>
</ul>
<font face="Verdana, Arial, Helvetica" size="4"><strong>推想<br />
<br />
</strong></font><font size="+0"><font face="Verdana, Arial, Helvetica" size="2">现在您已经了解了 ClassLoader 的工作原理，现在该构建一个了。在下一章中，我们将讨论 CCL。<br />
<br />
---------------------------------------------------------------------------------------------<br />
</font></font><font face="Verdana, Arial, Helvetica" size="2">
<p><font face="Verdana, Arial, Helvetica" size="2">
<p><font face="Verdana, Arial, Helvetica" size="2">
<p><strong>第三章：Compiling ClassLoader</strong></p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>CCL 揭密</strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>我们的 ClassLoader (CCL) 的任务是确保代码被编译和更新。 </p>
<p>下面描述了它的工作方式： </p>
<p>&nbsp;</p>
<ul>
    <li>当请求一个类时，先查看它是否在磁盘的当前目录或相应的子目录。<br x="7" />
    <br x="7" />
    <li>如果该类不存在，但源码中有，那么调用 Java 编译器来生成类文件。<br x="7" />
    <br x="7" />
    <li>如果该类已存在，检查它是否比源码旧。如果是，调用 Java 编译器来重新生成类文件。<br x="7" />
    <br x="7" />
    <li>如果编译失败，或者由于其它原因不能从现有的源码中生成类文件，返回 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">ClassNotFoundException</code>。<br x="7" />
    <br x="7" />
    <li>如果仍然没有该类，也许它在其它库中，所以调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code> 来寻找该类。<br x="7" />
    <br x="7" />
    <li>如果还是没有，则返回 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">ClassNotFoundException</code>。<br x="7" />
    <br x="7" />
    <li>否则，返回该类。 </li>
</ul>
</font><font face="Verdana, Arial, Helvetica" size="4"><strong>Java 编译的工作方式</strong></font>
<p>&nbsp;</p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>在深入讨论之前，应该先退一步，讨论 Java 编译。通常，Java 编译器不只是编译您要求它编译的类。它还会编译其它类，如果这些类是您要求编译的类所需要的类。 </p>
<p>CCL 逐个编译应用程序中的需要编译的每一个类。但一般来说，在编译器编译完第一个类后，CCL 会查找所有需要编译的类，然后编译它。为什么？Java 编译器类似于我们正在使用的规则：如果类不存在，或者与它的源码相比，它比较旧，那么它需要编译。其实，Java 编译器在 CCL 之前的一个步骤，它会做大部分的工作。 </p>
<p>当 CCL 编译它们时，会报告它正在编译哪个应用程序上的类。在大多数的情况下，CCL 会在程序中的主类上调用编译器，它会做完所有要做的 -- 编译器的单一调用已足够了。 </p>
<p>然而，有一种情形，在第一步时不会编译某些类。如果使用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Class.forName</code> 方法，通过名称来装入类，Java 编译器会不知道这个类时所需要的。在这种情况下，您会看到 CCL 再次运行 Java 编译器来编译这个类。在<a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/j-classloader-6-1.shtml">源代码</a>中演示了这个过程。 <br />
</p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>使用 CompilationClassLoader</strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>要使用 CCL，必须以特殊方式调用程序。不能直接运行该程序，如： </p>
<p>&nbsp;</p>
<blockquote><font face="Courier">% java Foo arg1 arg2 </font></blockquote>
<p>应以下列方式运行它： </p>
<p>&nbsp;</p>
<blockquote><font face="Courier">% java CCLRun Foo arg1 arg2 </font></blockquote>
<p>CCLRun 是一个特殊的存根程序，它创建 CompilingClassLoader 并用它来装入程序的主类，以确保通过 CompilingClassLoader 来装入整个程序。CCLRun 使用 Java Reflection API 来调用特定类的主方法并把参数传递给它。有关详细信息，请参阅<a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/j-classloader-6-1.shtml">源代码</a>。 <br />
</p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>运行示例</strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>源码包括了一组小类，它们演示了工作方式。主程序是 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Foo</code> 类，它创建类 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Bar</code> 的实例。类 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Bar</code> 创建另一个类 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Baz</code> 的实例，它在 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">baz</code> 包内，这是为了展示 CCL 是如何处理子包里的代码。<code style="font-size: 12px; font-family: Courier New,Courier,monospace">Bar</code> 也是通过名称装入的，其名称为 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Boo</code>，这用来展示它也能与 CCL 工作。 </p>
<p>每个类都声明已被装入并运行。现在用<a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/j-classloader-6-1.shtml">源代码</a>来试一下。编译 CCLRun 和 CompilingClassLoader。确保不要编译其它类（<code style="font-size: 12px; font-family: Courier New,Courier,monospace">Foo</code>、<code style="font-size: 12px; font-family: Courier New,Courier,monospace">Bar</code>、<code style="font-size: 12px; font-family: Courier New,Courier,monospace">Baz</code> 和 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Boo</code>），否则将不会使用 CCL，因为这些类已经编译过了。 </p>
<p>&nbsp;</p>
<blockquote><font face="Courier">
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace"><br />
% java CCLRun Foo arg1 arg2<br />
CCL: Compiling Foo.java...<br />
foo! arg1 arg2<br />
bar! arg1 arg2<br />
baz! arg1 arg2<br />
CCL: Compiling Boo.java...<br />
Boo!<br />
</code></pre>
</font></blockquote>
<p>请注意，首先调用编译器，<code style="font-size: 12px; font-family: Courier New,Courier,monospace">Foo.java</code> 管理 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Bar</code> 和 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">baz.Baz</code>。直到 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Bar</code> 通过名称来装入 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">Boo</code> 时，被调用它，这时 CCL 会再次调用编译器来编译它。 </p>
</font>
<p>&nbsp;</p>
</font>
<p>&nbsp;</p>
</font>
<p>&nbsp;</p>
</font>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------------<br />
</p>
<p><strong>第四章：java2 中ClassLoader的变动</strong></p>
<br />
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>概述</strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>在 Java 版本 1.2 和以后的版本中，对 ClassLoader 做了一些改进。任何为老系统编写的代码可以在新版本中运行，但新系统为您提供了一些便利。 </p>
<p>新模型是<em>委托</em>模型，这意味着如果 ClassLoader 不能找到类，它会请求父代 ClassLoader 来执行此项任务。所有 ClassLoaders 的根是系统 ClassLoader，它会以缺省方式装入类 -- 即，从本地文件系统。 <br />
</p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong><code style="font-size: 18px; font-family: Courier New,Courier,monospace">loadClass</code> 的缺省实现</strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>定制编写的 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 方法一般尝试几种方式来装入所请求的类，如果您编写许多类，会发现一次次地在相同的、很复杂的方法上编写变量。 </p>
<p>在 Java 1.2 中 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 的实现嵌入了大多数查找类的一般方法，并使您通过覆盖 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findClass</code> 方法来定制它，在适当的时候 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findClass</code> 会调用 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code>。 </p>
<p>这种方式的好处是您可能不一定要覆盖 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code>；只要覆盖 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findClass</code> 就行了，这减少了工作量。 <br />
</p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>新方法：<code style="font-size: 18px; font-family: Courier New,Courier,monospace">findClass</code></strong></font></p>
<p><font size="+0"><font face="Verdana, Arial, Helvetica" size="2"><code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code> 的缺省实现调用这个新方法。<code style="font-size: 12px; font-family: Courier New,Courier,monospace">findClass</code> 的用途包含您的 ClassLoader 的所有特殊代码，而无需要复制其它代码（例如，当专门的方法失败时，调用系统 ClassLoader）。</font></font></p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>新方法：<code style="font-size: 18px; font-family: Courier New,Courier,monospace">getSystemClassLoader</code></strong></font></p>
<p><font size="+0"><font face="Verdana, Arial, Helvetica" size="2">如果覆盖 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findClass</code> 或 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">loadClass</code>，<code style="font-size: 12px; font-family: Courier New,Courier,monospace">getSystemClassLoader</code> 使您能以实际 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">ClassLoader</code> 对象来访问系统 ClassLoader（而不是固定的从 <code style="font-size: 12px; font-family: Courier New,Courier,monospace">findSystemClass</code> 调用它）。</font></font></p>
<p><font face="Verdana, Arial, Helvetica" size="4"><strong>新方法：<code style="font-size: 18px; font-family: Courier New,Courier,monospace">getParent</code></strong></font></p>
<p><font face="Verdana, Arial, Helvetica" size="2">
<p>为了将类请求委托给父代 ClassLoader，这个新方法允许 ClassLoader 获取它的父代 ClassLoader。当使用特殊方法，定制的 ClassLoader 不能找到类时，可以使用这种方法。 </p>
<p>父代 ClassLoader 被定义成创建该 ClassLoader 所包含代码的对象的 ClassLoader。 <br />
</p>
<p>----------------------------------------------------------------------------------<br />
</p>
</font>
<p>&nbsp;</p>
</font>
<p>&nbsp;</p>
</font>
<p>&nbsp;</p>
<p><strong>第五章.源代码</strong></p>
</font>
<p>&nbsp;</p>
</font><font face="Verdana, Arial, Helvetica" size="4"><strong>CompilingClassLoader.java<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p>以下是 CompilingClassLoader.java 的源代码 </p>
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace">// $Id$<br />
import java.io.*;<br />
/*<br />
A CompilingClassLoader compiles your Java source on-the-fly.  It checks<br />
for nonexistent .class files, or .class files that are older than their<br />
corresponding source code.*/<br />
public class CompilingClassLoader extends ClassLoader<br />
{<br />
// Given a filename, read the entirety of that file from disk<br />
// and return it as a byte array.<br />
private byte[] getBytes( String filename ) throws IOException {<br />
// Find out the length of the file<br />
File file = new File( filename );<br />
long len = file.length();<br />
// Create an array that's just the right size for the file's<br />
// contents<br />
byte raw[] = new byte[(int)len];<br />
// Open the file<br />
FileInputStream fin = new FileInputStream( file );<br />
// Read all of it into the array; if we don't get all,<br />
// then it's an error.<br />
int r = fin.read( raw );<br />
if (r != len)<br />
throw new IOException( "Can't read all, "+r+" != "+len );<br />
// Don't forget to close the file!<br />
fin.close();<br />
// And finally return the file contents as an array<br />
return raw;<br />
}<br />
// Spawn a process to compile the java source code file<br />
// specified in the 'javaFile' parameter.  Return a true if<br />
// the compilation worked, false otherwise.<br />
private boolean compile( String javaFile ) throws IOException {<br />
// Let the user know what's going on<br />
System.out.println( "CCL: Compiling "+javaFile+"..." );<br />
// Start up the compiler<br />
Process p = Runtime.getRuntime().exec( "javac "+javaFile );<br />
// Wait for it to finish running<br />
try {<br />
p.waitFor();<br />
} catch( InterruptedException ie ) { System.out.println( ie ); }<br />
// Check the return code, in case of a compilation error<br />
int ret = p.exitValue();<br />
// Tell whether the compilation worked<br />
return ret==0;<br />
}<br />
// The heart of the ClassLoader -- automatically compile<br />
// source as necessary when looking for class files<br />
public Class loadClass( String name, boolean resolve )<br />
throws ClassNotFoundException {<br />
<br />
// Our goal is to get a Class object<br />
Class clas = null;<br />
<br />
// First, see if we've already dealt with this one<br />
clas = findLoadedClass( name );<br />
<br />
//System.out.println( "findLoadedClass: "+clas );<br />
<br />
// Create a pathname from the class name<br />
// E.g. java.lang.Object =&gt; java/lang/Object<br />
String fileStub = name.replace( '.', '/' );<br />
<br />
// Build objects pointing to the source code (.java) and object<br />
// code (.class)<br />
String javaFilename = fileStub+".java";<br />
String classFilename = fileStub+".class";<br />
<br />
File javaFile = new File( javaFilename );<br />
File classFile = new File( classFilename );<br />
<br />
//System.out.println( "j "+javaFile.lastModified()+" c "+<br />
//  classFile.lastModified() );<br />
<br />
// First, see if we want to try compiling.  We do if (a) there<br />
// is source code, and either (b0) there is no object code,<br />
// or (b1) there is object code, but it's older than the source<br />
if (javaFile.exists() &amp;&amp;<br />
(!classFile.exists() ||<br />
javaFile.lastModified() &gt; classFile.lastModified())) {<br />
<br />
try {<br />
// Try to compile it.  If this doesn't work, then<br />
// we must declare failure.  (It's not good enough to use<br />
// and already-existing, but out-of-date, classfile)<br />
if (!compile( javaFilename ) || !classFile.exists()) {<br />
throw new ClassNotFoundException( "Compile failed: "+javaFilename );<br />
}<br />
} catch( IOException ie ) {<br />
<br />
// Another place where we might come to if we fail<br />
// to compile<br />
throw new ClassNotFoundException( ie.toString() );<br />
}<br />
}<br />
<br />
// Let's try to load up the raw bytes, assuming they were<br />
// properly compiled, or didn't need to be compiled<br />
try {<br />
<br />
// read the bytes<br />
byte raw[] = getBytes( classFilename );<br />
<br />
// try to turn them into a class<br />
clas = defineClass( name, raw, 0, raw.length );<br />
} catch( IOException ie ) {<br />
// This is not a failure!  If we reach here, it might<br />
// mean that we are dealing with a class in a library,<br />
// such as java.lang.Object<br />
}<br />
<br />
//System.out.println( "defineClass: "+clas );<br />
<br />
// Maybe the class is in a library -- try loading<br />
// the normal way<br />
if (clas==null) {<br />
clas = findSystemClass( name );<br />
}<br />
<br />
//System.out.println( "findSystemClass: "+clas );<br />
<br />
// Resolve the class, if any, but only if the "resolve"<br />
// flag is set to true<br />
if (resolve &amp;&amp; clas != null)<br />
resolveClass( clas );<br />
<br />
// If we still don't have a class, it's an error<br />
if (clas == null)<br />
throw new ClassNotFoundException( name );<br />
<br />
// Otherwise, return the class<br />
return clas;<br />
}<br />
</code><font size="+0"><font face="Verdana, Arial, Helvetica" size="2">
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace"> }<br />
</code><font face="Verdana, Arial, Helvetica" size="4"><strong>CCRun.java<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p><br />
以下是 CCRun.java 的源代码<br />
</p>
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace"><br />
// $Id$<br />
<br />
import java.lang.reflect.*;<br />
<br />
/*<br />
<br />
CCLRun executes a Java program by loading it through a<br />
CompilingClassLoader.<br />
<br />
*/<br />
<br />
public class CCLRun<br />
{<br />
static public void main( String args[] ) throws Exception {<br />
<br />
// The first argument is the Java program (class) the user<br />
// wants to run<br />
String progClass = args[0];<br />
<br />
// And the arguments to that program are just<br />
// arguments 1..n, so separate those out into<br />
// their own array<br />
String progArgs[] = new String[args.length-1];<br />
System.arraycopy( args, 1, progArgs, 0, progArgs.length );<br />
<br />
// Create a CompilingClassLoader<br />
CompilingClassLoader ccl = new CompilingClassLoader();<br />
<br />
// Load the main class through our CCL<br />
Class clas = ccl.loadClass( progClass );<br />
<br />
// Use reflection to call its main() method, and to<br />
// pass the arguments in.<br />
<br />
// Get a class representing the type of the main method's argument<br />
Class mainArgType[] = { (new String[0]).getClass() };<br />
<br />
// Find the standard main method in the class<br />
Method main = clas.getMethod( "main", mainArgType );<br />
<br />
// Create a list containing the arguments -- in this case,<br />
// an array of strings<br />
Object argsArray[] = { progArgs };<br />
<br />
// Call the method<br />
main.invoke( null, argsArray );<br />
}<br />
}<br />
</code><font face="Verdana, Arial, Helvetica" size="4"><strong>Foo.java<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p><br />
以下是 Foo.java 的源代码</p>
<br />
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace"><br />
// $Id$<br />
<br />
public class Foo<br />
{<br />
static public void main( String args[] ) throws Exception {<br />
System.out.println( "foo! "+args[0]+" "+args[1] );<br />
new Bar( args[0], args[1] );<br />
}<br />
}<br />
</code><font face="Verdana, Arial, Helvetica" size="4"><strong>Bar.java<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p><br />
以下是 Bar.java 的源代码        </p>
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace"><br />
// $Id$<br />
<br />
import baz.*;<br />
<br />
public class Bar<br />
{<br />
public Bar( String a, String b ) {<br />
System.out.println( "bar! "+a+" "+b );<br />
new Baz( a, b );<br />
<br />
try {<br />
Class booClass = Class.forName( "Boo" );<br />
Object boo = booClass.newInstance();<br />
} catch( Exception e ) {<br />
e.printStackTrace();<br />
}<br />
}<br />
}<br />
</code><font face="Verdana, Arial, Helvetica" size="4"><strong>baz/Baz.java<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p><br />
以下是 baz/Baz.java 的源代码<br />
</p>
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace"><br />
// $Id$<br />
<br />
package baz;<br />
<br />
public class Baz<br />
{<br />
public Baz( String a, String b ) {<br />
System.out.println( "baz! "+a+" "+b );<br />
}<br />
}<br />
<br />
</code><font face="Verdana, Arial, Helvetica" size="4"><strong>Boo.java<br />
</strong></font><font face="Verdana, Arial, Helvetica" size="2">
<p><br />
以下是 Boo.java 的源代码        </p>
<pre><code style="font-size: 12px; font-family: Courier New,Courier,monospace"><br />
// $Id$<br />
<br />
public class Boo<br />
{<br />
public Boo() {<br />
System.out.println( "Boo!" );<br />
}<br />
}<br />
<br />
</code></pre>
</font></pre>
</font></pre>
</font></pre>
</font></pre>
</font></pre>
</font></font></font></pre>
</div>
<img src ="http://www.blogjava.net/boluobn/aggbug/177748.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/boluobn/" target="_blank">菠萝</a> 2008-01-25 13:52 <a href="http://www.blogjava.net/boluobn/archive/2008/01/25/177748.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>