天天看點

實作java中自定義類加載器

前言:

由于類的加載是采用委托模式,及先是父類加載器去加載加載失敗才是子類

要實作一個類加載器不是複寫loadClass而是複寫findClass

源碼主要邏輯就幾句:

注意:這個findClass調用的源碼:

    protected Class<?> loadClass(String name, boolean resolve)

        throws ClassNotFoundException

    {

        synchronized (getClassLoadingLock(name)) {

            // First, check if the class has already been loaded

            Class<?> c = findLoadedClass(name);

            if (c == null) {

                long t0 = System.nanoTime();

                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.

                    long t1 = System.nanoTime();

                    c = findClass(name);//如果還沒有早到就調用自己的findClass來加載

                    // this is the defining class loader; record the stats

                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

                    sun.misc.PerfCounter.getFindClasses().increment();

                }

            }

            if (resolve) {

                resolveClass(c);

            }

            return c;

        }

    }

1. 複寫findClass方法

2. 擷取這個.class檔案所對應的二進制流

3. 再在這個findClass最後傳回 return defineClass(null, data, 0, data.length);

public class MyClassLoader extends ClassLoader{

   private String path;//這裡用來記錄加載的路徑(相對eclipse所在項目)

   private final String fileType =".class";

   private int key =0;//預設不加密

   public MyClassLoader(String path){

    this.path =System.getProperty("user.dir")+"\\"+ path;//這個路徑是你eclipse項目路徑加上你輸入的路徑

   }

   public MyClassLoader() {

 super();

}

public MyClassLoader(ClassLoader parent) {

 super(parent);

}

public void setKey(int key) {

 this.key = key;

}

public void setPath(String path) {

 this.path = path;

}

private byte[] loadClassData(String name){//把要加載的.class變成二進制數組

    if(null==path)

     path = System.getProperty("user.dir");

    if(!path.endsWith("\\")){

     path+="\\";

    }

    String filename = path + name.substring(name.lastIndexOf(".")+1) +fileType;

    ByteArrayOutputStream bos = new ByteArrayOutputStream();//這個是将檔案中擷取的二進制存放用的

    try(

      BufferedInputStream bis =new BufferedInputStream(new FileInputStream(filename));

      ){

      byte[] buf  = new byte[1024];

      int len = -1;

      while((len=bis.read(buf))!=-1){

       for(int i=0;i<len;i++)

        buf[i]+=key;

       bos.write(buf, 0, len);

      }   

      return bos.toByteArray();

    }catch(Exception e){

     e.printStackTrace();

     throw new RuntimeException();

    }

   }

@Override

protected Class<?> findClass(String name) throws ClassNotFoundException {

  byte[] data = loadClassData(name);

 return defineClass(null, data, 0, data.length);

}

}

可能碰到的問題:

 1.總不能掉用你複寫的findClass方法  原因:你調用loadClass的路徑會被應用類加載器正确解析做法:如果你要加載的類在com.test.Hello (最後的Hello為類名)你可以寫成loadClass(com.Hello) 不過你自己要在你的自定義的類加載器中給它拼去一個完整的路徑。

      我的上面的做法時我隻要你傳人的類名 name.substring(name.lastIndex(".")+1) (因為loadClass(name) 父類加載不了時它會傳人到findClass(name)) 餘下的我自定義類中有path

2. 為什麼我們加載成功後的類不能轉成原有的對象而隻能轉成它的父類。

如果我們加載的類為Hello 它沒有任何父類時它隻能轉成Object... 可能是我們defineClass中第一個參數傳人null的原有

上面可能有不足如果有高手指點很樂意

繼續閱讀