副标题#e#
一、在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)不然,将类返回给挪用措施。
#p#副标题#e#
三、一个实现了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
#p#分页标题#e#
/**
*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);
}
}
好了,运行java TestRun Hello 阿飞
....
....
....
Hello,阿飞