實現一個簡單的自定義類加載器

java虛擬機中,有一個類加載子系統,它包括了四種類加載器

1、根裝載器(啓動類裝載器)2、擴展類裝載器3、系統類裝載器4、用戶自定義類加載器

根加載器負責加載API裏面的類,例如java.lang.object

擴展類裝載器複製加載jre中ext包中的類,如C:\Program Files\Java\jdk1.6.0_21\jre\lib\ext

系統類裝載器加載classpath中的類,記得我們配置JDK環境變量的時候如何配置classpath的嗎?.;C:\Program Files\Java\jdk1.5.0_06\lib\tools.jar; C:\Program Files\Java\jdk1.5.0_06\lib\rt.jar,這些就是classpath裏面的類,你程序中的類,也是由它加載的,因爲你程序屬於當前路徑,classpath中有個“.”

補充個知識點(摘至百度):

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

rt.jar ,dt.jar ,tool.jar都是 做什麼用的 ,分別什麼時候需要設置到classpath裏?
---------------------------------------------------------------

rt.jar是JAVA基礎類庫,dt.jar是關於運行環境的類庫,tools.jar是工具類庫

設置在classpath裏是爲了讓你 import *
---------------------------------------------------------------

web系統都用到tool.jar

你用winrar看看裏面是什麼內容啦
---------------------------------------------------------------

1.
rt.jar 默認就在 根classloader的加載路徑裏面 放在claspath是多此一舉
不信你可以去掉classpath裏面的rt.jar

然後用 java -verbose XXXX 的方式運行一個簡單的類 就知道 JVM的系統根Loader的路徑裏面

不光rt.jar jre\lib下面的大部分jar 都在這個路徑裏 

2.

tools.jar 是系統用來編譯一個類的時候用到的 也就是javac的時候用到

javac XXX.java

實際上就是運行 

java -Calsspath=%JAVA_HOME%\lib\tools.jar  xx.xxx.Main  XXX.java 

javac就是對上面命令的封裝 所以tools.jar 也不用加到classpath裏面

3.
dt.jar是關於運行環境的類庫,主要是swing的包   你要用到swing時最好加上

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

類裝載器是如何裝載類的呢?

類裝載器採用了父親委派模式來進行加載,父親委派模式是這樣的,要加載一個類的時候,首先叫負責加載該類的類裝載器的父裝載器去嘗試加載,如果加載不了,再往上拋,一直拋到根加載器,如果根加載器還加載不了,那麼就讓負責加載該類的類裝載器來加載。

根裝載器沒有父裝載器,它是C實現的,所以在程序中獲取不到它,擴展類裝載器的父裝載器是根裝載器,系統類裝載器的父裝載器是擴展類裝載器,而用戶自定義的類裝載器,默認情況下是系統類裝載器,當然,在自定義類裝載器的時候,是可以指定父裝載器的。

講完了加載的順序,那麼我們就來嘗試着寫一個類裝載器,讓它裝載固定某個地方的類


首先寫一個自己的類加載器,MyClassLoader.java,繼承ClassLoader類,然後重寫findClass方法:

package com.wyp12.myClassLoader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
 * 自己寫一個類加載器 ,去加載"d:\myclass\com\wyp12\*.class"
 * 
 * @author prince
 * 
 */
public class MyClassLoader extends ClassLoader {
	private String name;
	public MyClassLoader(String name) {
		super(); // 通過這個構造方法生成的類加載器,它的父加載器是系統類加載器
		this.name = name;
	}
	public MyClassLoader(String name, ClassLoader loader) {
		super(loader); // 通過這個這個構造方法生成的類加載器,該加載器的父加載器是loader,如果爲空,則父加載器爲根加載器
		// 子類繼承父類,如果不顯式寫出調用父類的哪個構造方法,那麼就默認調用父類的無參構造函數
		this.name = name;
	}
	public String toString()
	{
		return this.name;
	}
	// 要重寫findclass這個方法,loadclass會調用它
	 @Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		 byte[] data = null;
		 FileInputStream fis = null;
		try {
			fis = new FileInputStream("d:\\myclass\\com\\wyp12\\myClassLoader\\"+name+".class");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		 ByteArrayOutputStream abos = new ByteArrayOutputStream();
		 int ch = 0;
		 try {
			while (-1!=(ch=fis.read()))
			 {
				  abos.write(ch);        //把字節一個一個寫到輸出流中
			 }
		} catch (IOException e) {
			e.printStackTrace();
		}
		 data = abos.toByteArray();   //把輸出流中的字節弄成一個字節數組
		return this.defineClass("com.wyp12.myClassLoader."+name,data, 0, data.length,null);
           }
}
這個類加載器我讓它默認加載
d:\myclass\com\wyp12\myClassLoader
路徑下的class文件,具體文件名到時候我們構造實例的時候再給定,下面是測試類:

import com.wyp12.myClassLoader.MyClassLoader;
public class TestLoader {
	public static void main(String[] args) throws Exception {
		MyClassLoader l1 = new MyClassLoader("loader1");
	 	Class dogC = l1.loadClass("Dog");
	 	dogC.newInstance();
		/*MyClassLoader l2 = new MyClassLoader("loader2",l1);  //把L1作爲它的父加載器
		Class doccc = l2.loadClass("Dog");
		doccc.newInstance(); */
	}
}

下面是要加載的類的源代碼,很簡答,就是讓它打印出是誰加載了他

package com.wyp12.myClassLoader;

public class Dog {
    public Dog()
    {
        System.out.println(this.getClass().getClassLoader());
    }
}


運行結果:

loader1


下面貼上ClassLoader的主要源代碼:

它的幾個構造函數

protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }
protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }
protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

通過幾個函數,把字節碼數組轉換成一個CLASS實例

  protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                     ProtectionDomain protectionDomain)
    throws ClassFormatError
    {
         return defineClassCond(name, b, off, len, protectionDomain, true);
    }

    // Private method w/ an extra argument for skipping class verification
    private final Class<?> defineClassCond(String name,
                                           byte[] b, int off, int len,
                                           ProtectionDomain protectionDomain,
                                           boolean verify)
        throws ClassFormatError
    {
    protectionDomain = preDefineClass(name, protectionDomain);

    Class c = null;
        String source = defineClassSourceLocation(protectionDomain);

    try {
        c = defineClass1(name, b, off, len, protectionDomain, source,
                             verify);
    } catch (ClassFormatError cfe) {
        c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
                                       source, verify);
    }

    postDefineClass(c, protectionDomain);
    return c;
    }

下面這個是loadClass()

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
    }
protected synchronized Class<?> loadClass(String name, boolean resolve)
	throws ClassNotFoundException
    {
	// First, check if the class has already been loaded
	Class c = findLoadedClass(name);
	if (c == null) {
	    try {
		if (parent != null) {
		    c = parent.loadClass(name, false);
		} else {
		    c = findBootstrapClassOrNull(name);
		}
	    } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
	        // If still not found, then invoke findClass in order
	        // to find the class.
	        c = findClass(name);
	    }
	}

下面是我們要重寫的方法:

protected Class<?> findClass(String name) throws ClassNotFoundException {
	throw new ClassNotFoundException(name);
    }

基本就這樣了,其實還很多細節,由於自己的文筆也不是很好,就不寫出來了,只是做個mark,以後便於複習























發佈了56 篇原創文章 · 獲贊 8 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章