Java类加载机制的奥秘


Java 类加载机制的奥秘 一一一一、、、、在在在在 jdk1.2 以后以后以后以后,,,,类加载是通过委托来完成的类加载是通过委托来完成的类加载是通过委托来完成的类加载是通过委托来完成的,,,,这意味着如果这意味着如果这意味着如果这意味着如果 ClassLoader 不能找到类不能找到类不能找到类不能找到类,,,, 它 会 请 求 父 代它 会 请 求 父 代它 会 请 求 父 代它 会 请 求 父 代 ClassLoader 来 执 行 此 项 任 务来 执 行 此 项 任 务来 执 行 此 项 任 务来 执 行 此 项 任 务 ,,,, 所 有所 有所 有所 有 ClassLoaders 的 根 是 系 统的 根 是 系 统的 根 是 系 统的 根 是 系 统 ClassLoader ,,,,它会以缺省方式装入类它会以缺省方式装入类它会以缺省方式装入类它会以缺省方式装入类 -- 即即即即,,,,从本地文件系统从本地文件系统从本地文件系统从本地文件系统。。。。今天今天今天今天 一一一一、、、、在在在在 jdk1.2 以后以后以后以后,,,,类加载是通过委托来完成的类加载是通过委托来完成的类加载是通过委托来完成的类加载是通过委托来完成的,,,,这意味着如果这意味着如果这意味着如果这意味着如果 ClassLoader 不能找到类不能找到类不能找到类不能找到类,,,, 它 会 请 求 父 代它 会 请 求 父 代它 会 请 求 父 代它 会 请 求 父 代 ClassLoader 来 执 行 此 项 任 务来 执 行 此 项 任 务来 执 行 此 项 任 务来 执 行 此 项 任 务 ,,,, 所 有所 有所 有所 有 ClassLoaders 的 根 是 系 统的 根 是 系 统的 根 是 系 统的 根 是 系 统 ClassLoader ,,,,它会以缺省方式装入类它会以缺省方式装入类它会以缺省方式装入类它会以缺省方式装入类 -- 即即即即,,,,从本地文件系统从本地文件系统从本地文件系统从本地文件系统。。。。今天我们就来探讨一下在今天我们就来探讨一下在今天我们就来探讨一下在今天我们就来探讨一下在 jvm 中这些机制是怎样运行的中这些机制是怎样运行的中这些机制是怎样运行的中这些机制是怎样运行的。。。。让我们假设有一个让我们假设有一个让我们假设有一个让我们假设有一个 class 字节码文件字节码文件字节码文件字节码文件((((比如比如比如比如 Hello.class 文件文件文件文件),),),), 那么在应用程序中那么在应用程序中那么在应用程序中那么在应用程序中,,,,他是如何被加载进来他是如何被加载进来他是如何被加载进来他是如何被加载进来,,,,并形成一个类对象的呢并形成一个类对象的呢并形成一个类对象的呢并形成一个类对象的呢????我们这篇文章的目的我们这篇文章的目的我们这篇文章的目的我们这篇文章的目的 就是为了解释这个问题就是为了解释这个问题就是为了解释这个问题就是为了解释这个问题。。。。 在在在在 java.lang 包里有个包里有个包里有个包里有个 ClassLoader 类类类类,,,,ClassLoader 的基本目标是对类的请求提供服的基本目标是对类的请求提供服的基本目标是对类的请求提供服的基本目标是对类的请求提供服 务务务务。。。。当当当当 JVM 需要使用类时需要使用类时需要使用类时需要使用类时,,,,它根据名称向它根据名称向它根据名称向它根据名称向 ClassLoader 请求这个类请求这个类请求这个类请求这个类,,,,然后然后然后然后 ClassLoader 试试试试 图返回一个表示这个类的图返回一个表示这个类的图返回一个表示这个类的图返回一个表示这个类的 Class 对象对象对象对象。。。。通过覆盖对应于这个过程不同阶段的方法通过覆盖对应于这个过程不同阶段的方法通过覆盖对应于这个过程不同阶段的方法通过覆盖对应于这个过程不同阶段的方法,,,,可以创可以创可以创可以创 建定制的建定制的建定制的建定制的 ClassLoader 。。。。其中有个其中有个其中有个其中有个 loadClass(String name, boolean resolve) 方法方法方法方法,,,,该方法为该方法为该方法为该方法为 ClassLoader 的入口点的入口点的入口点的入口点,,,,在在在在 jdk1.2 以后以后以后以后,,,,loadClass 方法将缺省调用方法将缺省调用方法将缺省调用方法将缺省调用 findClass 方法方法方法方法,,,,详细内详细内详细内详细内 容可以参考容可以参考容可以参考容可以参考 API 文档文档文档文档,,,,我们编写的我们编写的我们编写的我们编写的 ClassLoader 主要就是为了覆盖以上两个方法主要就是为了覆盖以上两个方法主要就是为了覆盖以上两个方法主要就是为了覆盖以上两个方法。。。。回到我回到我回到我回到我 们刚才的问题们刚才的问题们刚才的问题们刚才的问题,,,,怎样读进字节码文件怎样读进字节码文件怎样读进字节码文件怎样读进字节码文件,,,,并把它构成一个类对象呢并把它构成一个类对象呢并把它构成一个类对象呢并把它构成一个类对象呢????在在在在 ClassLoader 里有里有里有里有个方个方个方个方 法法法法,,,,Class defineClass(String name, byte[] b, int off, int len) ,,,,答案就在这里了答案就在这里了答案就在这里了答案就在这里了,,,,我们根据把我们根据把我们根据把我们根据把 class 字节码文件字节码文件字节码文件字节码文件((((如如如如 Hello.class) 读进一个字节数组里读进一个字节数组里读进一个字节数组里读进一个字节数组里,,,,byte[] b, 并把它转化为并把它转化为并把它转化为并把它转化为 Class 对象对象对象对象,,,, 而这些数据可以来源于文件而这些数据可以来源于文件而这些数据可以来源于文件而这些数据可以来源于文件,,,,网络等网络等网络等网络等,,,,神奇吧神奇吧神奇吧神奇吧:) defineClass 管理管理管理管理 JVM 的许多复杂的许多复杂的许多复杂的许多复杂、、、、神秘和倚赖于实现的方面神秘和倚赖于实现的方面神秘和倚赖于实现的方面神秘和倚赖于实现的方面 -- 它把字节码分析成它把字节码分析成它把字节码分析成它把字节码分析成 运行时数据结构运行时数据结构运行时数据结构运行时数据结构、、、、校验有效性等等校验有效性等等校验有效性等等校验有效性等等。。。。不必担心不必担心不必担心不必担心,,,,您无需亲自编写它您无需亲自编写它您无需亲自编写它您无需亲自编写它。。。。事实上事实上事实上事实上,,,,即使您想要即使您想要即使您想要即使您想要 这么做也不能覆盖它这么做也不能覆盖它这么做也不能覆盖它这么做也不能覆盖它,,,,因因因因为它已被标记成最终的为它已被标记成最终的为它已被标记成最终的为它已被标记成最终的。。。。 其他一些方法其他一些方法其他一些方法其他一些方法:::: findSystemClass 方法方法方法方法::::从本地文件系统装入文件从本地文件系统装入文件从本地文件系统装入文件从本地文件系统装入文件。。。。它在本地文件系统中寻找类文件它在本地文件系统中寻找类文件它在本地文件系统中寻找类文件它在本地文件系统中寻找类文件,,,, 如果存在如果存在如果存在如果存在,,,,就使用就使用就使用就使用 defineClass 将原始字节转换成将原始字节转换成将原始字节转换成将原始字节转换成 Class 对象对象对象对象,,,,以将该文件转换成类以将该文件转换成类以将该文件转换成类以将该文件转换成类。。。。 findClass 方法方法方法方法::::jdk1.2 以后以后以后以后 loadClass 的缺省实现调用这个新方法的缺省实现调用这个新方法的缺省实现调用这个新方法的缺省实现调用这个新方法。。。。findClass 的用途的用途的用途的用途 包含您的包含您的包含您的包含您的 ClassLoader 的所有特殊代码的所有特殊代码的所有特殊代码的所有特殊代码,,,,而无需要复制其它代码而无需要复制其它代码而无需要复制其它代码而无需要复制其它代码((((例如例如例如例如,,,,当专门的方法失当专门的方法失当专门的方法失当专门的方法失 败时败时败时败时,,,,调用系统调用系统调用系统调用系统 ClassLoader )。)。)。)。 getSystemClassLoader :::: 如果覆盖如果覆盖如果覆盖如果覆盖 findClass 或或或或 loadClass ,,,,getSystemClassLoader 使使使使 您能以实际您能以实际您能以实际您能以实际 ClassLoader 对象来访问系统对象来访问系统对象来访问系统对象来访问系统 ClassLoader((((而不是固定的从而不是固定的从而不是固定的从而不是固定的从 findSystemClass 调用它调用它调用它调用它)。)。)。)。 getParent ::::为了将类请求委托给父代为了将类请求委托给父代为了将类请求委托给父代为了将类请求委托给父代 ClassLoader ,,,,这个新方法允许这个新方法允许这个新方法允许这个新方法允许 ClassLoader 获获获获 取它的父代取它的父代取它的父代取它的父代 ClassLoader 。。。。当使用特殊方法当使用特殊方法当使用特殊方法当使用特殊方法,,,,定制的定制的定制的定制的 ClassLoader 不能找到类时不能找到类时不能找到类时不能找到类时,,,,可以使可以使可以使可以使 用这种方法用这种方法用这种方法用这种方法。。。。 resolveClass: 可以不完全地可以不完全地可以不完全地可以不完全地((((不带解析不带解析不带解析不带解析))))装入类装入类装入类装入类,,,,也可以完全地也可以完全地也可以完全地也可以完全地((((带解析带解析带解析带解析))))装入类装入类装入类装入类。。。。 当编写我们自己的当编写我们自己的当编写我们自己的当编写我们自己的 loadClass 时时时时,,,,可以调用可以调用可以调用可以调用 resolveClass ,,,,这取决于这取决于这取决于这取决于 loadClass 的的的的 resolve 参数的值参数的值参数的值参数的值。。。。 findLoadedClass: 充 当 一 个 缓 存充 当 一 个 缓 存充 当 一 个 缓 存充 当 一 个 缓 存,当 请 求当 请 求当 请 求当 请 求 loadClass 装 入 类 时装 入 类 时装 入 类 时装 入 类 时 ,,,, 它 调 用 该方 法 来 查看它 调 用 该方 法 来 查看它 调 用 该方 法 来 查看它 调 用 该方 法 来 查看 ClassLoader 是否已装入这个类是否已装入这个类是否已装入这个类是否已装入这个类,,,,这样可以避免重新装入已存在类所造成的麻烦这样可以避免重新装入已存在类所造成的麻烦这样可以避免重新装入已存在类所造成的麻烦这样可以避免重新装入已存在类所造成的麻烦。。。。应首先调应首先调应首先调应首先调 用该方法用该方法用该方法用该方法。。。。 二二二二、、、、工作流程工作流程工作流程工作流程:::: 1) 调用调用调用调用 findLoadedClass(String) 来查看是否存在已装入的类来查看是否存在已装入的类来查看是否存在已装入的类来查看是否存在已装入的类,如果没有如果没有如果没有如果没有,,,,那么采用那种那么采用那种那么采用那种那么采用那种 特殊的神奇方式来获取原始字节特殊的神奇方式来获取原始字节特殊的神奇方式来获取原始字节特殊的神奇方式来获取原始字节。。。。 2))))通过父类通过父类通过父类通过父类 ClassLoader 调用调用调用调用 loadClass 方法方法方法方法,,,,如果父类如果父类如果父类如果父类 ClassLoader 是是是是 null ,,,,那么按那么按那么按那么按 缺省方式装入类缺省方式装入类缺省方式装入类缺省方式装入类,,,,即系统即系统即系统即系统 ClassLoader 。。。。 3))))调用调用调用调用 findClass(String) 去查找类并获取类去查找类并获取类去查找类并获取类去查找类并获取类;;;; 4))))如果如果如果如果 loadClass 的的的的 resolve 参数的值为参数的值为参数的值为参数的值为 true ,,,,那么调用那么调用那么调用那么调用 resolveClass 解析解析解析解析 Class 对对对对 象象象象. 5))))如果还没有类如果还没有类如果还没有类如果还没有类,,,,返回返回返回返回 ClassNotFoundException 。。。。 6))))否则否则否则否则,,,,将类返回给调用程序将类返回给调用程序将类返回给调用程序将类返回给调用程序。。。。 三三三三、、、、一个实现了一个实现了一个实现了一个实现了 ClassLoader 的例子的例子的例子的例子:::: /** *CompilingClassLoader.java *Copyright 2005-2-12 */ import java.io.*; public class CompilingClassLoader extends ClassLoader{ // 读取一个文件的内容读取一个文件的内容读取一个文件的内容读取一个文件的内容 private byte[] getBytes(String filename) throws IOException{ File file=new File(filename); long len=file.length(); byte[] raw=new byte[(int)len]; FileInputStream fin=new FileInputStream(file); int r=fin.read(raw); if(r!=len) throw new IOException("Can't read all,"+r+"!="+len); fin.close(); return raw; } private boolean compile(String javaFile) throws IOException{ System.out.println("CCL:Compiling "+javaFile+"..."); // 调用系统的调用系统的调用系统的调用系统的 javac 命令命令命令命令 Process p=Runtime.getRuntime().exec("javac "+javaFile); try{ // 其他线程都等待这个线程完成其他线程都等待这个线程完成其他线程都等待这个线程完成其他线程都等待这个线程完成 p.waitFor(); }catch(InterruptedException ie){ System.out.println(ie); } int ret=p.exitValue(); return ret==0; } public Class loadClass(String name,boolean resovle) throws ClassNotFoundException{ Class clas=null; clas=findLoadedClass(name); // 这里说明了包的表示这里说明了包的表示这里说明了包的表示这里说明了包的表示 String fileStub=name.replace('.','/'); String javaFilename=fileStub+".java"; String classFilename=fileStub+".class"; File javaFile=new File(javaFilename); File classFile=new File(classFilename); // 如果存在如果存在如果存在如果存在 class 文件就不编译文件就不编译文件就不编译文件就不编译 if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){ try{ if(!compile(javaFilename)||!classFile.exists()){ throw new ClassNotFoundException("ClassNotFoundExcetpion:"+javaFilename); } }catch(IOException ie){ throw new ClassNotFoundException(ie.toString()); } } try{ byte[] raw=getBytes(classFilename); // 通过读入数据来构造一个类结构通过读入数据来构造一个类结构通过读入数据来构造一个类结构通过读入数据来构造一个类结构,,,,这是核心这是核心这是核心这是核心 clas=defineClass(name,raw,0,raw.length); }catch(IOException ie){ // } if(clas==null){ clas=findSystemClass(name); } System.out.println("findSystemClass:"+clas); if(resovle && clas!=null){ resolveClass(clas); } if(clas==null){ throw new ClassNotFoundException(name); } return clas; } } 测试该测试该测试该测试该 loader :::: /** *TestRun.java *Copyright 2005-2-11 */ import java.lang.reflect.*; public class TestRun{ public static void main(String[] args) throws Exception{ String progClass=args[0]; String progArgs[]=new String[args.length-1]; System.arraycopy(args,1,progArgs,0,progArgs.length); CompilingClassLoader ccl=new CompilingClassLoader(); Class clas=ccl.loadClass(progClass); // 返回一个返回一个返回一个返回一个 class 的的的的 type Class[] mainArgType={(new String[0]).getClass()}; Method main=clas.getMethod("main",mainArgType); Object argsArray[]={progArgs}; main.invoke(null,argsArray); } } 以上的核心内容已经编写完了以上的核心内容已经编写完了以上的核心内容已经编写完了以上的核心内容已经编写完了,,,,编译后编译后编译后编译后,,,,我们得到两个文件我们得到两个文件我们得到两个文件我们得到两个文件:::: CompilingClassLoader.class,TestRun.class 四四四四、、、、编写一个例子编写一个例子编写一个例子编写一个例子,,,,然后运行我们的然后运行我们的然后运行我们的然后运行我们的 ClassLoader /** *Hello.java */ public class Hello{ public static void main(String[] args){ if(args.length!=1){ System.err.println("Error,exit!"); System.exit(1); } String name=args[0]; System.out.println("Hello,"+name); } } 本 篇 文 章 来 源 于本 篇 文 章 来 源 于本 篇 文 章 来 源 于本 篇 文 章 来 源 于 :::: 开 发 学 院开 发 学 院开 发 学 院开 发 学 院 http://edu.codepub.com 原 文 链 接原 文 链 接原 文 链 接原 文 链 接 :::: http://edu.codepub.com/2011/0119/28920.php
还剩4页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

5cai

贡献于2011-05-22

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf