<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    sharajava

    2006年7月21日 #

    類的動態加載

    : 調用 Class.forName() ClassLoader.loadClass() 的區別在什么地方 ?

    : 這兩方法都是通過一個給定的類名去定位和加載這個類名對應的 java.long.Class 類對象 . 盡管如此 , 它們的在行為方式上還是有區別的 .

    ????????? 用哪個 java.lang.ClassLoader 進行加載

    ????????? 返回的 Class 對象是否被初始化

    Class.forName(String) 方法(只有一個參數), 使用調用者的類加載器來加載, 也就是用加載了調用forName方法的代碼的那個類加載器. 相應的, ClassLoader.loadClass()方法是一個實例方法(非靜態方法), 調用時需要自己指定類加載器, 那么這個類加載器就可能是也可能不是加載調用代碼的類加載器. 如果用特定的類加載器來加載類在你的設計中占有比較重要的地位, 你就應該調用ClassLoader.loadClass(String)方法或Class.forName(String, boolean, ClassLoader)方法.

    ??? 另外, Class.forName()方法對加載的類對象進行初始化. 可見的效果就是類中靜態初始化段及字節碼中對所有靜態成員的初始工作的執行(這個過程在類的所有父類中遞歸地調用). 這點就與ClassLoader.loadClass()不同. ClassLoader.loadClass()加載的類對象是在第一次被調用時才進行初始化的.

    ??? 你可以利用上述的差異. 比如,要加載一個靜態初始化開銷很大的類, 你就可以選擇提前加載該類(以確保它在classpath), 但不進行初始化, 直到第一次使用該類的域或方法時才進行初始化.

    ??? 最常用的是Class.forName(String, boolean, ClassLoader). 設置第二個參數為false即推遲初始化, 第三個參數指定要用來進行加載的類加載器. 我建議為了最大的靈活性使用這個方法.

    類初始化錯誤是難處理的

    ??? 成功地加載了類, 并不意味著就不會有其它問題. 靜態初始化代碼可以拋出異常, 異常被包裝到java.long.ExceptionInInitializerError的實例中. 異常拋出后, 這個類將不可用. 這樣, 如果你需要在代碼中處理這些錯誤, 你就應該調用進行初始化的Class.forName()方法.

    ??? 但進一步說, 如果你要處理ExceptionInInitializerError并試圖從錯誤中恢復, 很可能不如你想象的那樣正常工作. 請看下面的示例代碼:


    public ? class ?Main
    {
    ????
    public ? static ? void ?main?(String?[]?args)? throws ?Exception
    ????{
    ????????
    for ?( int ?repeat?=?0;?repeat?<?3;?++?repeat)
    ????????{
    ????????????
    try
    ????????????{
    ????????????????
    //?"Real"?name?for?X?is?outer?class?name+$+nested?class?name:
    ????????????????Class.forName?("Main$X");
    ????????????}
    ????????????
    catch ?(Throwable?t)
    ????????????{
    ????????????????System.out.println?("load?attempt?#"?+?repeat?+?":");
    ????????????????t.printStackTrace?(System.out);
    ????????????}
    ????????}
    ????}

    ????
    private ? static ? class ?X
    ????{
    ????????
    static
    ????????{
    ????????????
    if ?(++?s_count?==?1)
    ????????????????
    throw ? new ?RuntimeException?("failing?static?initializer");
    ????????}
    ????????
    ????}?
    //?End?of?nested?class

    ????
    private ? static ? int ?s_count;

    }?
    //?End?of?class

    ??? 上面的代碼3次嘗試加載一個內部類X, 即便是X的靜態初始化只在每一次加載時失敗, 3次加載都拋出了異常.

    >java Main
    load attempt #0:
    java.lang.ExceptionInInitializerError
    ????????at java.lang.Class.forName0(Native Method)
    ????????at java.lang.Class.forName(Class.java:140)
    ????????at Main.main(Main.java:17)
    Caused by: java.lang.RuntimeException: failing static initializer...
    ????????at Main$X.<clinit>(Main.java:40)
    ????????... 3 more
    load attempt #1:
    java.lang.NoClassDefFoundError
    ????????at java.lang.Class.forName0(Native Method)
    ????????at java.lang.Class.forName(Class.java:140)
    ????????at Main.main(Main.java:17)
    load attempt #2:
    java.lang.NoClassDefFoundError
    ????????at java.lang.Class.forName0(Native Method)
    ????????at java.lang.Class.forName(Class.java:140)
    ????????at Main.main(Main.java:17)

    ??? 有點令人吃驚的時, 在第2, 3次進行類加載時, 拋出的異常竟然是java.lang.NoClassDefFoundError. 這里發生的事情是, 第一次加載后(在進行初始化之前), JVM發現X已經被加載, 而這個X的類實例在加載它的類加載器被垃圾回收之前是不會被卸載的. 所以這之后的對Class.forName()的調用時, JVM不會再嘗試進行初始化的工作, 但是, 更令人不解的是, 拋出一個NoClassDefFoundError.

    ??? 卸載這樣的類的方法是丟棄原來加載該類的類加載器實例并重新創建一個. 當然, 這只能是在你使用了Class.forName(String, boolean, ClassLoader)這個3參數的方法的時候才能辦到.

    隱藏的 Class.forName() 方法

    ??? 你一定用過JavaX.class的語法去獲取一個在編譯器就知道名字的類對象實例. 在字節碼的層次上, 這一點是如何實現的就不被人熟知了. 不同的編譯器有不同的實例細節, 但共同點是, 所有編譯器所相應產生的代碼都是調用的Class.forName(String)這一個參數的方法. 比如J2SE 1.4.1javac就把Class cls = X.class; 翻譯成如下等價的形式:

    ?
    ????????
    //?This?is?how?"Class?cls?=?X.class"?is?transformed:
    ???????? if ?( class $Main$X?==? null )
    ????????{
    ????????????
    class $Main$X?=? class $?("Main$X");
    ????????}
    ????????Class?cls?=?
    class $Main$X;

    ????

    ????
    static ?Class? class $?(String?s)
    ????{
    ????????
    try
    ????????{
    ????????????
    return ?Class.forName?(s);
    ????????}
    ????????
    catch ?(ClassNotFoundException?e)
    ????????{
    ????????????
    throw ? new ?NoClassDefFoundError?(e.getMessage());
    ????????}
    ????}

    ????
    static ?Class? class $Main$X;? //?A?synthetic?field?created?by?the?compiler

    Sun javac 開個玩笑

    從上面的例子你可以看到, 編譯器調用Class.forName()方法加載類對象, 并將其緩存到一個包內可見的靜態變量中. 這種令人費解的實現方式的可能是因為在早期版本的Java, 這種X.class的語法還未被支持, so the feature was added on top of the Java 1.0 byte-code instruction set.(???)

    利用這一點, 你可以在編譯器的開銷上做一些有趣的事情. J2SE 1.3.1編譯下面的代碼片段:

    public ? class ?Main
    {
    ????
    public ? static ? void ?main?(String?[]?args)? throws ?Exception
    ????{
    ????????System.out.println?("String?class:?"?+?String.
    class );
    ????????
    class $java$lang$String?=? int . class ;
    ????????System.out.println?("String?class:?"?+?String.
    class );
    ????}
    ????
    ????
    static ?Class? class $java$lang$String;

    }?
    //?End?of?class

    運行它, 你會得到下面這個很荒謬的輸出結果:

    >java Main

    String class: class java.lang.String

    String class: int

    J2SE 1.4.1, 上面的代碼將不能被編譯通過, 但你仍然可以用反射的方式戲弄它:

    public ? static ? void ?main?(String?[]?args)? throws ?Exception
    ????{
    ????????System.out.println?("String?class:?"?+?String.
    class );
    ????????Main.
    class .getDeclaredField?("class$java$lang$String").set?( null ,? int . class );
    ????????System.out.println?("String?class:?"?+?String.
    class );
    ????}

    ?

    ??? 綜上所述, 下次你再調用Class.forName()方法時, 你應該知道它的局限性可選的替代方案了.

    ?

    ?

    posted @ 2006-07-27 09:06 sharajava 閱讀(2149) | 評論 (0)編輯 收藏

    JBoss MBean服務加載過程

    http://www.tkk7.com/images/blogjava_net/sharajava/13416/o_start-jboss-mbean-service.JPG?

    ?

    1.????? 前綴1.1的方法, 代表加載和解析XML服務描述文件的過程.

    2.????? 前綴1.2的方法, 代表對XML服務描述文件中類路徑條目的處理. 這個過程創建獨立的部署, 使得jar和類目錄對于相應的統一類加載器(UnifiedClassLoader)可用. 這個UnifiedClassLoader是注冊到統一的類加載器倉庫中的.

    3.????? 前綴1.3的方法, 代表處理服務描述文件中定義的本地目錄條目的過程. 這個過程把在路徑屬性中指定的SAR相關條目復制一份到server/<config>/db目錄下.

    4.????? 方法1.4, 代表對已經部署的服務中嵌套的可部單元的部署過程. 子部署項目被創建并被加入到服務部署信息的子部署項列表中.

    5.????? 方法2.1, SAR部署單元相應的UnifiedClassLoader(本身也是MBean)被注冊到MBean Server, 這樣它就可以被用來加載SAR中的MBean.

    6.????? 方法2.2, 創建XML服務描述文件中定義的每個MBean并用描述文件中給定的值初始化其屬性. 這些工作通過調用ServiceControllerinstall(Element, ObjectName)方法來完成的.

    7.????? 方法2.4.1, 對于前面步驟中創建好的每個MBean實例, 獲取其JMX對象名并由ServiceController處理服務生命周期中的create步驟. ServiceController處理MBean服務的依賴關系, 只有當所有依賴都滿足時, create方法才會被調用.

    8.????? 前綴3.1的方法, 代表MBean實例的起動過程. 對于創建好的每個MBean實例, 獲取其JMX對象名并由ServiceController處理服務生命周期中的start步驟. ServiceController處理MBean服務的依賴關系, 只有當所有依賴都滿足時, start方法才會被調用.

    posted @ 2006-07-25 11:18 sharajava 閱讀(1914) | 評論 (0)編輯 收藏

    走出ClassLoader迷局

    : 我什么時候應該使用 Thread.getContextClassLoader()?

    : 這個問題經常出現在編寫框架代碼 , 需要動態加載很多類和資源的時候 . 通常當你需要動態加載資源的時候 , 你至少有三個 ClassLoader 可以選擇 :

    2??????? 系統類加載器或叫作應用類加載器 (system classloader or application classloader)

    2??????? 當前類加載器

    2??????? 當前線程類加載器

    上面的問題指的是最后一種類加載器 . 哪種類加載器是正確的選擇呢 ?

    第一種選擇可以很容易地排除 : 系統類加載器 (system classloader). 這個類加載器處理 -classpath 下的類加載工作 , 可以通過 ClassLoader.getSystemClassLoader() 方法調用 . ClassLoader 下所有的 getSystemXXX() 的靜態方法都是通過這個方法定義的 . 在你的代碼中 , 你應該盡量少地調用這個方法 , 以其它的類加載器作為代理 . 否則你的代碼將只能工作在簡單的命令行應用中 , 這個時候系統類加載器 (system classloader) JVM 最后創建的類加載器 . 一但你把代碼移到 EJB, Web 應用或 Java Web Start 應用中 , 一定會出問題 .

    ????? 所以我們來看第二種選擇 : 當前上下文環境下的類加載器 . 根據定義 , 當前類加載器就是你當前方法所屬的類的加載器 . 在運行時類之間動態聯編 , 及調用 Class.forName,() Class.getResource() 等類似方法時 , 這個類加載器會被隱含地使用 . It is also used by syntactic constructs like X.class class literals.

    ??? 線程上下文類型加載器是在Java 2平臺上被引入的. 每一個線程都有一個類加載器與之對應(除非這個線程是被本地代碼創建的). 這個類加載器是通過Thread.setContextClassLoaser()方法設置的. 如果你不在線程構造后調用這個方法, 這個線程將從它的父線程中繼承相應的上下文類加載器. 如果在整個應用中你不做任何特殊設置, 所有的線程將都以系統類加載器(system classloader)作為自己的線程上下文類加載器. 自從WebJ2EE應用服務器使用成熟的類加載器機制來實現諸如JNDI, 線程池, 組件熱部署等功能以來, 這種在整個應用中不做任何線程類加載器設置的情況就很少了.

    ??? 為什么線程上下文類加載器存在于如此重要的位置呢? 這個概念在J2SE中的引入并不引人注目. 很多開發人員對這一概念迷惑的原因是Sun公司在這方面缺乏適當的指引和文檔.

    ??? 事實上, 上下文類加載器提供了類加載機制的后門, 這一點也在J2SE中被引入了. 通常, JVM中的所有類加載器被組織成了有繼承層次的結構, 每一個類加載器(除了引導JVM的原始類加載器)都有一個父加載器. 每當被請示加載類時, 類加載器都會首先請求其父類加載器, 只有當父類加載器不能加載時, 才會自己進行類加載.

    ?? 有時候這種類加載的順序安排不能正常工作, 通常當必須動態加載應用程序開發人員提供的資源的時候. JNDI為例: 它的內容(J2SE1.3開始)就在rt.jar中的引導類中實現了, 但是這些JNDI核心類需要動態加載由獨立廠商實現并部署在應用程序的classpath下的JNDI提供者. 這種情況就要求一個父classloader(本例, 就是引導類加載器)去加載對于它其中一個子classloader(本例, 系統類加載器)可見的類. 這時通常的類加載代理機制不能實現這個要求. 解決的辦法(workaround)就是, JNDI核心類使用當前線程上下文的類加載器, 這樣, 就基本的類加載代理機制的相反方向建立了一條有效的途徑.

    ??? 另外, 上面一段可能讓你想起一些其它的事情: XML解析Java API(JAXP). 是的, JAXP只是J2SE的擴展進, 它很自然地用當前類加載器來引導解析器的實現. 而當JAXP被加入到J2SE1.4的核心類庫中時, 它的類加載也就改成了用當前線程類加載器, JNDI的情況完全類似(也使很多程序員很迷惑). 明白為什么我說來自Sun的指導很缺乏了吧?

    ?? 在以上的介紹之后, 我們來看關鍵問題: 這兩種選擇(當前類加載器和當前線程類加載器)都不是在所有環境下都適用. 有些人認為當前線程類加載器應該成為新的標準策略. 但是, 如果這樣, 當多個線程通過共享數據進行交互的時, 將會呈現出一幅極其復雜的類加載的畫面, 除非它們全部使用了同一個上下文的類加載器. 進一步說, 在某些遺留下來的解決方案中, 委派到當前類加載器的方法已經是標準. 比如對Class.forName(String)的直接調用(這也是我為什么推薦盡量避免對這個方法進行調用的原因). 即使你努力去只調用上下文相關的類加載器, 仍然會有一些代碼會不由你控制. 這種不受控制的類加載委派機制是混入是很危險的.

    ??? 更嚴重的問題, 某些應用服務器把環境上下文及當前類加載器設置到不同的類加載器實例上, 而這些類加載器有相同的類路徑但卻沒有委派機制中的父子關系. 想想這為什么十分可怕. 要知道類加載器定義并加載的類實例會帶有一個JVM內部的ID. 如果當前類加載器加載一個類X的實例, 這個實例調用JNDI查找類Y的實例, 些時的上下文的類加載器也可以定義了加載類Y實例. 這個類Y的定義就與當前類加載器看到的類Y的定義不同. 如果進行強制類型轉換, 則產生異常.

    ?? 這種混亂的情況還將在Java中存在一段時間. 對于那些需要動態加載資源的J2SEAPI, 我們來猜想它們的類加策略. 例如:

    ????????? JNDI 使用線程上下文類加載器

    ????????? Class.getResource() Class.forName()使用當前類加載器

    ????????? JAXP(J2SE 1.4 及之后)使用線程上下文類加載器

    ????????? java.util.ResourceBundle 使用調用者的當前類加載器

    ????????? URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only

    ????????? Java 序列化API默認使用調用者當前的類加載器

    這些類及資源的加載策略問題, 肯定是J2SE領域中文檔最及說明最缺乏的部分了.

    posted @ 2006-07-25 11:04 sharajava 閱讀(2507) | 評論 (1)編輯 收藏

    JBoss啟動過程(譯)

    1.????? org.jboss.Main.main(String[]) 為入口.

    2.????? main 函數創建一個名叫”jboss”的線程組, 然后創建一個屬于該組的線程, 在線程中執行boot方法.

    3.????? boot 方法首先處理main函數中的參數(及一些其它的系統環境設置), 接著就用系統的屬性創建了org.jboss.system.server.ServerLoader實例[new ServerLoader(props)].

    4.????? ServerLoader 注冊Jboss相關的類路徑, 包括XML解析器, jboss-jmx.jar, concurrent.jar及其它的一些額外的類路徑.

    5.????? ServerLoader 通過load(ClassLoader)方法創建Jboss Server實例. 參數ClassLoaderClassLoader parentCL = Thread.currentThread(). getContextClassLoader( )得到的當前線程的類加載器. 創建的Server實例是org.jboss.system.server.Server接口的實現. load(ClassLoader)方法的細節:

    ???????? jar包及在ServerLoader中注冊的類路徑創建一個URLClassLoader的實例, 把傳入的ClassLoader作為該URLClassLoaderparent.

    ???????? Server 接口的實現類由系統屬性 jboss.server.type決定, 默認是????? org.jboss.system.server.ServerImpl.

    ???????? URLClassLoader 通過無參構造函數加載Server接口實現的實例. 在加載前把當前線程的類加載器置為該URLClassLoader, 在加載完成后再置回之前傳入的ClassLoader.

    6.????? Server 實例用系統屬性進行初始化[server.init(props)].

    7.????? 服務起動[server.start()]. 起動過程的默認實現如下:

    ???????? 把當前線程類型加載器置為加載該Server接口實現實例的ClassLoader.

    ???????? jboss域內, 通過MBeanServerFactorycreateMBeanServer(String)方法創建MbeanServer實例.

    ???????? MBean Server上注冊ServerImplServerConfigImpl兩個MBean.

    ???????? 初始化統一的類加載倉庫(unified class loader repository), 用來裝載服務器配置目錄及其它可選目錄下的jar文件. 對于每一個jar文件和類目錄都會創建一個相應的org.jboss.jmx.loading.UnifiedClassLoader實例, 并且注冊到統一的倉庫中. 其中一個UnifiedClassLoader實例會被設置為當前線程上下文的ClassLoader. [?: This effectively makes allUnifiedClassLoaders available through the thread context class loader.]

    ???????? 接下來創建org.jboss.system.ServiceControllerMBean實例. ServiceController管理JBoss MBean服務的生命周期.

    ???????? org.jboss.deployment.MainDeployer 實例被創建并起動. MainDeployer管理部署的依賴和部署的定向.

    ???????? org.jboss.deployment. JARDeployer 實例被創建并起動. JARDeployer處理jar包的部署.

    ???????? org.jboss.deployment. SARDeployer 實例被創建并起動. SARDeployer處理JBoss MBean服務的部署.

    ???????? MainDeployer 對當前服務器文件環境里conf/jboss-service.xml定義的服務進行部署.

    啟動過程結束. 把當前線程上下文類加載器置回為起動前的ClassLoader.

    posted @ 2006-07-21 08:27 sharajava 閱讀(1608) | 評論 (0)編輯 收藏

    主站蜘蛛池模板: 亚洲中文无码永久免| 亚洲人成人无码.www石榴| 97青青草原国产免费观看| 亚洲an日韩专区在线| www亚洲一级视频com| 久久永久免费人妻精品下载| 亚洲日本成本人观看| 亚洲欧洲日产国码av系列天堂 | 亚洲人成在线播放| 免费在线观看的黄色网址| 日韩中文字幕免费视频| 校园亚洲春色另类小说合集| 亚洲VA中文字幕无码一二三区| 女人与禽交视频免费看| 日本免费在线中文字幕| 真实国产乱子伦精品免费| 亚洲人成网站看在线播放| 国产亚洲人成网站在线观看| 成年人免费观看视频网站| A级毛片高清免费视频在线播放| 久久亚洲国产最新网站| 亚洲AV无码成人精品区在线观看| 午夜dj免费在线观看| 久久不见久久见免费视频7| 三级片免费观看久久| 国产成人亚洲合集青青草原精品| 亚洲日韩激情无码一区| 国产精品二区三区免费播放心| 精品一区二区三区无码免费视频| 四虎影视永久在线精品免费| 亚洲中文无码卡通动漫野外 | 亚洲乱码中文论理电影| 亚洲国产精品一区二区第一页| 国产成人免费片在线视频观看| 久久免费看黄a级毛片| 免费看黄的成人APP| 特a级免费高清黄色片| 亚洲大码熟女在线观看| 国产午夜亚洲精品理论片不卡| 在线jlzzjlzz免费播放| 成人免费的性色视频|