Java的classloader

   類加載器的基本概念 java

    類加載器(class loader)用來加載 Java 類到 Java 虛擬機中。通常來講,Java 虛擬機使用 Java 類的方式以下:Java 源程序(.java 文件)在通過 Java 編譯器編譯以後就被轉換成 Java 字節代碼(.class 文件)。類加載器負責讀取 Java 字節代碼,並轉換成 java.lang.Class類的一個實例。每一個這樣的實例用來表示一個 Java 類。經過此實例的 newInstance()方法就能夠建立出該類的一個對象。 web

    基本上全部的類加載器都是 java.lang.ClassLoader類的一個實例。 數據庫

    下面詳細介紹這個 Java 類。 apache

    java.lang.ClassLoader類的基本職責就是根據一個指定的類的名稱,找到或者生成其對應的字節代碼,而後從這些字節代碼中定義出一個 Java 類,即 java.lang.Class類的一個實例。除此以外,ClassLoader還負責加載 Java 應用所需的資源,如圖像文件和配置文件等。爲了完成加載類的這個職責,ClassLoader提供了一系列的方法,比較重要的方法如 表 1所示。關於這些方法的細節會在下面進行介紹。

    表 1. ClassLoader 中與加載類相關的方法 api

方法
說明
getParent()
 返回該類加載器的父類加載器。
loadClass(String name)
加載名稱爲 name的類,返回的結果是 java.lang.Class類的實例
findClass(String name)
查找名稱爲 name的類,返回的結果是 java.lang.Class類的實例
findLoadedClass(String name)
查找名稱爲 name的已經被加載過的類,返回的結果是 java.lang.Class類的實例
defineClass(String name, byte[] b, int off, int len)
把字節數組 b中的內容轉換成 Java 類,返回的結果是 java.lang.Class類的實例。這個方法被聲明爲 final的
resolveClass(Class<?> c)
連接指定的 Java 類
   
     本文將從如下幾個方面來闡述classloader。
 

    1.分類
        1.1.Bootstrap ClassLoader(啓動類加載器)
                加載JAVA_HOME/lib目錄下的核心api 或 -Xbootclasspath 選項指定的jar包裝入工做, 
                是用原生代碼來實現的, 並不繼承自 java.lang.ClassLoader。
        1.2.Extension ClassLoader(擴展類加載器)
                加載 Java 的擴展庫。Java 虛擬機的實現會提供一個擴展庫目錄
                加載JAVA_HOME/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包
        1.3.System ClassLoader(系統類加載器)
                加載java -classpath/-cp/-Djava.class.path所指的目錄下的類與jar包
                它根據 Java 應用的類路徑(CLASSPATH)來加載 Java 類。通常來講,Java 應用的類都是由它來完成加載的。
                能夠經過 ClassLoader.getSystemClassLoader()來獲取它。
                
                每一個classpath以文件名或目錄結尾,該文件名或目錄取決於將類路徑設置成什麼:
                對於包含.class文件的.zip或.jar文件,路徑以.zip或.jar文件名結尾。
                對於未命名包中的.class文件,路徑以包含.class文件的目錄結尾。
                對於已命名包中的.class文件,路徑以包含root包(完整包名中的第一個包)的目錄結尾。
數組

        1.4.自定義類加載器
                經過繼承 java.lang.ClassLoader類的方式實現本身的類加載器,
                用戶自定義 ClassLoader 能夠根據用戶的須要定製本身的類加載過程,在運行期進行指定類的動態實時加載。
             tomcat

    2. 層次結構 安全

         

        這四種類加載器的層次關係如上圖所示。
        通常來講,這四種類加載器會造成一種父子關係,高層爲低層的父加載器。
        能夠經過如下代碼來獲取類加載器, 同時該代碼也演示了類的層次結構         
網絡

public class ClassLoaderTree { 
	public static void main(String[] args) { 
        ClassLoader loader = ClassLoaderTree.class.getClassLoader(); 
        while (loader != null) { 
            System.out.println(loader.toString()); 
            loader = loader.getParent(); 
        } 
    } 
 }
代碼運行結果以下:
sun.misc.Launcher$AppClassLoader@9304b1 
 sun.misc.Launcher$ExtClassLoader@190d11
        第一個輸出的是 ClassLoaderTree類的類加載器, 即系統類加載器。它是 sun.misc.Launcher$AppClassLoader類的實例
        第二個輸出的是擴展類加載器, 是 sun.misc.Launcher$ExtClassLoader類的實例。
        這裏並無輸出引導類加載器, 這是因爲JDK 的實現對於父類加載器是引導類加載器的狀況, getParent()方法返回 null。


    3.加載過程 app

                    

        在進行類加載時,首先會自底向上挨個檢查是否已經加載了指定類,若是已經加載則直接返回該類的引用。
        若是到最高層也沒有加載過指定類,那麼會自頂向下挨個嘗試加載,直到用戶自定義類加載器,若是還不能成功,就會拋出異常。
        直接使用系統加載器加載類失敗拋出的是NoClassDefFoundException異常。
        若是使用自定義的類加載器loadClass方法或者ClassLoader的findSystemClass方法加載類,拋出的是 ClassNotFoundException。
        如下代碼是除 BootstrapClassLoader 外的類加載器加載流程:
    

// 檢查類是否已被裝載過
Class c = findLoadedClass(name);
if (c == null ) {
     // 指定類未被裝載過
     try {
         if (parent != null ) {
             // 若是父類加載器不爲空, 則委派給父類加載
             c = parent.loadClass(name, false );
         } else {
             // 若是父類加載器爲空, 則委派給啓動類加載加載
             c = findBootstrapClass0(name);
         }
     } catch (ClassNotFoundException e) {
         // 啓動類加載器或父類加載器拋出異常後, 當前類加載器將其
         // 捕獲, 並經過findClass方法, 由自身加載
         c = findClass(name);
     }
}
    4.加載類時的幾個原則

        4.1. 代理/雙親委託
                類加載器在嘗試本身去查找某個類的字節代碼並定義它時, 會先代理給其父類加載器, 父加載器也會請求它的父加載器代理加載, 依次類推。

                在介紹代理模式背後的動機以前, 首先須要說明一下 Java 虛擬機是如何斷定兩個 Java 類是相同的。
                Java 虛擬機不只要看類的全名是否相同, 還要看加載此類的類加載器是否同樣。
                只有二者都相同的狀況, 才認爲兩個類是相同的。

                即使是一樣的字節代碼, 被不一樣的類加載器加載以後所獲得的類,也是不一樣的。
                下面經過實例代碼來講明:                

package com.example; 

 public class Sample { 
    private Sample instance; 

    public void setSample(Object instance) { 
        this.instance = (Sample) instance; 
    } 
 }
            測試Java類是否相同:             
public class ClassIdentity {

	public static void main(String[] args) {
		new ClassIdentity().testClassIdentity();
	}
	
	public void testClassIdentity() {
		String classDataRootPath = "C:\\Documents and Settings\\Administrator\\workspace\\Classloader\\classData";
		FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
		FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
		String className = "com.example.Sample";	
		try {
			Class class1 = fscl1.loadClass(className);
			Object obj1 = class1.newInstance();
			Class class2 = fscl2.loadClass(className);
			Object obj2 = class2.newInstance();
			Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class);
			setSampleMethod.invoke(obj1, obj2);
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
}

        測試Java類是否相同的代碼運行結果:        

java.lang.reflect.InvocationTargetException 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597) 
at classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26) 
at classloader.ClassIdentity.main(ClassIdentity.java:9) 
Caused by: java.lang.ClassCastException: com.example.Sample 
cannot be cast to com.example.Sample 
at com.example.Sample.setSample(Sample.java:7)
        運行結果能夠看到,運行時拋出了 java.lang.ClassCastException異常。
        雖然兩個對象 obj1和 obj2的類的名字相同,可是這兩個類是由不一樣的類加載器實例來加載的,所以不被 Java 虛擬機認爲是相同的。
        不一樣的類加載器爲相同名稱的類建立了額外的名稱空間。
        相同名稱的類能夠並存在 Java 虛擬機中,只須要用不一樣的類加載器來加載它們便可。
        不一樣類加載器加載的類之間是不兼容的,這就至關於在 Java 虛擬機內部建立了一個個相互隔離的 Java 類空間。

        
        由於在此模型下用戶自定義的類裝載器不可能裝載應該由父加載器裝載的可靠類,從而防止不可靠甚至惡意的代碼代替由父加載器裝載的可靠代碼。
        例如全部 Java 應用都至少須要引用 java.lang.Object類,也就是說在運行的時候,java.lang.Object這個類須要被加載到 Java 虛擬機中。
        若是這個加載過程由 Java 應用本身的類加載器來完成的話,極可能就存在多個版本的 java.lang.Object類,並且這些類之間是不兼容的。
        經過代理模式,對於 Java 核心庫的類的加載工做由引導類加載器來統一完成,保證了 Java 應用所使用的都是同一個版本的 Java 核心庫的類,是互相兼容的。
        可是實際上,類加載器的編寫者能夠自由選擇不用把請求委託給parent加載器,也就是能夠違背代理原則, 但正如上所說,會帶來安全的問題。

        4.2. 可見性/隔離性
            
            被子加載器加載的類擁有被父加載器加載的類的可見性,但反之則否則。
            自定義類加載器擁有三個其本類加載器加載的全部類的可見性,可是處於不一樣分支的自定義類加載器相互之間不具備可見性。
            所謂不可見即不能直接互相訪問, 也就是即便它們裝載同一個類,也會擁有不一樣的命名空間, 會有不一樣的Class實例。                         
            但若是持有類所對應的Class對象的引用, 仍是能夠訪問另外一命名空間的類。正如示例代碼中咱們經過反射的方式實現了不一樣加載器加載的類的訪問。        

Class<?> class1 = fscl1.loadClass(className); 
        Object obj1 = class1.newInstance(); 
        Class<?> class2 = fscl2.loadClass(className); 
        Object obj2 = class2.newInstance(); 
        Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class); 
        setSampleMethod.invoke(obj1, obj2);

            一樣咱們也能夠利用可見性原則實現不一樣加載器加載的類之間的互訪, 只須要對Sample類稍加改造, 讓其實現ISample接口。

public interface ISample {
	public void setSample(Object instance)
}
public class Sample implements ISample { 
    private Sample instance; 

    public void setSample(Object instance) { 
        this.instance = (Sample) instance; 
    } 
 }


String classDataRootPath = "C:\\Documents and Settings\\Administrator\\workspace\\Classloader\\classData";
		FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
		FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
		String className = "com.example.Sample";	
		try {
			Class<?> class1 = fscl1.loadClass(className);
			ISample obj1 = (ISample)class1.newInstance();
			Class<?> class2 = fscl2.loadClass(className);
			ISample obj2 = (ISample)class2.newInstance(); 
                        obj1.setSample(obj2);                 
                } catch (Exception e) {
			e.printStackTrace();
		}             

            上面示例的代碼中咱們使用自定義類加載器加載了Sample類, 而接口ISample是由系統類加載器加載的, 因此ISample對於Sample是具備可見性的, 所以轉型成功。

        4.3. 惟一性

            咱們繼續分析上面的示例, 使用下面的代碼作轉型   

Class<?> class1 = fscl1.loadClass(className);
Sample obj1 = (Sample)class1.newInstance();

            若是咱們嘗試直接使用如上的代碼來訪問, 會拋出 ClassCastException 異常。
            由於在 Java 中, 即便是同一個類文件,若是是由不一樣的類加載器加載的, 那麼它們的類型是不相同的。
            在上面的例子中class1是由自定義類加載器加載的, 而Sample變量類型聲名和轉型裏的Sample類倒是由類加載器(默認爲 AppClassLoader)加載的, 所以是徹底不一樣的類型, 因此會拋出轉型異常。

            類加載器的代理/雙親委託原則, 決定了每個類在一個加載器裏最多加載一次,  固然多個加載器能夠加載同一個類。
            每一個類對象在各自的namespace內,對類對象進行比較或者對實例進行類型轉換時,會同時比較各自的名字空間。

    5.自定義類加載器
        自定義加載器給Java語言增長了不少靈活性,主要的用途有

  • 能夠從多個地方加載類,好比網絡上,數據庫中,甚至即時的編譯源文件得到類文件;
  • 類加載器能夠在運行時原則性的加載某個版本的類文件;
  • 類加載器能夠動態卸載一些類;
  • 類加載器能夠對類進行解密解壓縮後再載入類

    下面的代碼是上面示例中用到的自定義類加載器的實現類, 功能是從本地文件系統加載類  

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileSystemClassLoader extends ClassLoader {

	private String rootDir;

	public FileSystemClassLoader(String rootDir) {
		this.rootDir = rootDir;
	}

	protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] classData = getClassData(name);
		if (classData == null) {
			throw new ClassNotFoundException();
		}
		else {
			return defineClass(name, classData, 0, classData.length);
		}
	}

	private byte[] getClassData(String className) {
		String path = classNameToPath(className);
		try {
			InputStream ins = new FileInputStream(path);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			int bufferSize = 4096;
			byte[] buffer = new byte[bufferSize];
			int bytesNumRead = 0;
			while ((bytesNumRead = ins.read(buffer)) != -1) {
				baos.write(buffer, 0, bytesNumRead);
			}
			return baos.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private String classNameToPath(String className) {
		return rootDir + File.separatorChar
				+ className.replace('.', File.separatorChar) + ".class";
	}
}
        在該代碼中必需要說明的一點是,  該自定義類加載器的並無指定父加載器。
         JVM規範中規定在不指定父類加載器的狀況下, 默認採用系統類加載器做爲其父加載器, 因此在使用該自定義類加載器時, 須要加載的類不能在類路徑中, 不然的話依據類加載器的代理/委託原則, 待加載類會由系統類加載器加載,
        這樣自定義類加載器想要實現的, 諸如類的熱替換, 多版本共存, 將變的不可實現。
        若是咱們必定想要把自定義加載器須要加載的類放在類路徑中, 應該怎麼辦呢, 答案是把自定義類加載器的父加載器設置爲null。
        JVM規範中規定若是用戶自定義的類加載器將父類加載器強制設置爲null,那麼會自動將啓動類加載器設置爲當前用戶自定義類加載器的父類加載器。
        須要注意的是自定義類加載器不一樣的父加載器決定了加載類的不一樣的可見性。
        下面的代碼示例是一個把自定義類加載器的父加載器設置爲null時, 如何處理加載類的不一樣可見性。
class CustomCL extends ClassLoader { 

    private String basedir; // 須要該類加載器直接加載的類文件的基目錄
    private HashSet dynaclazns; // 須要由該類加載器直接加載的類名

    public CustomCL(String basedir, String[] clazns) { 
        super(null); // 指定父類加載器爲 null 
        this.basedir = basedir; 
        dynaclazns = new HashSet(); 
        loadClassByMe(clazns); 
    } 

    private void loadClassByMe(String[] clazns) { 
        for (int i = 0; i < clazns.length; i++) { 
            loadDirectly(clazns[i]); 
            dynaclazns.add(clazns[i]); 
        } 
    } 

    private Class loadDirectly(String name) { 
        Class cls = null; 
        StringBuffer sb = new StringBuffer(basedir); 
        String classname = name.replace('.', File.separatorChar) + ".class";
        sb.append(File.separator + classname); 
        File classF = new File(sb.toString()); 
        cls = instantiateClass(name,new FileInputStream(classF),
            classF.length()); 
        return cls; 
    }   		

    private Class instantiateClass(String name,InputStream fin,long len){ 
        byte[] raw = new byte[(int) len]; 
        fin.read(raw); 
        fin.close(); 
        return defineClass(name,raw,0,raw.length); 
    } 
    
	protected Class loadClass(String name, boolean resolve) 
            throws ClassNotFoundException { 
        Class cls = null; 
        cls = findLoadedClass(name); 
        if(!this.dynaclazns.contains(name) && cls == null) 
            cls = getSystemClassLoader().loadClass(name); 
        if (cls == null) 
            throw new ClassNotFoundException(name); 
        if (resolve) 
            resolveClass(cls); 
        return cls; 
    } 

}
        在上面的自定義類加載器中, 咱們設置了該自定義類加載器的父加載器爲null, 那麼當咱們在使用自定義類加載器加載的類中引用第三方的類, 例如引用了原本應該是由擴展類加載器或者系統加載器加載的類時, 就會出現不能加載的問題。
        因此咱們在上面的自定義類加載器中, 重寫了loadClass方法, 修改了或者說打破了代理/委託邏輯,  自定義類加載器先嚐試本身加載, 當自定義類加載器不能加載的類,  交由系統加載器來加載。        
protected Class loadClass(String name, boolean resolve) 
            throws ClassNotFoundException { 
            Class cls = null; 
            cls = findLoadedClass(name); 
            if(!this.dynaclazns.contains(name) && cls == null) 
                cls = getSystemClassLoader().loadClass(name); 
            if (cls == null) 
                throw new ClassNotFoundException(name); 
            if (resolve) 
                resolveClass(cls); 
            return cls; 
        }
     6. 類加載方式
        除了上面提到的經過自定義類加載器加載類, 咱們一般會使用下面的兩種方式來加載類

        6.1. 隱式加載                

A a = new A();
               若是程序運行到這段代碼時尚未A類,那麼JVM會請求裝載當前類的類裝器來裝載類。

        6.2. 顯示加載                                 

//效果相同, 執行類的初始化
Class.forName("test.A");
Class.forName("test.A", true, this.getClass().getClassLoader());
//效果相同, 不執行類的初始化
getClass().getClassLoader().loadClass("test.A");
Class.forName("test.A", false, this.getClass().getClassLoader());
//效果相同, 不執行類的初始化
ClassLoader.getSystemClassLoader().loadClass("test.A");
Class.forName("test.A", false, Classloader.getSystemClassLoader());
//效果相同, 不執行類的初始化
Thread.currentThread().getContextClassLoader().loadClass("test.A")
Class.forName("test.A", false, Thread.currentThread().getContextClassLoader());
    7. 上下文類加載器( ContextClassLoader)
        類 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設置線程的上下文類加載器。
        若是沒有經過 setContextClassLoader(ClassLoader cl)方法進行設置的話,線程將繼承其父線程的上下文類加載器。
        Java 應用運行的初始線程的上下文類加載器是系統類加載器。

        在線程中運行的代碼能夠經過此類加載器來加載類和資源。
        正常的雙親委派模型中,下層的類加載器可使用上層父加載器加載的對象,可是上層父類的加載器不可使用子類加載的對象。
        而有些時候程序的確須要上層調用下層,這時候就須要線程上下文加載器來處理。     

Thread.currentThread().getContextClassLoader()
        前面提到的類加載器的代理模式並不能解決 Java 應用開發中會遇到的類加載器的所有問題。
        Java 提供了不少服務提供者接口(Service Provider Interface,SPI),容許第三方爲這些接口提供實現。
        常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。這些 SPI 的接口由 Java 核心庫來提供,如 JAXP 的 SPI 接口定義包含在 javax.xml.parsers包中。
        這些 SPI 的實現代碼極可能是做爲 Java 應用所依賴的 jar 包被包含進來,能夠經過類路徑(CLASSPATH)來找到,如實現了 JAXP SPI 的 Apache Xerces所包含的 jar 包。
        SPI 接口中的代碼常常須要加載具體的實現類。如 JAXP 中的 javax.xml.parsers.DocumentBuilderFactory類中的 newInstance()方法用來生成一個新的 DocumentBuilderFactory的實例。
        這裏的實例的真正的類是繼承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的實現所提供的。如在 Apache Xerces 中,實現的類是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。
        而問題在於,SPI 的接口是 Java 核心庫的一部分,是由引導類加載器來加載的;SPI 實現的 Java 類通常是由系統類加載器來加載的。引導類加載器是沒法找到 SPI 的實現類的,由於它只加載 Java 的核心庫。
        它也不能代理給系統類加載器,由於它是系統類加載器的祖先類加載器。也就是說,類加載器的代理模式沒法解決這個問題。
        線程上下文類加載器正好解決了這個問題。
        若是不作任何的設置,Java 應用的線程的上下文類加載器默認就是系統上下文類加載器。
        在 SPI 接口的代碼中使用線程上下文類加載器,就能夠成功的加載到 SPI 實現的類。線程上下文類加載器在不少 SPI 的實現中都會用到。
        
        在5.自定義類加載器中咱們的自定義類加載器CustomCL, 若是放到tomcat下的web應用中去使用會出現什麼問題呢, 例如在自定義加載器 待加載的類中使用第三方類, 這個時候自定義加載器不能加載的類會交由系統加載器加載,  而該第三類不存在於類路徑中, 只存在於該webApp下,  顯然是加載不到的, 爲了解決這個問題,  這個時候咱們就須要上下文類加載器來解決這個問題了。

        在給出代碼以前先說下Tomcat.6的類加載器, 結構層次以下:     

+-----------------------------+
        |         Bootstrap           |
        |             |               |
        |          System             |
        |             |               |
        |          Common             |
        |         /      \            |
        |     WebApp1  WebApp2        |
        |                             |
        |                             |
        +-----------------------------+
       Webapp 類裝載器:
      應用層的類裝載器,每一個應用程序都會建立一個單獨的類裝載器。該類裝載器只能本應用程序中可見。
      全部/WEB-INF/classes目錄下未壓縮的類文件,資源文件都由該類裝載器加載。
      全部/WEB-INF/lib目錄下壓縮後Jar/zip文件都由該類裝載器加載
  
      顯然上面咱們說到的問題應該用webapp類加載器來加載第三方類, 那咱們在自定義類加載器中如何得到webapp類加載器呢,  在tomcat6中啓動webapp線程的上下文類加載器被設置爲webapp類加載器了, 因此咱們能夠經過以下代碼來完成加載。
protected Class loadClass(String name, boolean resolve) 
            throws ClassNotFoundException { 
        Class cls = null; 
        cls = findLoadedClass(name); 
        if(!this.dynaclazns.contains(name) && cls == null) 
            cls = getSystemClassLoader().loadClass(name);
        if (cls == null)
                //自定義加載器和系統加載器均不能正常加載的類, 交由上下文加載器加載
        	cls =  Thread.currentThread().getContextClassLoader().loadClass(name);
        if(cls == null)
            throw new ClassNotFoundException(name); 
        if (resolve) 
            resolveClass(cls); 
        return cls; 
    }
        其實ContextClassLoader就是Thread的一個屬性而已,  咱們固然能夠不使用ContextClassLoader, 本身找個地方把classLoader保存起來, 在須要獲取的時候能獲得此classLoader就能夠。

     8. 自定義類加載器的其餘應用

        8.1. 熱加載
                每次建立一個新的類加載器, 咱們修改下上面示例中的ClassIdentity類, 讓他能夠實現熱加載。           

public class ClassIdentity  extends Thread {

    public static void main(String[] args) {
        new ClassIdentity().start();
    }
    public void run() {
        while(true) {
            this.testClassIdentity();
            try {
                Thread.sleep(30 * 1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }           
        }       
    }
    public void testClassIdentity() {
        String classDataRootPath = "C:\\Documents and Settings\\Administrator\\workspace\\Classloader\\classData";
        FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
        FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
        String className = "com.example.Sample";    
        try {
            Class<?> class1 = fscl1.loadClass(className);
            Object obj1 = class1.newInstance();
            Class<?> class2 = fscl2.loadClass(className);
            Object obj2 = class2.newInstance();
            Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class);
            setSampleMethod.invoke(obj1, obj2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

        運行該代碼, 在運行過程當中咱們修改Sample類, 並覆蓋原Sample類。

        8.2. 類加密                 指通常意義上的加密,  經過自定義加載器解密載入加密類         8.3. 應用隔離                 很是典型的應用就是web容器

相關文章
相關標籤/搜索