Java類載入器(二)——本身定義類載入器

  用戶定製本身的ClassLoader可以實現如下的一些應用:java

  1. 本身定義路徑下查找本身定義的class類文件,或許咱們需要的class文件並不老是在已經設置好的Classpath如下,那麼咱們必須想辦法來找到這個類,在這樣的清理下咱們需要本身實現一個ClassLoader。

  2. 確保安全性:Java字節碼很是easy被反編譯,對咱們本身的要載入的類作特殊處理,如保證經過網絡傳輸的類的安全性。可以將類通過加密後再傳輸,在加密到JVM以前需要對類的字節碼在解密。這個過程就可以在本身定義的ClassLoader中實現。

  3. 實現類的熱部署:可以定義類的實現機制。假設咱們可以檢查已經載入的class文件是否被改動,假設改動了。可以又一次載入這個類。

  findClass()的功能是找到class文件並把字節碼載入到內存中。本身定義的ClassLoader通常覆蓋改方法。以便使用不一樣的載入路徑,而後調用defineClass()解析字節碼。安全


  defineClass()方法用來將byte字節流解析成JVM可以識別的Class對象。markdown

有了這種方法意味着咱們不單單可以經過class文件實例化對象。還可以經過其它方式實例化對象,如咱們經過網絡接收到一個類的字節碼,拿這個字節碼流直接建立類的Class對象形式實例化對象。網絡


  本身定義的載入器可以覆蓋方法loadClass()以便定義不一樣的載入機制。
  假設本身定義的載入器僅覆蓋了findClass(),而未覆蓋loadClass(即載入規則同樣,但載入路徑不一樣);則調用getClass().getClassLoader()返回的仍然是AppClassLoader!因爲真正的load類,仍是AppClassLoader.ide


載入本身定義路徑下的class文件

  如下演示一個方法載入指定路徑下(」D:/workspace_jee/JavaTest/src/」)的類文件。post

this

package classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class PathClassLoader extends ClassLoader { private String classPath; public PathClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?

> findClass(String name) throws ClassNotFoundException { byte[] classData = getData(name); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] getData(String className) { String path = classPath + File.separatorChar+className.replace('.', File.separatorChar)+".class"; try { InputStream is = new FileInputStream(path); ByteArrayOutputStream stream = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; int num = 0; while((num = is.read(buffer))!=-1) { stream.write(buffer,0,num); } return stream.toByteArray(); } catch(IOException e) { e.printStackTrace(); } return null; } public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader pcl = new PathClassLoader("D:/workspace_jee/JavaTest/src/"); Class c = pcl.loadClass("classloader.SingleClass"); System.out.println(c.newInstance()); } }

  輸出結果:classloader.SingleClass@22a7fdef加密


載入本身定義格式的class文件

  假設咱們從網路上下載一個class文件的字節碼,但是爲了安全性在傳輸以前對這個字節碼進行了簡單的加密處理,而後再經過網絡傳輸。spa

當client接收到這個類的字節碼後需要通過解密才幹還原成原始的類格式。而後再經過ClassLoader的defineClass()方法建立這個類的實例,最後完畢類的載入工做。code


  比方上面的代碼中,在獲取到字節碼(byte[] classData = getData(name);)以後再經過一個類似如下的代碼:

private byte[] deCode(byte[] src){
        byte[] decode = null;
        //do something niubility! 精密解碼過程
        return decode; 
    }

  將字節碼解碼成所需要的字節碼就能夠。


實現類的熱部署

  JVM默認不能熱部署類,因爲載入類時會去調用findLoadedClass(),假設類已被載入,就不會再次載入。
  JVM推斷類是否被載入有兩個條件:完整類名是否同樣,ClasssLoader是不是同一個
  因此要實現熱部署的話,僅僅需要使用ClassLoader的不一樣實例來載入。


  假設用同一個ClassLoader實例來載入同一個類,則會拋出LinkageError.
  Jsp就是一個熱部署的樣例。
  例如如下所看到的:

package classloader;

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

public class ClassReloader extends ClassLoader {
    private String classPath;
    String classname = "classloader.SingleClass";

    public ClassReloader(String classpath)
    {
        this.classPath = classpath;
    }

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

    private byte[] getData(String className)
    {
        String path = classPath+className;
        try
        {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            byte[] buffer = new byte[2048];
            int num = 0;
            while((num = is.read(buffer))!=-1)
            {
                stream.write(buffer,0,num);
            }
            return stream.toByteArray();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args)
    {
        try
        {
            String path = "D:/workspace_jee/JavaTest/src/classloader/";
            ClassReloader reloader = new ClassReloader(path);
            Class r = reloader.findClass("SingleClass.class");
            System.out.println(r.newInstance());
// ClassReloader reloader2 = new ClassReloader(path);
            Class r2 = reloader.findClass("SingleClass.class");
            System.out.println(r2.newInstance());
        }
        catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
        {
            e.printStackTrace();
        }
    }
}

  這段代碼的執行結果爲:

java.lang.LinkageError: loader (instance of  classloader/ClassReloader): attempted  duplicate class definition for name: "classloader/SingleClass"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at classloader.ClassReloader.findClass(ClassReloader.java:26)
    at classloader.ClassReloader.main(ClassReloader.java:62)

  比較兩個類是否「相等」,僅僅有在這兩個類是由同一個類載入器載入的前提下才有意義。不然。即便這兩個類來源於同一個Class文件,被同一個虛擬機載入,僅僅要載入他們的類載入器不一樣,那這兩個類就必然不相等。

相關文章
相關標籤/搜索