Class loadClass(String name)或 loadClass(String name , boolean resolve)方法是加载的核心。它根据类的全名(比如 String 类的全名是 java.lang.String)获得对应类的二进制数据,然后通过 Class defineClass(byte[] b) 将二进制数据加载到 JVM 的方法区,并返回对应类的 Class 实例,然后根据可选的参数 resolve 决定是否需要现在解析这个类。最后将这个 Class 实例作为 loadClass 方法的返回值。
如果无法加载和 defineClass,即无法通过本加载器直接加载类的情况,则使用 Class findSystemClass(String name) 将类加载任务委派给系统类加载器查找。如果能找到则加载,否则抛出 ClassNotFoundException 异常。
编程实例
以下用实例来展示这一过程:
类 CompilingClassLoader 是一个自定义加载器,它能直接读取 Java 源文件实现类加载。CLL 类的 main 方法为程序入口,通过 ComplilingClassLoader 加载一个 Foo 类,使用反射机制调用 Foo 类的 main 方法。
importjava.io.*;/* A CompilingClassLoader compiles your Java source on-the-fly. It checks for nonexistent .class files, or .class files that are older than their corresponding source code. */publicclassCompilingClassLoaderextendsClassLoader{// Given a filename, read the entirety of that file from disk // and return it as a byte array. privatebyte[]getBytes(Stringfilename)throwsIOException{// Find out the length of the file Filefile=newFile(filename);longlen=file.length();// Create an array that's just the right size for the file's // contents byteraw[]=newbyte[(int)len];// Open the file FileInputStreamfin=newFileInputStream(file);// Read all of it into the array; if we don't get all, // then it's an error. intr=fin.read(raw);if(r!=len)thrownewIOException("Can't read all, "+r+" != "+len);// Don't forget to close the file! fin.close();// And finally return the file contents as an array returnraw;}// Spawn a process to compile the java source code file // specified in the 'javaFile' parameter. Return a true if // the compilation worked, false otherwise. privatebooleancompile(StringjavaFile)throwsIOException{// Let the user know what's going on System.out.println("CCL: Compiling "+javaFile+"...");// Start up the compiler Processp=Runtime.getRuntime().exec("javac "+javaFile);// Wait for it to finish running try{p.waitFor();}catch(InterruptedExceptionie){System.out.println(ie);}// Check the return code, in case of a compilation error intret=p.exitValue();// Tell whether the compilation worked returnret==0;}// The heart of the ClassLoader -- automatically compile // source as necessary when looking for class files publicClassloadClass(Stringname,booleanresolve)throwsClassNotFoundException{// Our goal is to get a Class object Classclas=null;// First, see if we've already dealt with this one clas=findLoadedClass(name);//System.out.println( "findLoadedClass: "+clas ); // Create a pathname from the class name // E.g. java.lang.Object => java/lang/Object StringfileStub=name.replace('.','/');// Build objects pointing to the source code (.java) and object // code (.class) StringjavaFilename=fileStub+".java";StringclassFilename=fileStub+".class";FilejavaFile=newFile(javaFilename);FileclassFile=newFile(classFilename);//System.out.println( "j "+javaFile.lastModified()+" c "+ // classFile.lastModified() ); // First, see if we want to try compiling. We do if (a) there // is source code, and either (b0) there is no object code, // or (b1) there is object code, but it's older than the source if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){try{// Try to compile it. If this doesn't work, then // we must declare failure. (It's not good enough to use // and already-existing, but out-of-date, classfile) if(!compile(javaFilename)||!classFile.exists()){thrownewClassNotFoundException("Compile failed: "+javaFilename);}}catch(IOExceptionie){// Another place where we might come to if we fail // to compile thrownewClassNotFoundException(ie.toString());}}// Let's try to load up the raw bytes, assuming they were // properly compiled, or didn't need to be compiled try{// read the bytes byteraw[]=getBytes(classFilename);// try to turn them into a class clas=defineClass(name,raw,0,raw.length);}catch(IOExceptionie){// This is not a failure! If we reach here, it might // mean that we are dealing with a class in a library, // such as java.lang.Object }//System.out.println( "defineClass: "+clas ); // Maybe the class is in a library -- try loading // the normal way if(clas==null){clas=findSystemClass(name);}//System.out.println( "findSystemClass: "+clas ); // Resolve the class, if any, but only if the "resolve" // flag is set to true if(resolve&&clas!=null)resolveClass(clas);// If we still don't have a class, it's an error if(clas==null)thrownewClassNotFoundException(name);// Otherwise, return the class returnclas;}}
/* CCLRun executes a Java program by loading it through a CompilingClassLoader. */publicclassCCLRun{staticpublicvoidmain(Stringargs[])throwsException{// The first argument is the Java program (class) the user // wants to run StringprogClass=args[0];// And the arguments to that program are just // arguments 1..n, so separate those out into // their own array StringprogArgs[]=newString[args.length-1];System.arraycopy(args,1,progArgs,0,progArgs.length);// Create a CompilingClassLoader CompilingClassLoaderccl=newCompilingClassLoader();// Load the main class through our CCL Classclas=ccl.loadClass(progClass);// Use reflection to call its main() method, and to // pass the arguments in. // Get a class representing the type of the main method's argument ClassmainArgType[]={(newString[0]).getClass()};// Find the standard main method in the class Methodmain=clas.getMethod("main",mainArgType);// Create a list containing the arguments -- in this case, // an array of strings ObjectargsArray[]={progArgs};// Call the method main.invoke(null,argsArray);}}
try{if(parent!=null){c=parent.loadClass(name,false);}else{c=findBootstrapClassOrNull(name);}}catch(ClassNotFoundExceptione){// ClassNotFoundException thrown if class not found // from the non-null parent class loader }
packagebiaobiaoqi.classLoader;importjava.io.*;importorg.objectweb.asm.ClassReader;importorg.objectweb.asm.ClassWriter;importbiaobiaoqi.asm.*;publicclassASMClassLoaderextendsClassLoader{StringbasePath;/**reference to System Classloader as the parent class loader * @param path <br> the path of .class files will be loaded */publicASMClassLoader(Stringpath){basePath=path;}/** * reference to parent as it's parent classloader * @param path * @param parent */publicASMClassLoader(Stringpath,ClassLoaderparent){super(parent);basePath=path;}@OverridepublicClassfindClass(Stringname)throwsClassNotFoundException{System.out.println("findClass");byte[]raw;try{raw=getBytesFromBasePath(name);}catch(IOExceptione){e.printStackTrace();thrownewClassNotFoundException();}byte[]transformed=instrumentBtyeCode(raw);/* try{ FileOutputStream file = new FileOutputStream( "/home/biaobiaoqi/" +name.replace( '.', '/' )+".class"); file.write( transformed); file.close(); } catch (IOException e) { e.printStackTrace(); } */if(transformed==null){thrownewClassNotFoundException();}returndefineClass(name,transformed,0,transformed.length);}privatebyte[]getBytesFromBasePath(StringclassName)throwsIOException,ClassNotFoundException{StringfileStub=className.replace('.','/');StringclassFileName=basePath+fileStub+".class";Filefile=newFile(classFileName);longlen=file.length();byteraw[]=newbyte[(int)len];FileInputStreamfin=newFileInputStream(file);intr=fin.read(raw);if(r!=len)thrownewIOException("Can't read all, "+r+" != "+len);fin.close();returnraw;}privatebyte[]instrumentBtyeCode(byte[]raw){ClassWritercw=newClassWriter(ClassWriter.COMPUTE_FRAMES);ASMClassAdaptermca=newASMClassAdapter(cw);ClassReadercr=newClassReader(raw);cr.accept(mca,0);returncw.toByteArray();}@OverridepublicClassloadClass(Stringname,booleanresolve)throwsClassNotFoundException{System.out.println("loadClass_resolve");returnsuper.loadClass(name,resolve);}@OverridepublicClassloadClass(Stringname)throwsClassNotFoundException{System.out.println("loadClass");returnsuper.loadClass(name);}}