程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> java自定義類加載器

java自定義類加載器

編輯:JAVA編程入門知識

前言

  java反射,最常用的Class.forName()方法。做畢設的時候,接收到代碼字符串,通過 JavaCompiler將代碼字符串生成A.class文件(存放在classpath下,也就是eclipse項目中的bin目錄裡),然後通過java反射機制,獲取main方法並執行。.class文件名稱固定。當 A.class文件更新的時候,問題出現了,main方法的執行結果總和第一次的執行結果相同。

程序流程

  代碼提交->接收代碼->編譯成A.class文件->java反射->main方法執行

  具體代碼參考:http://www.cnblogs.com/hujunzheng/p/5203067.html

問題原因

  類加載器的委托機制!說到這裡,不得不介紹一下java的類加載器。

java虛擬機中的類加載器

  java虛擬機中可以安裝多個類加載器,系統默認三個主要的類加載器,每個類負責加載特定位置的類: BootStrap,ExtClassLoader,AppClassLoader

  類加載器也是Java類,因為Java類的類加載器本身也是要被類加載器加載的,顯然必須有第一個類加載器不是Java類,這個正是BootStrap,使用C/C++代碼寫的,已經封裝到JVM內核中了,而ExtClassLoader和AppClassLoader是Java類。

類加載器的屬性結構圖

  盜圖一張:

由此得到結論

  首先我的A.class文件更新了,接著調用Class.forName()[我想的是重新加載一下字節碼文件對象],然後最終由AppClassLoader去加載,其中有一個函數很重要,就是loadClass(), 看一下這個函數的源碼,如下:

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{  
 //加上鎖,同步處理,因為可能是多線程在加載類  
 synchronized (getClassLoadingLock(name)) {  
     //檢查,是否該類已經加載過了,如果加載過了,就不加載了  
     Class c = findLoadedClass(name);  
     if (c == null) {  
         long t0 = System.nanoTime();  
         try {  
             //如果自定義的類加載器的parent不為null,就調用parent的loadClass進行加載類  
             if (parent != null) {  
                 c = parent.loadClass(name, false);  
             } else {  
                 //如果自定義的類加載器的parent為null,就調用findBootstrapClass方法查找類,就是Bootstrap類加載器  
                 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();  
             //如果parent加載類失敗,就調用自己的findClass方法進行類加載  
             c = findClass(name);  

             // 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;  
 }  
}  

  如果同名的.class文件之前加載了就不會在加載了。。。

解決辦法  用戶自定義類加載器

  想法1: 重寫loadClass()這個函數,無論是否加載過.class問價,都重新加載。

   @Override
    public java.lang.Class<?> loadClass(String name) throws ClassNotFoundException {
        System.out.println(name);
        byte[] data = loaderClassData(name);
        return this.defineClass(name, data, 0, data.length);
    };

  但是竟然出錯了,至今還沒有搞明白... Main是我要加載的類,loadClass()函數執行了兩次,第二次不知道怎麼調用的。。。?有誰知到,告訴我一下,謝了!

Main
java.lang.Object
java.io.FileNotFoundException: java\lang\Object.class (系統找不到指定的路徑。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at com.ds.tools.MyClassLoader.loaderClassData(MyClassLoader.java:53)
    at com.ds.tools.MyClassLoader.loadClass(MyClassLoader.java:78)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at com.ds.tools.MyClassLoader.loadClass(MyClassLoader.java:79)
    at com.ds.tools.MyClassLoader.main(MyClassLoader.java:96)

  想法2: 只能默默的重寫findClass()方法了, loadClass()方法中會調用這個函數,為了避過AppClassLoader檢查類是否已經加載過了,我把A.class的生成位置放到了項目根目錄下的myClass目錄中,這樣MyClassLoader委托AppClassLoader對A.class進行加載時,在當前的classpath下找不到對應的類,無法完成類的加載(同樣BootStrapLoader和ExtClassLoader都不會找到),最終是我們自定的類加載器完成類的加載,代碼如下:

public class MyClassLoader extends ClassLoader {
    //類加載器名稱
    private String loaderName;
    //加載類的路徑
    private String path = "";
    private final String fileType = ".class";
    public MyClassLoader(String loaderName){
        //讓系統類加載器成為該 類加載器的父加載器
        super();
        this.loaderName = loaderName;
    }

    public MyClassLoader(ClassLoader parent, String loaderName){
        //顯示指定該類加載器的父加載器
        super(parent);
        this.loaderName = loaderName;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public String toString() {
        return this.loaderName;
    }

    /**
     * 獲取.class文件的字節數組
     * @param name
     * @return
     */
    private byte[] loaderClassData(String name){
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        name = name.replace(".", "/");
        try {
            is = new FileInputStream(new File(path + name + fileType));
            int c = 0;
            while(-1 != (c = is.read())){
                baos.write(c);
            }
            data = baos.toByteArray();

        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                if(is != null)
                    is.close();
                if(baos != null)
                    baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }
    
    /**
     * 獲取Class對象
     */
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException{
        byte[] data = loaderClassData(name);
        return this.defineClass(name, data, 0, data.length);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
        for(int i=0; i<5; i++){
            MyClassLoader loader1 = new MyClassLoader("MyClassLoader");
            //String path = new File(MyClassLoader.getSystemClassLoader().getResource("").getPath()).getParent();
            loader1.setPath("myClass/");
            Class<?> clazz = loader1.loadClass("Main");
            System.out.println(clazz.getName());
        }
    }
}
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved