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

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

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

    慘淡人生,平淡生活

    The Feature Is Stupid

    [翻譯]走出ClassLoader的迷宮

    [說明]幾個關鍵字將不翻譯

    1. ClassLoader
    2. System
    3. Context
    4. Thread

    走出ClassLoader的迷宮

                                                                   System、Current和Context ClassLoader?分別在何種情形下使用?


    1、問題:在何種情形下使用thread.getcontextclassloader()?

    盡管沒經常遇到這個問題,但是想獲得準確的答案并不那么容易,特別是在開發應用框架的時候,你需要動態的加載一些類和資源,不可避免的你會被此困擾。一般來說,動態載入資源有三種ClassLoader可以選擇,System ClassLoader(也叫App ClassLoader)、當前類的ClassLoader和CurrentThread的Context ClassLoader。那么, 如何選擇使用?

    首先可以簡單排除的是System ClassLoader,這個ClassLoader負責從參數-classpath、-cp、和操作系統CLASSPATH中載入資源。并且,任何ClassLoader的getSystemXXX()方法都是有以上幾個路徑指定的。我們應該很少需要編寫直接使用ClassLoader的程序,否則你的代碼將只能在命令行運行,發布你的代碼成為ejb、web應用或者java web start應用,我肯定他們會崩潰!

    接下來,我們只剩下兩個選擇了:當前ClassLoader和Thread Context ClassLoader

    Current ClassLoader:當前類所屬的ClassLoader,在虛擬機中類之間引用,默認就是使用這個ClassLoader。另外,當你使用Class.forName(), Class.getResource()這幾個不帶ClassLoader參數的方法是,默認同樣適用當前類的ClassLoader。你可以通過方法XX.class.GetClassLoader()獲取。

    Thread Context ClassLoader,沒一個Thread有一個相關聯系的Context ClassLoader(由native方法建立的除外),可以通過Thread.setContextClassLoader()方法設置。如果你沒有主動設置,Thread默認集成Parent Thread的 Context ClassLoader(注意,是parent Thread 不是父類)。如果 你整個應用中都沒有對此作任何處理,那么 所有的Thread都會以System ClassLoader作為Context ClassLoader。知道這一點很重要,因為從web服務器,java企業服務器使用一些復雜而且精巧的ClassLoader結構去實現諸如JNDI、線程池和熱部署等功能以來,這種簡單的情況越發的少見了。

    這篇文章中為什么把Thread Context ClassLoader放在首要的位置,別人并沒有大張旗鼓的介紹它?很多開發者都對此不甚了解,因為sun沒有提供很好的說明文檔。

    事實上,Context ClassLoader提供一個突破委托代理機制的后門。虛擬機通過父子層次關系組織管理ClassLoader,沒有個ClassLoader都有一個Parent ClassLoader(BootStartp不在此范圍之內),當要求一個ClassLoader裝載一個類是,他首先請求Parent ClassLoader去裝載,只有parent ClassLoader裝載失敗,才會嘗試自己裝載。

    但是,某些時候這種順序機制會造成困擾,特別是jvm需要動態載入有開發者提供的資源時。就以JNDI為例,JNDI的類是由bootstarp ClassLoader從rt.jar中間載入的,但是JNDI具體的核心驅動是由正式的實現提供的,并且通常會處于-cp參數之下(注:也就是默認的System ClassLoader管理),這就要求bootstartp ClassLoader去載入只有SystemClassLoader可見的類,正常的邏輯就沒辦法處理。怎么辦呢?parent可以通過獲得當前調用Thread的方法獲得調用線程的Context ClassLoder 來載入類。

    順帶補充一句,JAXP從1.4之后也換成了類似JNDI的ClassLoader實現,嘿嘿,剛剛我說什么來著,SUN文檔缺乏 ^_^

    介紹完這些之后,我們走到的十字路口,任一選擇都不是萬能的。一些人認為Context ClassLoader將會是新的標準。但是 一旦你的多線程需要通訊某些共享數據,你會發現,你將有一張極其丑陋的ClassLoader分布圖,除非所有的線程使用一樣的Context ClassLoader。并且委派使用當前ClassLoder對一些方法來說是默認繼承來的,比如說Class.forName()。盡管你明確的在任何你能控制的地方使用Context ClassLoader,但是畢竟還有很多代碼不歸你管(備注:想起一個關于UNIX名字來源的笑話)。

    某些應用服務器使用不同的ClassLoder作為Context ClassLoader和當前ClassLoader,并且這些ClassLoader有著相同的ClassPath,但沒有父子關系,這使得情況更復雜。請列位看官,花幾秒鐘時間想一想,為什么這樣不好?被載入的類在虛擬機內部有一個全名稱,不同的ClassLoader載入的相同名稱的類是不一樣的,這就隱藏了類型轉換錯誤的隱患。(注:奶奶的 俺就遇到過,JBOSSClassLoader機制蠻挫的)

    這種混亂事實上在java類中也有,試著去猜測任何一個包含動態加載的java規范的ClassLoader機制,以下是一個清單:

    • JNDI uses context classloaders
    • Class.getResource() and Class.forName() use the current classloader
    • JAXP uses context classloaders (as of J2SE 1.4)
    • java.util.ResourceBundle uses the caller's current classloader
    • URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only
    • Java Serialization API uses the caller's current classloader by default

    而且關于這些資源的類加載機制文檔時很少。

    java開發人員應該怎么做?

    如果你的實現是利用特定的框架,那么恭喜你,實現它遠比實現框架要簡單得多!例如,在web應用和EJB應用中,你僅僅只要使用 Class.getResource()就足夠了。

    其他的情形下,俺有個建議(這個原則是俺工作中發現的,侵權必究,抵制盜版。),

    下面這個類可以在整個應用中的任何地方使用,作為一個全局的ClassLoader(所有的示例代碼可以從download下載):

     1 public abstract class ClassLoaderResolver {
     2 /**
     3 * This method selects the best classloader instance to be used for
     4 * class/resource loading by whoever calls this method. The decision
     5 * typically involves choosing between the caller's current, thread context,
     6 * system, and other classloaders in the JVM and is made by the
     7 * {@link IClassLoadStrategy} instance established by the last call to
     8 * {@link #setStrategy}.
     9 *
    10 @return classloader to be used by the caller ['null' indicates the
    11 * primordial loader]
    12 */
    13 public static synchronized ClassLoader getClassLoader() {
    14 final Class caller = getCallerClass(0);
    15 final ClassLoadContext ctx = new ClassLoadContext(caller);
    16 
    17 return s_strategy.getClassLoader(ctx);
    18 }
    19 
    20 public static synchronized IClassLoadStrategy getStrategy() {
    21 return s_strategy;
    22 }
    23 
    24 public static synchronized IClassLoadStrategy setStrategy(
    25 final IClassLoadStrategy strategy) {
    26 final IClassLoadStrategy old = s_strategy;
    27 s_strategy = strategy;
    28 
    29 return old;
    30 }
    31 
    32 /**
    33 * A helper class to get the call context. It subclasses SecurityManager to
    34 * make getClassContext() accessible. An instance of CallerResolver only
    35 * needs to be created, not installed as an actual security manager.
    36 */
    37 private static final class CallerResolver extends SecurityManager {
    38 protected Class[] getClassContext() {
    39 return super.getClassContext();
    40 }
    41 
    42 // End of nested class
    43 
    44 /*
    45 * Indexes into the current method call context with a given offset.
    46 */
    47 private static Class getCallerClass(final int callerOffset) {
    48 return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET
    49 + callerOffset];
    50 }
    51 
    52 private static IClassLoadStrategy s_strategy; // initialized in <clinit>
    53 
    54 private static final int CALL_CONTEXT_OFFSET = 3// may need to change if
    55 // this class is
    56 // redesigned
    57 private static final CallerResolver CALLER_RESOLVER; // set in <clinit>
    58 
    59 static {
    60 try {
    61 // This can fail if the current SecurityManager does not allow
    62 // RuntimePermission ("createSecurityManager"):
    63 
    64 CALLER_RESOLVER = new CallerResolver();
    65 catch (SecurityException se) {
    66 throw new RuntimeException(
    67 "ClassLoaderResolver: could not create CallerResolver: "
    68 + se);
    69 }
    70 
    71 s_strategy = new DefaultClassLoadStrategy();
    72 }
    73 // End of class.
    74 
    75 
    76 


    通過ClassLoaderResolver.getClassLoader()方法獲得一個ClassLoader的引用,并且利用正常的ClassLoader的api去加載資源,你也可以使用 ResourceLoader API作為備選方案

     1 public abstract class ResourceLoader {
     2 
     3 /**
     4  * @see java.lang.ClassLoader#loadClass(java.lang.String)
     5  */
     6 public static Class loadClass (final String name)throws ClassNotFoundException{
     7 
     8 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
     9 
    10 return Class.forName (name, false, loader);
    11 
    12 }
    13 
    14 /**
    15 
    16 @see java.lang.ClassLoader#getResource(java.lang.String)
    17 
    18 */    
    19 
    20 
    21 public static URL getResource (final String name){
    22 
    23 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
    24 
    25 if (loader != null)return loader.getResource (name);
    26 else return ClassLoader.getSystemResource (name);
    27 }
    28  more methods 
    29 
    30 // End of class

    而決定使用何種ClassLoader策略是由接口實現的,這是一種插件機制,方便變更。

    public interface IClassLoadStrategy{
    ClassLoader getClassLoader (ClassLoadContext ctx);
    // End of interface

    它需要一個ClassLoader Context 對象去決定使用何種ClassLoader策略。
     1 public class ClassLoadContext{
     2 
     3 public final Class getCallerClass (){
     4 return m_caller;
     5 }
     6 
     7 ClassLoadContext (final Class caller){
     8 m_caller = caller;
     9 
    10 }
    11 
    12 private final Class m_caller;
    13 
    14 // End of class

    ClassLoadContext.getCallerClass()返回調用者給ClassLoaderResolver 或者 ResourceLoader,因此能獲得調用者的ClassLoader。需要注意的是,調用者是不會變的 (注:作者使用的final修飾字)。俺的方法不需要對現有的業務方法做擴展,而且可以作為靜態方法是用。而且,你可以根據自己的業務場景實現獨特的ClassLoaderContext。

    看出來沒,這是一種很熟悉的設計模式,XD ,把獲得ClassLoader的策略從業務中獨立出來,這個策略可以是"總是用ContextClassLoader"或者"總是用當前ClassLoader"。想預先知道那種策略是正確的比較困難,那么這種模式可以讓你簡單的改變策略。

    俺寫了一個默認的實現,基本可以對付95%的場景(enjoy yourself)

     1 public class DefaultClassLoadStrategy implements IClassLoadStrategy{
     2 
     3 public ClassLoader getClassLoader (final ClassLoadContext ctx){
     4 
     5 final ClassLoader callerLoader = ctx.getCallerClass ().getClassLoader ();
     6 
     7 final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader ();
     8 
     9 ClassLoader result;
    10 // If 'callerLoader' and 'contextLoader' are in a parent-child
    11 // relationship, always choose the child:
    12 if (isChild (contextLoader, callerLoader))result = callerLoader;
    13 else if (isChild (callerLoader, contextLoader))result = contextLoader;
    14 else{
    15 // This else branch could be merged into the previous one,
    16 // but I show it here to emphasize the ambiguous case:
    17 result = contextLoader;
    18 }
    19 final ClassLoader systemLoader = ClassLoader.getSystemClassLoader ();
    20 
    21 
    22 // Precaution for when deployed as a bootstrap or extension class:
    23 if (isChild (result, systemLoader))result = systemLoader;
    24 return result;
    25 }
    26 
    27 
    28 
    29  more methods 
    30 
    31 // End of class
    32 


    上面的邏輯比較簡單,如果當前ClassLoader和Context ClassLoader是父子關系,那就總選兒子,根據委托原則,這個很容易理解。

    如果兩人平級,選擇正確的ClassLoader很重要,運行時不允許含糊。這種情況下,我的代碼選擇Context ClassLoader(這是俺個人的經驗之談),當然也不要擔心不能改變,你能隨便根據需要改變。一般而言,Context ClassLoader比較適合框架,而Current ClassLoader在業務邏輯中用的更多。

    最后,檢查確保選中的ClassLoader不是System ClassLoader的parent,一旦高于System ClassLoader ,請使用System ClassLoader(你的類部署在Ext路徑下面,就會出現這種情況)。

    請注意,俺故意沒關注被載入資源的名稱。Java XML API 成為java 核心api的經歷告訴我們,根據資源名稱過濾是很不cool的idea。而且 我也沒有去確認到底哪個ClassLoader被取得了,因為只要清楚原理,這很容易被推理出來。(哈哈,俺是強淫)

    盡管討論java 的ClassLoader不是一個很cool的話題(譯者注,當年不cool,但是現在很cool),而且Java EE的ClassLoader策略越發的依賴各種平臺的升級。如果這沒有一個更好的設計的話,將會變成一個大大的問題。不敢您是否同意俺的觀點,俺尊重你說話的權利,所以請給俺分享您的意見經驗。

    作者介紹:

    Vladimir Roubtsov,曾經使用多種語言有超過13年的編程經歷(恩 現在應該超過15年了 hoho),95年開始接觸java(hoho 俺是99年看的第一本java書)。現在為Trilogy in Austin, Texas開發企業軟件。



    翻譯完了,MMD 翻譯還是很麻煩的。 XD ........

    43 Things :

    posted on 2008-05-30 18:22 季失羽 閱讀(5295) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲日本视频在线观看| 免费无码不卡视频在线观看| 99久久精品毛片免费播放| 亚洲a∨无码精品色午夜| 亚洲乱人伦中文字幕无码| 最新亚洲卡一卡二卡三新区| 亚洲国产日产无码精品| 亚洲中字慕日产2020| 亚洲国产成人九九综合| 亚洲人成片在线观看| 亚洲午夜电影一区二区三区| 亚洲一区二区三区久久| 亚洲Av高清一区二区三区| 色天使亚洲综合在线观看| 久久久久亚洲国产| 亚洲日韩中文字幕一区| 色欲aⅴ亚洲情无码AV| 日韩一级片免费观看| 在线视频网址免费播放| 国产成人免费ā片在线观看老同学 | 亚洲视频在线免费观看| 99精品视频免费观看| 色老头永久免费网站| 成年女人免费v片| 麻豆国产VA免费精品高清在线| 日韩精品视频免费网址| yy6080亚洲一级理论| 久久精品国产精品亚洲| 亚洲国产精品无码专区在线观看| 亚洲第一中文字幕| 在线a亚洲老鸭窝天堂av高清| 精品亚洲国产成人av| 精精国产www视频在线观看免费| 久久免费高清视频| 无码永久免费AV网站| 情侣视频精品免费的国产| 亚洲综合图色40p| 91天堂素人精品系列全集亚洲| 日韩亚洲产在线观看| 午夜在线免费视频 | 亚洲精品中文字幕无乱码麻豆|