java類加載器

詳細內容請參考原鏈接:http://blog.csdn.net/jiangwei0910410003/article/details/17733153,此文只是轉載
1、新建一個java對象,JVM需要把這個對象對應的字節碼加載到內存中,字節碼就是對應的Class文件,自己java工程,其在bin目錄下,也就是把.class文件的內容讀到內存中;
2、Java虛擬機中類加載器
java虛擬機中可以安裝多個類加載器,系統默認三個主要的類加載器,每個類負責加載特定位置的類:BootStarp,ExtClassLoader,AppClassLoader,其中BootStrap是用C++寫的,內置在JVM內核中,也就是所有類的鼻祖;
之所以有不同的類加載器,是因爲不同的類加載器加載不同目錄下的.class文件;採用委託的方式,向父類請求加載,每個父類去指定的目錄加載,成功結束;不成功再順序往下請求,直到加載成功,所有加載類都沒有成功,則拋出ClassNotFoundException異常

類加載器的委託機制:
當Java虛擬機要加載第一個類的時候,到底派出哪個類加載器去加載呢?
(1). 首先當前線程的類加載器去加載線程中的第一個類(當前線程的類加載器:Thread類中有一個get/setContextClassLoader(ClassLoader cl);方法,可以獲取/指定本線程中的類加載器)
(2). 如果類A中引用了類B,Java虛擬機將使用加載類A的類加載器來加載類B
(3). 還可以直接調用ClassLoader.loadClass(String className)方法來指定某個類加載器去加載某個類
每個類加載器加載類時,又先委託給其上級類加載器當所有祖宗類加載器沒有加載到類,回到發起者類加載器,還加載不了,則會拋出ClassNotFoundException,不是再去找發起者類加載器的兒子,因爲沒有getChild()方法。例如:如上圖所示: MyClassLoader->AppClassLoader->Ext::ClassLoader->BootStrap.自定定義的MyClassLoader1首先會先委託給AppClassLoader,AppClassLoader會委託給ExtClassLoader,ExtClassLoader會委託給BootStrap,這時候BootStrap就去加載,如果加載成功,就結束了。如果加載失敗,就交給ExtClassLoader去加載,如果ExtClassLoader加載成功了,就結束了,如果加載失敗就交給AppClassLoader加載,如果加載成功,就結束了,如果加載失敗,就交給自定義的MyClassLoader1類加載器加載,如果加載失敗,就報ClassNotFoundException異常,結束;
因爲BootStrap不是java類,所以打印出來的class是null
3、System類、list、map這樣的類都在rt.jar中,需要由BootStrap來加載,所以其加載類打印出來爲null;也就是說,誰加載的你當前這個jar,那麼它的classloader就是誰,比方說把MyClassLoader打包成jar,然後放到jre1.x/lib/ext目錄下,那麼它的第一個加載類就會變成sun.misc.Launcher$ExtClassLoader
4、可以定義自己的類加載器,需要將自己的類加載器掛載到系統類加載器樹上,在構造函數時可以指定類加載器
5、定義了自己的類加載器,每個加載器都有自己的搜索目錄,自己類加載器有自己的目錄,而它的父類也有自己的目錄,所以類名字,如果父類找到了,那麼子類不會繼續查找了;所以即使使用了自己的類加載器,如果是父類找到了對應名稱的類class,其加載器就是父類
編寫自己的加載類:
先寫一個測試類:

public class ClassLoaderAttachment extends Date{
    private static final long serialVersionUID = 98398492342L;
    @Override
    public String toString() {
        // TODO Auto-generated method stub

        return "Hell ClassLoaderAttachment";
    }
}

編譯後,在bin目錄會生成它的.class文件,然後在項目目錄(bin的上一層把)創建一個”temp_folder”目錄,用於生成加密後的.class文件
寫一個main函數,把上面的ClassLoaderAttachment.class文件加密

        //測試,先將ClassLoaderAttachment.class文件加密寫到工程的class_temp目錄下  
        public static void main(String[] args) throws Exception{  
            //配置運行參數  
            String srcPath = args[0];//ClassLoaderAttachment.class原路徑  
            String desPath = args[1];//ClassLoaderAttachment.class輸出的路徑  
            String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);  
            String desPathFile = desPath + "/" + desFileName;  
            FileInputStream fis = new FileInputStream(srcPath);  
            FileOutputStream fos = new FileOutputStream(desPathFile);  
            //將class進行加密  
            encodeAndDecode(fis,fos);  
            fis.close();  
            fos.close();  
        }  

        /** 
         * 加密和解密算法 
         * @param is 
         * @param os 
         * @throws Exception 
         */  
        private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{  
            int bytes = -1;  
            while((bytes = is.read())!= -1){  
                bytes = bytes ^ 0xff;//和0xff進行異或處理  
                os.write(bytes);  
            }  
        }

會在temp_folder目錄下生成一個新的ClassLoaderAttachment.class,此目錄和class文件名用於創建自己的ClassLoader時,作爲參數傳入
下面編寫自己的加載類

public class myLoadClass extends ClassLoader{  

        //需要加載類.class文件的目錄  
        private String classDir;  

        //無參的構造方法,用於class.newInstance()構造對象使用  
        public myLoadClass(){  
        }  

        public myLoadClass(String classDir){  
            this.classDir = classDir;  
        }  

        @SuppressWarnings("deprecation")  
        @Override  
        protected Class<?> findClass(String name) throws ClassNotFoundException {  
            //class文件的路徑  
            String classPathFile = classDir + "/" + name + ".class";  
            try {
                //將class文件進行解密  
                FileInputStream fis = new FileInputStream(classPathFile);  
                ByteArrayOutputStream bos = new ByteArrayOutputStream();  
                encodeAndDecode(fis,bos);  
                byte[] classByte = bos.toByteArray();  
                //將字節流變成一個class  
                return defineClass(classByte,0,classByte.length);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            return super.findClass(name);  
        }  

        //測試,先將ClassLoaderAttachment.class文件加密寫到工程的class_temp目錄下  
        public static void main(String[] args) throws Exception{  
            //配置運行參數  
            String srcPath = args[0];//ClassLoaderAttachment.class原路徑  
            String desPath = args[1];//ClassLoaderAttachment.class輸出的路徑  
            String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);  
            String desPathFile = desPath + "/" + desFileName;  
            FileInputStream fis = new FileInputStream(srcPath);  
            FileOutputStream fos = new FileOutputStream(desPathFile);  
            //將class進行加密  
            encodeAndDecode(fis,fos);  
            fis.close();  
            fos.close();  
        }  

        /** 
         * 加密和解密算法 
         * @param is 
         * @param os 
         * @throws Exception 
         */  
        private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{  
            int bytes = -1;  
            while((bytes = is.read())!= -1){  
                bytes = bytes ^ 0xff;//和0xff進行異或處理  
                os.write(bytes);  
            }  
        }  
}

其中的main函數就是剛纔加密class文件用的
然後寫測試myDexLoader的測試方法:

public class myMain {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
            //輸出ClassLoaderText的類加載器名稱  
            System.out.println("ClassLoaderText類的加載器的名稱:"+myMain.class.getClassLoader().getClass().getName());  
            System.out.println("System類的加載器的名稱:"+System.class.getClassLoader());  
            System.out.println("List類的加載器的名稱:"+List.class.getClassLoader());

            ClassLoader cl = myMain.class.getClassLoader();
            while(cl != null){
                System.out.print(cl.getClass().getName()+"->");  
                cl = cl.getParent();  
            }
            System.out.println(cl);
            /**
             * 如果在bin目錄下有ClassLoaderAttachment.class,那麼用的是系統的appClassLader,因爲父類已經找到了
             * 如果想自己的類加載,那麼需要把bin目錄下對應的.class文件刪掉,使父類找不到,這樣自己的加載類纔有機會去執行
             * **/
            Class myClass = new myLoadClass("temp_folder").loadClass("ClassLoaderAttachment");
            Date myDate = (Date)myClass.newInstance();
            System.out.println("classLoader is :"+myDate.getClass().getClassLoader().getClass().getName());
            System.out.println("date result is "+myDate.toString());
            ClassLoader cll = myDate.getClass().getClassLoader();
            while(cll != null){
                System.out.print(cll.getClass().getName()+"->");
                cll = cll.getParent();
            }
            System.out.println(cll);
    }
}

也是一個main方法,執行即可;把bin目錄下的ClassLoaderAttachment.class刪除後,最後得到的加載類鏈表是:

執行結果,加載類名稱以及父類鏈表

由於temp_folder目錄下是加了密的class文件,所以只能被我們自己能解密的ClassLoader進行加載執行,其實就是返回真是的字節碼;
如果把它拷貝到bin目錄,那麼系統的sun.misc.Launcher$AppClassLoader會進行加載此class,但是文件是加了密的,所以其解析錯誤,則會報執行錯誤

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章