Java ClassLoader 學習理解

/**
 * <html>
 * <body>
 *  <P> Copyright 1994 JsonInternational</p>
 *  <p> All rights reserved.</p>
 *  <p> Created on 19941115</p>
 *  <p> Created by Jason</p>
 *  </body>
 * </html>
 */
package cn.ucaner.core.classload;

import java.net.URL;

/**
* @Package:cn.ucaner.core.classload   
* @ClassName:LoaderTest   
* @Description:   <p> 類加載器學習
* https://blog.csdn.net/eff666/article/details/52203406
* 
* 一個java文件從被加載到被卸載這個生命過程,總共要經歷5個階段,JVM將類加載過程分爲:
* 加載->連接(驗證+準備+解析)->初始化(使用前的準備)->使用->卸載 
* 
* (1)加載 
  首先經過一個類的全限定名來獲取此類的二進制字節流;其次將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構;
    最後在java堆中生成一個表明這個類的Class對象,做爲方法區這些數據的訪問入口。總的來講就是查找並加載類的二進制數據。 
(2)連接: 
  驗證:確保被加載類的正確性; 
  準備:爲類的靜態變量分配內存,並將其初始化爲默認值; 
  解析:把類中的符號引用轉換爲直接引用; 
(3)爲類的靜態變量賦予正確的初始值 

三、類的初始化 
(1)類何時才被初始化 
  1)建立類的實例,也就是new一個對象 
  2)訪問某個類或接口的靜態變量,或者對該靜態變量賦值 
  3)調用類的靜態方法 
  4)反射Class.forName("cn.ucaner.core.classload.LoaderTest")
  5)初始化一個類的子類(會首先初始化子類的父類) 
  6)JVM啓動時標明的啓動類,即文件名和類名相同的那個類 
(2)類的初始化順序 
  1)若是這個類尚未被加載和連接,那先進行加載和連接 
  2)假如這個類存在直接父類,而且這個類尚未被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用於接口) 
  3)加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。 
  4)總的來講,初始化順序依次是:(靜態變量、靜態初始化塊)–>(變量、初始化塊)–> 構造器;若是有父類,則順序是:父類static方法 –> 子類static方法 –> 父類構造方法- -> 子類構造方法 
四、類的加載 
  類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,而後在堆區建立一個這個類的java.lang.Class對象,用來封裝類在方法區類的對象。如: 
    
    類的加載的最終產品是位於堆區中的Class對象。Class對象封裝了類在方法區內的數據結構,而且向Java程序員提供了訪問方法區內的數據結構的接口。加載類的方式有如下幾種: 
  1)從本地系統直接加載 
  2)經過網絡下載.class文件 
  3)從zip,jar等歸檔文件中加載.class文件 
  4)從專有數據庫中提取.class文件 
  5)將Java源文件動態編譯爲.class文件(服務器) 

五、加載器 
  JVM的類加載是經過ClassLoader及其子類來完成的,類的層次關係和加載順序能夠由下圖來描述: 

(1)加載器介紹 
1)BootstrapClassLoader(啓動類加載器)   - 父加載器  -Top 
  負責加載$JAVA_HOME中jre/lib/rt.jar裏全部的class,加載System.getProperty("sun.boot.class.path")所指定的路徑或jar。 
2)ExtensionClassLoader(標準擴展類加載器) 
  負責加載java平臺中擴展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目錄下的jar包。載System.getProperty(「java.ext.dirs」)所指定的路徑或jar。 
3)AppClassLoader(系統類加載器) 
  負責記載classpath中指定的jar包及目錄中class 
4)CustomClassLoader(自定義加載器) 
  屬於應用程序根據自身須要自定義的ClassLoader,如tomcat、jboss都會根據j2ee規範自行實現。

(2)類加載器的順序 
1)加載過程當中會先檢查類是否被已加載,[檢查順序][自底向上],從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只全部ClassLoader加載一次。
而[加載]的順序是[自頂向下],也就是由上層來逐層嘗試加載此類。 
2)在加載類時,每一個類加載器會將加載任務上交給其父,若是其父找不到,再由本身去加載。 

3)Bootstrap Loader(啓動類加載器)是最頂級的類加載器了,其父加載器爲null。
* </p>
* @Author: - Jason   
* @CreatTime:2018年4月10日 下午9:44:12   
* @Modify By:   
* @ModifyTime:  2018年4月10日
* @Modify marker:   
* @version    V1.0
 */
public class LoaderTest {  
  
    @SuppressWarnings("static-access")
    public static void main(String[] args) {  
        try {  
            System.out.println(ClassLoader.getSystemClassLoader());  
            System.out.println(ClassLoader.getSystemClassLoader().getParent());  
            System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
            URL systemResource1 = ClassLoader.getSystemClassLoader().getSystemResource("spring-mvc.xml");
            URL systemResource2 = ClassLoader.getSystemClassLoader().getSystemResource("classpath:spring-mvc.xml");
            System.out.println(systemResource1);
            System.out.println(systemResource2);
            
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}  
//Outputs 
//sun.misc.Launcher$AppClassLoader@2a139a55
//sun.misc.Launcher$ExtClassLoader@7852e922
//null
//null

 

 

/**
 * <html>
 * <body>
 *  <P> Copyright 1994 JsonInternational</p>
 *  <p> All rights reserved.</p>
 *  <p> Created on 19941115</p>
 *  <p> Created by Jason</p>
 *  </body>
 * </html>
 */
package cn.ucaner.core.classload.test;  
  
/**
* @Package:cn.ucaner.core.classload.test   
* @ClassName:ClassLoaderTest   
* @Description:   <p> ClassLoaderTest </p>
* @Author: - Jason   
* @CreatTime:2018年10月19日 下午3:18:23   
* @Modify By:   
* @ModifyTime:  2018年10月19日
* @Modify marker:   
* @version    V1.0
 */
public class ClassLoaderTest {  
  
    public static void main(String[] args) {  
        try {  
            
            //查看當前系統類路徑中包含的路徑條目  
            String property = System.getProperty("java.class.path");
            String[] split = property.split(";");
            for (String str : split) {
                System.out.println(str);  
            }
            
            //調用加載當前類的類加載器(這裏即爲系統類加載器)加載TestBean  
            Class<?> typeLoaded = Class.forName("cn.ucaner.core.classload.TestBean");  
            
            //查看被加載的TestBean類型是被那個類加載器加載的  
            System.out.println(typeLoaded.getClassLoader());  
            //sun.misc.Launcher$AppClassLoader@2a139a55
            
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}  

 

/**
 * <html>
 * <body>
 *  <P> Copyright 1994 JsonInternational</p>
 *  <p> All rights reserved.</p>
 *  <p> Created on 19941115</p>
 *  <p> Created by Jason</p>
 *  </body>
 * </html>
 */
package cn.ucaner.core.classload.fileload;  
  
import java.io.ByteArrayOutputStream;  
import java.io.InputStream;  
import java.net.URL;  
  
/**
* @Package:cn.ucaner.core.classload.fileload   
* @ClassName:NetworkClassLoader   
* @Description:   <p> 網絡類加載器</p>
* @Author: - Jason   
* @CreatTime:2018年4月10日 下午9:43:13   
* @Modify By:   
* @ModifyTime:  2018年4月10日
* @Modify marker:   
* @version    V1.0
 */
public class NetworkClassLoader extends ClassLoader {  
  
    private String rootUrl;  
  
    public NetworkClassLoader(String rootUrl) {  
        // 指定URL  
        this.rootUrl = rootUrl;  
    }  
  
    // 獲取類的字節碼  
    @Override  
    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 {  
            URL url = new URL(path);  
            InputStream ins = url.openStream();  
            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 (Exception e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
  
    private String classNameToPath(String className) {  
        // 獲得類文件的URL  
        return rootUrl + "/"  
                + className.replace('.', '/') + ".class";  
    }  
}  
/**
 * <html>
 * <body>
 *  <P> Copyright 1994 JsonInternational</p>
 *  <p> All rights reserved.</p>
 *  <p> Created on 19941115</p>
 *  <p> Created by Jason</p>
 *  </body>
 * </html>
 */
package cn.ucaner.core.classload.fileload;  
  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.ByteArrayOutputStream;  

/**
* @Package:cn.ucaner.core.classload.fileload   
* @ClassName:FileSystemClassLoader   
* @Description:   <p> 文件系統類加載器  </p>
* @Author: - Jason   
* @CreatTime:2018年4月10日 下午9:42:47   
* @Modify By:   
* @ModifyTime:  2018年4月10日
* @Modify marker:   
* @version    V1.0
 */
public class FileSystemClassLoader extends ClassLoader {  
  
    private String rootDir;  
  
    public FileSystemClassLoader(String rootDir) {  
        this.rootDir = rootDir;  
    }  
  
    // 獲取類的字節碼  
    @Override  
    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";  
    }  
}  
相關文章
相關標籤/搜索