敬的世界

          導(dǎo)航

          <2009年5月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          留言簿(7)

          隨筆分類(21)

          文章分類

          收藏夾

          隨筆檔案(49)

          文章檔案(4)

          閱讀排行榜

          評論排行榜

          常用鏈接

          統(tǒng)計

          最新評論

          ClassLoader原理

          ?JVM規(guī)范定義了兩種類型的類裝載器:啟動內(nèi)裝載器(bootstrap)和用戶自定義裝載器(user-defined class loader)

          一.??? ClassLoader基本概念
          1.ClassLoader分類
          類裝載器是用來把類(class)裝載進(jìn)JVM的。
          JVM規(guī)范定義了兩種類型的類裝載器:啟動內(nèi)裝載器(bootstrap)和用戶自定義裝載器(user-defined class loader)


          JVM在運(yùn)行時會產(chǎn)生三個ClassLoader:Bootstrap?ClassLoader、Extension?ClassLoader和AppClassLoader.Bootstrap是用C++編寫的,我們在Java中看不到它,是null,是JVM自帶的類裝載器,用來裝載核心類庫,如java.lang.*等。
          AppClassLoaderParentExtClassLoader,而ExtClassLoaderParentBootstrap?ClassLoader
          ?
          Java提供了抽象類ClassLoader,所有用戶自定義類裝載器都實例化自ClassLoader的子類。 System Class Loader是一個特殊的用戶自定義類裝載器,由JVM的實現(xiàn)者提供,在編程者不特別指定裝載器的情況下默認(rèn)裝載用戶類。系統(tǒng)類裝載器可以通過ClassLoader.getSystemClassLoader() 方法得到。
          ?
          例1,測試你所使用的JVM的ClassLoader
          /*LoaderSample1.java*/
          public?class?LoaderSample1?{
          ????
          public?static?void?main(String[]?args)?{
          ????????Class?c;
          ????????ClassLoader?cl;
          ????????cl?
          =?ClassLoader.getSystemClassLoader();
          ????????System.out.println(cl);
          ????????
          while?(cl?!=?null)?{
          ????????????cl?
          =?cl.getParent();
          ????????????System.out.println(cl);
          ????????}
          ????????
          try?{
          ????????????c?
          =?Class.forName("java.lang.Object");
          ????????????cl?
          =?c.getClassLoader();
          ????????????System.out.println(
          "java.lang.Object's?loader?is?"?+?cl);
          ????????????c?
          =?Class.forName("LoaderSample1");
          ????????????cl?
          =?c.getClassLoader();
          ????????????System.out.println(
          "LoaderSample1's?loader?is?"?+?cl);
          ????????}?
          catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}
          }


          在我的機(jī)器上(Sun Java 1.4.2)的運(yùn)行結(jié)果
          sun.misc.Launcher$AppClassLoader@1a0c10f
          sun.misc.Launcher$ExtClassLoader@e2eec8
          null
          java.lang.Object's loader is null
          LoaderSample1's loader is sun.misc.Launcher$AppClassLoader@1a0c10f

          第一行表示,系統(tǒng)類裝載器實例化自類sun.misc.Launcher$AppClassLoader
          第二行表示,系統(tǒng)類裝載器的parent實例化自類sun.misc.Launcher$ExtClassLoader
          第三行表示,系統(tǒng)類裝載器parent的parent為bootstrap
          第四行表示,核心類java.lang.Object是由bootstrap裝載的
          第五行表示,用戶類LoaderSample1是由系統(tǒng)類裝載器裝載的
          ?
          ?
          二.parent delegation模型
          從1.2版本開始,Java引入了雙親委托模型,從而更好的保證Java平臺的安全。在此模型下,當(dāng)一個裝載器被請求裝載某個類時,它首先委托自己的parent去裝載,若parent能裝載,則返回這個類所對應(yīng)的Class對象,若parent不能裝載,則由parent的請求者去裝載

          圖 1 parent delegation模型
          如圖1所示,loader2的parent為loader1,loader1的parent為system class loader。假設(shè)loader2被要求裝載類MyClass,在parent delegation模型下,loader2首先請求loader1代為裝載,loader1再請求系統(tǒng)類裝載器去裝載MyClass。若系統(tǒng)裝載器能成功裝載,則將MyClass所對應(yīng)的Class對象的reference返回給loader1,loader1再將reference返回給loader2,從而成功將類MyClass裝載進(jìn)虛擬機(jī)。若系統(tǒng)類裝載器不能裝載MyClass,loader1會嘗試裝載MyClass,若loader1也不能成功裝載,loader2會嘗試裝載。若所有的parent及l(fā)oader2本身都不能裝載,則裝載失敗。
          ?
          若有一個能成功裝載,實際裝載的類裝載器被稱為定義類裝載器,所有能成功返回Class對象的裝載器(包括定義類裝載器)被稱為初始類裝載器。如圖1所示,假設(shè)loader1實際裝載了MyClass,則loader1為MyClass的定義類裝載器,loader2和loader1為MyClass的初始類裝載器。
          ?
          需要指出的是,Class Loader是對象,它的父子關(guān)系和類的父子關(guān)系沒有任何關(guān)系。
          ?
          那么parent delegation模型為什么更安全了?因為在此模型下用戶自定義的類裝載器不可能裝載應(yīng)該由父親裝載器裝載的可靠類,從而防止不可靠甚至惡意的代碼代替由父親裝載器裝載的可靠代碼。實際上,類裝載器的編寫者可以自由選擇不用把請求委托給parent,但正如上所說,會帶來安全的問題。
          ?
          ?
          三.命名空間及其作用
          每個類裝載器有自己的命名空間,命名空間由所有以此裝載器為創(chuàng)始類裝載器的類組成。不同命名空間的兩個類是不可見的,但只要得到類所對應(yīng)的Class對象的reference,還是可以訪問另一命名空間的類。
          ?
          例2演示了一個命名空間的類如何使用另一命名空間的類。在例子中,LoaderSample2由系統(tǒng)類裝載器裝載,LoaderSample3由自定義的裝載器loader負(fù)責(zé)裝載,兩個類不在同一命名空間,但LoaderSample2得到了LoaderSample3所對應(yīng)的Class對象的reference,所以它可以訪問LoaderSampl3中公共的成員(如age)。
          例2不同命名空間的類的訪問
          /*LoaderSample2.java*/

          import?java.net.*;
          import?java.lang.reflect.*;
          public?class?LoaderSample2?{
          ????
          public?static?void?main(String[]?args)?{
          ????????
          try?{
          ????????????String?path?
          =?System.getProperty("user.dir");
          ????????????URL[]?us?
          =?{new?URL("file://"?+?path?+?"/sub/")};
          ????????????ClassLoader?loader?
          =?new?URLClassLoader(us);
          ????????????Class?c?
          =?loader.loadClass("LoaderSample3");
          ????????????Object?o?
          =?c.newInstance();
          ????????????Field?f?
          =?c.getField("age");
          ????????????
          int?age?=?f.getInt(o);
          ????????????System.out.println(
          "age?is?"?+?age);
          ????????}?
          catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}
          }


          /*sub/Loadersample3.java*/

          public?class?LoaderSample3?{
          ????
          static?{
          ????????System.out.println(
          "LoaderSample3?loaded");
          ????}
          ????
          public?int?age?=?30;
          }

          編譯:javac LoaderSample2.java; javac sub/LoaderSample3.java
          運(yùn)行:java LoaderSample2
          LoaderSample3 loaded
          age is 30
          從運(yùn)行結(jié)果中可以看出,在類LoaderSample2中可以創(chuàng)建處于另一命名空間的類LoaderSample3中的對象并可以訪問其公共成員age。
          運(yùn)行時包(runtime package)
          由同一類裝載器定義裝載的屬于相同包的類組成了運(yùn)行時包,決定兩個類是不是屬于同一個運(yùn)行時包,不僅要看它們的包名是否相同,還要看的定義類裝載器是否相同。只有屬于同一運(yùn)行時包的類才能互相訪問包可見的類和成員。這樣的限制避免了用戶自己的代碼冒充核心類庫的類訪問核心類庫包可見成員的情況。假設(shè)用戶自己定義了一個類java.lang.Yes,并用用戶自定義的類裝載器裝載,由于java.lang.Yes和核心類庫java.lang.*由不同的裝載器裝載,它們屬于不同的運(yùn)行時包,所以java.lang.Yes不能訪問核心類庫java.lang中類的包可見的成員。
          ?
          總結(jié)
          命名空間并沒有完全禁止屬于不同空間的類的互相訪問,雙親委托模型加強(qiáng)了Java的安全,運(yùn)行時包增加了對包可見成員的保護(hù)。
          ?
          二.??? 擴(kuò)展ClassLoader方法
          我們目的是從本地文件系統(tǒng)使用我們實現(xiàn)的類裝載器裝載一個類。為了創(chuàng)建自己的類裝載器我們應(yīng)該擴(kuò)展ClassLoader類,這是一個抽象類。我們創(chuàng)建一個FileClassLoader extends ClassLoader。我們需要覆蓋ClassLoader中的findClass(String name)方法,這個方法通過類的名字而得到一個Class對象。

          ????public?Class?findClass(String?name)
          ????{
          ????????
          byte[]?data?=?loadClassData(name);
          ????????
          return?defineClass(name,?data,?0,?data.length);
          ????}


          ???我們還應(yīng)該提供一個方法loadClassData(String name),通過類的名稱返回class文件的字
          節(jié)數(shù)組。然后使用ClassLoader提供的defineClass()方法我們就可以返回Class對象了。

          ????public?byte[]?loadClassData(String?name)
          ????{
          ????????FileInputStream?fis?
          =?null;
          ????????
          byte[]?data?=?null;
          ????????
          try
          ????????{
          ????????????fis?
          =?new?FileInputStream(new?File(drive?+?name?+?fileType));
          ????????????ByteArrayOutputStream?baos?
          =?new?ByteArrayOutputStream();
          ????????????
          int?ch?=?0;
          ????????????
          while?((ch?=?fis.read())?!=?-1)
          ????????????{
          ????????????????baos.write(ch);
          ???????????????
          ????????????}
          ????????????data?
          =?baos.toByteArray();
          ????????}?
          catch?(IOException?e)
          ????????{
          ????????????e.printStackTrace();
          ????????}
          ????????
          ????????
          return?data;
          ????}


          再說說Package權(quán)限。Java語言規(guī)定,在同一個包中的class,如果沒有修飾符,默認(rèn)為Package權(quán)限,包內(nèi)的class都可以訪問。但是這還不夠準(zhǔn)確。確切的說,只有由同一個ClassLoader裝載的class才具有以上的Package權(quán)限。比如啟動類裝載器裝載了java.lang.String,類路徑裝載器裝載了我們自己寫的java.lang.Test,它們不能互相訪問對方具有Package權(quán)限的方法。這樣就阻止了惡意代碼訪問核心類的Package權(quán)限方法。


          posted on 2009-05-16 23:38 picture talk 閱讀(174) 評論(0)  編輯  收藏 所屬分類: Java

          主站蜘蛛池模板: 九龙坡区| 黄石市| 安溪县| 永仁县| 新昌县| 松溪县| 梨树县| 胶州市| 五原县| 龙州县| 织金县| 巴马| 上杭县| 芒康县| 黔西| 育儿| 吉林市| 望都县| 景谷| 尖扎县| 察隅县| 中江县| 雅安市| 新余市| 陵水| 得荣县| 茂名市| 汾西县| 乐业县| 滨海县| 陇南市| 讷河市| 大英县| 英超| 临朐县| 德格县| 克拉玛依市| 镇巴县| 康平县| 平阳县| 新泰市|