要創建用戶自己的類加載器,只需要擴展java.lang.ClassLoader類,然後覆蓋它的findClass(String name)方法即可,該方法根據參數指定的類的名字,返回對應的Class對象的引用。
我們來看看官網api文檔的findClass方法。
http://docs.oracle.com/javase/7/docs/api/
-
findClass
protected Class<?> findClass(String name) throws ClassNotFoundException
Finds the class with the specified binary name. This method should be overridden by class loader implementations that follow the delegation model for loading classes, and will be invoked by theloadClass
method after checking the parent class loader for the requested class. The default implementation throws a ClassNotFoundException.- Parameters:
name
- The binary name of the class- Returns:
- The resulting Class object
- Throws:
ClassNotFoundException
- If the class could not be found- Since:
- 1.2
MyClass.java
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader{
private String name; //類加載器
private String path ="d:\\"; //加載類的路徑
private final String fileType=".class"; //class文件的擴展名
private MyClassLoader(String name)
{
super(); //讓系統類加載器成爲該類加載器的父加載器
this.name = name;
}
public MyClassLoader(ClassLoader parent,String name)
{
super(parent); //顯式指定該類加載器的父加載器
this.name = name;
}
@Override
public String toString() {
return this.name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException
{
byte[] data = this.loadClassData(name);
return this.defineClass(name, data, 0,data.length);
}
private byte[] loadClassData(String name)
{
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
try {
this.name= this.name.replace(".", "\\");
is = new FileInputStream(new File(path+name+fileType));
baos = new ByteArrayOutputStream();
int ch = 0;
while(-1!=(ch = is.read()))
{
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
}
finally
{
try {
is.close();
baos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return data;
}
public static void main(String[] aregs) throws Exception
{
MyClassLoader loader1 = new MyClassLoader("loader1");
loader1.setPath("d:\\myapp\\serverlib\\");
MyClassLoader loader2 = new MyClassLoader(loader1,"loader2");
loader2.setPath("d:\\myapp\\clientlib\\");
MyClassLoader loader3 = new MyClassLoader(null,"loader3");
loader3.setPath("d:\\myapp\\otherlib\\");
test(loader2);
test(loader3);
}
public static void test(ClassLoader loader) throws Exception
{
Class clazz = loader.loadClass("Sample");
Object object = clazz.newInstance();
}
}
Sample.java
public class Sample {
public int v1= 1;
public Sample()
{
System.out.println("Sample is loaded by: "+this.getClass().getClassLoader());
new Dog();
}
}
Dog.java
public class Dog {
public Dog()
{
System.out.println("Dog is loaded by :"+this.getClass().getClassLoader());
}
}
按照以上代碼類加載的位置分別創建文件夾。
把編譯好的類文件放在各個文件夾下,把Sample.class,Dog.class放在otherlib,serverlib文件夾下。把MyclassLoader.class放在syslib文件夾下。
然後在syslib文件夾下運行dos窗口:
輸入命令:java MyClassLoader
輸出結果:
Sample is loaded by: loader1
Dog is loaded by :loader1
Sample is loaded by: loader3
Dog is loaded by :loader3
我們分析一下:
findClass方法在上面被重寫。
當執行loader2.loadClass("Sample")時,先由它上層的所有父加載器嘗試加載Sample類。loader1從D:\myapp\serverlib目錄下成功地加載了Sample類,因此loader1是Sample類的定義類加載器,loader1和loader2是Sample類的初始類加載器。
當執行loader3.loadClass("Sample")時,先由它上層的所有父加載器嘗試加載Sample類。loader3的父加載器爲根類加載器,它無法加載Sample類,接着loader3從D:\myapp\otherlib目錄下成功地加載了Sample類,因此loader3是Sample類的定義類加載器及初始類加載器。
在loader1和loader3各自的命名空間中都存在Sample類和Dog類。
在Sample類中主動使用了Dog類,當執行Sample類的構造方法中的new Dog()語句時,Java虛擬機需要先加載Dog類,到底是用哪個類加載器加載呢?從輸出結果可以看出,加載Sample類的loader1還加載了Dog類,Java虛擬機會用Sample類的定義類加載器去加載Dog類,加載過程也同樣採用父親委託機制。爲了驗證這一點,可以把D:\myapp\serverlib目錄下的Dog.class文件刪除,然後在D:\myapp\syslib目錄下存放一個Dog.class文件,此時程序的打印結果爲:
Sample is loaded by: loader1
Dog is loaded by :sun.misc.Launcher$AppClassLoader@73d16e93
Sample is loaded by: loader3
Dog is loaded by :loader3
由此可見,當由loader1加載的Sample類首次主動使用Dog類時,Dog類由系統類加載器加載。如果再把D:\myapp\syslib目錄下的Dog.class對象去掉,在D:\myapp\clientlib目錄下存放一個Dog.class文件。
當由loader1加載的Sample類首次主動使用Dog類時,由於loader1及它的父加載器都無法加載Dog類,因此test(loader2)方法會拋出ClassNotFoundException.
拓展:
1、命令中環境變量的指定
輸入命令:java -cp .;d:\myapp\serverlib MyClassLoader
輸出結果
Sample is loaded by: sun.misc.Launcher$AppClassLoader@73d16e93
Dog is loaded by :sun.misc.Launcher$AppClassLoader@73d16e93
Sample is loaded by: loader3
Dog is loaded by :loader3
.;d:\myapp\serverlib指定系統加載器的加載位置。
統加載器從指定的目錄D:\myapp\serverlib目錄下成功地加載了Sample類,因此係統加載器是Sample類的定義類加載器,系統加載器、loader1和loader2是Sample類的初始類加載器。
2、用代碼驗證加載器之間的關係
public class Test8 {
public static void main(String[] args)
{
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
while(null!=classLoader)
{
classLoader = classLoader.getParent();
System.out.println(classLoader);
}
}
}
輸出結果:
sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$ExtClassLoader@15db9742
null
分析:剛開始得到系統加載器,然後得到父加載器應用加載器,然後應用加載器的父加載器的爲 null。