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

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

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

    慘淡人生,平淡生活

    The Feature Is Stupid

    2009年3月16日 #

    實現web服務的三個誤區 讀后感

    我的消息吃了我的服務器!Kyle指出,通常,Web服務開發者開始經歷“內存溢出”的錯誤或者奇怪的“性能問題”時,總是會發現服 務器擁有極高的處理負載,CPU使用率接近100%,以及較低的吞吐量和高網絡延遲。導致這些癥狀的典型原因是非常大的(有時會達到50 MB或者更大)消息。而且,這些大消息往往包含了非常大的、作為XML消息主體的、采用base-64編碼的二進制編碼信息。導致其發生的原因通常是:

    ……開發者不理解技術的局限性:XML處理對解決許多問題都有用,但是你必須認識到消息是要被解析的——并且在大多數……產品中,這就意味著許多或者所有的消息都會駐留在內存中。

    Kyle建議采用如下方法來改善這種情況:

    • 不要發送冗余信息。在許多情況下,發送二進制數據時,你可能會發現消息高度重復。如果是這樣,你可能就要考慮在HTTP層面使用壓縮技術來改善你的網絡延遲。雖然這不會幫助你處理負載,但可能有助于減輕其中一個問題。
    • 在XML消息體中,根本不要嵌入二進制信息。這是較好的解決方法,還有幾種不同的途徑可以實現這一效果。比如,你可以使用帶有附件的SOAP或者消息傳輸優化機制(MTOM)繞過解析開銷,盡管這無助于網絡延遲問題。
    • ……還有一個更好的辦法,使用SOAP根本不發送大的二進制blob。替代方法,通過受控的文件傳輸系統,使用一個“帶外數據”傳輸……或者“聲明標簽(claim Check,參見《EIP模式》或這里)”模式,避免在SOAP和HTTP上發送大的二進制文件。




    任何一種技術都有它使用的環境,在做架構設計的時候一定要避免因為個人的偏好,無意識的舍棄某些選擇。 一種簡單的方法論是,根據需要達到的目的,列出所有可能的實現方案,最后做出決定。


    不好意思,你的數據正在顯示。根據Kyle所說,另一個典型的Web服務的“性能問題” 是,使用Web服務的層面非常、非常低——通常Web服務跟一個SQL語句相關,這是因為:

    誤解了SOA架構原則。一個優秀SOA架構的關鍵原則是你的服務應該具有高復用性。

    根據Kyle所說,這些情況通常發生在:

    ……如果設計是根據現有代碼“自上而下”衍生出服務,這類服務就會出現;通常,開發者會看著他們現有的架構圖并且決定將架構中的每一層(包括表現層)轉變成服務集。

    相反,在SOA架構的正確位置使用粗粒度的Web服務會更好。再次強調,檢查一個架構的標準分層模型,通常在架構中會有一個明確定義的地方已經封裝 了系統業務邏輯。可以使用“遠程門面模式(Remote Facade Pattern)”來包裝這些服務,以便用合適的方式來暴露基于模型的服務。



    同樣是可以利用方法論來避免問題,但對于粒度的把握就是一個經驗的問題。




    模式(Schema)?我們不需要任何發臭的模式! Kyle指出,通常開發者試圖重用現有代碼來生成和解析作為Web服務實現基礎的XML。這些實現通常使用XML解析器來編組/解組消息,同時使用 Java HTTP類來發送和接收XML文檔。使用Web服務時,通用的方法是,創建使用模式元素的WSDL文檔,使XML不受阻地通過,然后在現有代碼中對它們進 行解析。

    這個問題的癥狀是組織沒有看到SOA承諾的好處,而且維護他們的解決方案似乎比以前使用Web服務的時候更難(而不是更容易)

    簡單的解決方案是,每當寫Web服務時,不管使用WS-*標準還是使用REST方法,都要確保你創建了代表你文檔結構的完整準確的XML模式。

    如果你正在構建WS-* Web服務,那么這個XML應該被包含在描述你的Web服務的WSDL之中。即使你在使用REST方法,擁有易于訪問的XML模式將鼓勵你的服務被重用。






    posted @ 2009-03-16 14:55 季失羽 閱讀(225) | 評論 (0)編輯 收藏

    2009年3月3日 #

    如何看懂Java混淆后的反編譯代碼(轉)

    如何看懂Java混淆后的反編譯代碼

    作者:dozb

    一般情況下Java應用的開發者為了保護代碼不被別人抄襲,在生成class文件的時候都java文件進行了混淆,這種class文件用反編譯工具得到的結果很難看懂,并且不能進行編譯。本文從研究的角度,淺析如何讀懂這種反編譯過來的文件。

    例子一:賦值
    反編譯過來的代碼如下:
            Node node;
            Node node1 = _$3.getChildNodes().item(0);
            node1;
            node1;
            JVM INSTR swap ;
            node;
            getChildNodes();
            0;
            item();
            getChildNodes();
            0;
            item();
            getNodeValue();
            String s;
            s;
    原始語句:
            Node node;
            Node node1 = currDocument.getChildNodes().item(0);
     node = node1;
            String s = node.getChildNodes().item(0).getChildNodes().item(0).getNodeValue();
    注解:
            JVM INSTR swap ; //賦值語句
    練習:
            String s1;
            String s8 = node.getChildNodes().item(1).getChildNodes().item(0).getNodeValue();
            s8;
            s8;
            JVM INSTR swap ;
            s1;
            10;
            Integer.parseInt();
            int i;
            i;

       
    例子二:不帶參數創建對象
    反編譯過來的代碼如下:
            JVM INSTR new #244 <Class CrossTable>;
            JVM INSTR dup ;
            JVM INSTR swap ;
            CrossTable();
            CrossTable crosstable;
            crosstable;

    原始語句:
            CrossTable crosstable = new CrossTable();
    注解:
    練習:
            JVM INSTR new #246 <Class Database>;
            JVM INSTR dup ;
            JVM INSTR swap ;
            Database();
            Object obj;
            obj;

    例子三:帶參數創建對象
    反編譯過來的代碼如下:
            JVM INSTR new #262 <Class StringBuffer>;
            JVM INSTR dup ;
            JVM INSTR swap ;
            String.valueOf(s2);
            StringBuffer();
            s.substring(j, i);
            append();
            s6;
            append();
            toString();
            s2;
     
    原始語句:
     s2 = (new StringBuffer(String.valueOf(s2))).append(s.substring(j, i)).append(s6).toString();
    注解:
     此語句實際上是:s2 += s.substring(j, i) + s6;
    練習:

    例子四:for循環
    反編譯過來的代碼如下:
            int k = 0;
              goto _L4
    _L8:
     ...
     k++;
    _L4:
            if(k < as.length) goto _L8; else goto _L7

    原始語句:
     for(int k=0;k < as.length;k++)
     {
         ...
     }
    注解:

    例子五:while循環
    反編譯過來的代碼如下:
            String s1 = "";
              goto _L1
    _L3:
            JVM INSTR new #262 <Class StringBuffer>;
            JVM INSTR dup ;
            JVM INSTR swap ;
            String.valueOf(s1);
            StringBuffer();
            _$2(resultset, s, l);
            append();
            toString();
            s1;
    _L1:
            if(resultset.next()) goto _L3; else goto _L2

    原始語句:
     String s1 = "";
     while(resultset.next())
     {
      s1 = s1 + resultSetToString(resultset, s, l);

     }

    posted @ 2009-03-03 09:59 季失羽 閱讀(1647) | 評論 (0)編輯 收藏

    2008年5月30日 #

    [翻譯]走出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 @ 2008-05-30 18:22 季失羽 閱讀(5295) | 評論 (0)編輯 收藏

    2008年5月29日 #

    ClassLoader的幾個概念、類和對象的解釋

    首先,轉載一篇文章,個人認為是看到過了講得最清楚的 XD
     

    當JVM(Java虛擬機)啟動時,會形成由三個類加載器組成的初始類加載器層次結構:

            bootstrap classloader
                     |
            extension classloader
                     |
            system classloader

    bootstrap classloader - 引導(也稱為原始)類加載器,它負責加載Java的核心類。在Sun的JVM中,在執行java的命令中使用-Xbootclasspath選項或使用 -D選項指定sun.boot.class.path系統屬性值可以指定附加的類。這個加載器的是非常特殊的,它實際上不是 java.lang.ClassLoader的子類,而是由JVM自身實現的。大家可以通過執行以下代碼來獲得bootstrap classloader加載了那些核心類庫:
        URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
        
    for (int i = 0; i < urls.length; i++) {
          System.out.println(urls.toExternalForm());
        }

    在我的計算機上的結果為:
    file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/rt.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/jce.jar
    file:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar
    file:/C:/j2sdk1.4.1_01/jre/classes
    這時大家知道了為什么我們不需要在系統屬性CLASSPATH中指定這些類庫了吧,因為JVM在啟動的時候就自動加載它們了。

    extension classloader - 擴展類加載器,它負責加載JRE的擴展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統屬性指定的)中JAR的類 包。這為引入除Java核心類以外的新功能提供了一個標準機制。因為默認的擴展目錄對所有從同一個JRE中啟動的JVM都是通用的,所以放入這個目錄的 JAR類包對所有的JVM和system classloader都是可見的。在這個實例上調用方法getParent()總是返回空值null,因為引導加載器bootstrap classloader不是一個真正的ClassLoader實例。所以當大家執行以下代碼時:

        System.out.println(System.getProperty(
    "java.ext.dirs"));
        ClassLoader extensionClassloader
    =ClassLoader.getSystemClassLoader().getParent();
        System.out.println(
    "the parent of extension classloader : "+extensionClassloader.getParent());

    結果為:
    C:"j2sdk1.4.1_01"jre"lib"ext
    the parent of extension classloader : null
    extension classloader是system classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一個實際的classloader,所以為null。

    system classloader - 系統(也稱為應用)類加載器,它負責在JVM被啟動時,加載來自在命令java中的-classpath或者java.class.path系統屬性或者 CLASSPATH操作系統屬性所指定的JAR類包和類路徑。總能通過靜態方法ClassLoader.getSystemClassLoader()找 到該類加載器。如果沒有特別指定,則用戶自定義的任何類加載器都將該類加載器作為它的父加載器。執行以下代碼即可獲得:
        System.out.println(System.getProperty("java.class.path"));
    輸出結果則為用戶在系統屬性里面設置的CLASSPATH。
    classloader 加載類用的是全盤負責委托機制。所謂全盤負責,即是當一個classloader加載一個Class的時候,這個Class所依賴的和引用的所有 Class也由這個classloader負責載入,除非是顯式的使用另外一個classloader載入;委托機制則是先讓parent(父)類加載器 (而不是super,它與parent classloader類不是繼承關系)尋找,只有在parent找不到的時候才從自己的類路徑中去尋找。此外類加載還采用了cache機制,也就是如果 cache中保存了這個Class就直接返回它,如果沒有才從文件中讀取和轉換成Class,并存入cache,這就是為什么我們修改了Class但是必 須重新啟動JVM才能生效的原因。


    每個ClassLoader加載Class的過程是:
    1.檢測此Class是否載入過(即在cache中是否有此Class),如果有到8,如果沒有到2
    2.如果parent classloader不存在(沒有parent,那parent一定是bootstrap classloader了),到4
    3.請求parent classloader載入,如果成功到8,不成功到5
    4.請求jvm從bootstrap classloader中載入,如果成功到8
    5.尋找Class文件(從與此classloader相關的類路徑中尋找)。如果找不到則到7.
    6.從文件中載入Class,到8.
    7.拋出ClassNotFoundException.
    8.返回Class.

    其中5.6步我們可以通過覆蓋ClassLoader的findClass方法來實現自己的載入策略。甚至覆蓋loadClass方法來實現自己的載入過程。

    類加載器的順序是:
    先 是bootstrap classloader,然后是extension classloader,最后才是system classloader。大家會發現加載的Class越是重要的越在靠前面。這樣做的原因是出于安全性的考慮,試想如果system classloader“親自”加載了一個具有破壞性的“java.lang.System”類的后果吧。這種委托機制保證了用戶即使具有一個這樣的類, 也把它加入到了類路徑中,但是它永遠不會被載入,因為這個類總是由bootstrap classloader來加載的。大家可以執行一下以下的代碼:
        System.out.println(System.class.getClassLoader());
    將會看到結果是null,這就表明java.lang.System是由bootstrap classloader加載的,因為bootstrap classloader不是一個真正的ClassLoader實例,而是由JVM實現的,正如前面已經說過的。

    下面就讓我們來看看JVM是如何來為我們來建立類加載器的結構的:
    sun.misc.Launcher,顧名思義,當你執行java命令的時候,JVM會先使用bootstrap classloader載入并初始化一個Launcher,執行下來代碼:
       System.out.println("the Launcher's classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());
    結果為:
       the Launcher's classloader is null (因為是用bootstrap classloader加載,所以class loader為null)
    Launcher 會根據系統和命令設定初始化好class loader結構,JVM就用它來獲得extension classloader和system classloader,并載入所有的需要載入的Class,最后執行java命令指定的帶有靜態的main方法的Class。extension classloader實際上是sun.misc.Launcher$ExtClassLoader類的一個實例,system classloader實際上是sun.misc.Launcher$AppClassLoader類的一個實例。并且都是 java.net.URLClassLoader的子類。

    讓我們來看看Launcher初試化的過程的部分代碼。

    Launcher的部分代碼:
     1 public class Launcher  {
     2     public Launcher() {
     3         ExtClassLoader extclassloader;
     4         try {
     5             //初始化extension classloader
     6             extclassloader = ExtClassLoader.getExtClassLoader();
     7         } catch(IOException ioexception) {
     8             throw new InternalError("Could not create extension class loader");
     9         }
    10         try {
    11             //初始化system classloader,parent是extension classloader
    12             loader = AppClassLoader.getAppClassLoader(extclassloader);
    13         } catch(IOException ioexception1) {
    14             throw new InternalError("Could not create application class loader");
    15         }
    16         //將system classloader設置成當前線程的context classloader(將在后面加以介紹)
    17         Thread.currentThread().setContextClassLoader(loader);
    18         
    19     }
    20     public ClassLoader getClassLoader() {
    21         //返回system classloader
    22         return loader;
    23     }
    24 }
    25 

    extension classloader的部分代碼:
     1 static class Launcher$ExtClassLoader extends URLClassLoader {
     2 
     3     public static Launcher$ExtClassLoader getExtClassLoader()
     4         throws IOException
     5     {
     6         File afile[] = getExtDirs();
     7         return (Launcher$ExtClassLoader)AccessController.doPrivileged(new Launcher$1(afile));
     8     }
     9    private static File[] getExtDirs() {
    10         //獲得系統屬性“java.ext.dirs”
    11         String s = System.getProperty("java.ext.dirs");
    12         File afile[];
    13         if(s != null) {
    14             StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);
    15             int i = stringtokenizer.countTokens();
    16             afile = new File;
    17             for(int j = 0; j < i; j++)
    18                 afile[j] = new File(stringtokenizer.nextToken());
    19 
    20         } else {
    21             afile = new File[0];
    22         }
    23         return afile;
    24     }
    25 }


    system classloader的部分代碼:
     1 static class Launcher$AppClassLoader extends URLClassLoader
     2 {
     3 
     4     public static ClassLoader getAppClassLoader(ClassLoader classloader)
     5         throws IOException
     6     {
     7         //獲得系統屬性“java.class.path”
     8         String s = System.getProperty("java.class.path");
     9         File afile[] = s != null ? Launcher.access$200(s) : new File[0];
    10         return (Launcher$AppClassLoader)AccessController.doPrivileged(new Launcher$2(s, afile, classloader));
    11     }
    12 }


    看 了源代碼大家就清楚了吧,extension classloader是使用系統屬性“java.ext.dirs”設置類搜索路徑的,并且沒有parent。system classloader是使用系統屬性“java.class.path”設置類搜索路徑的,并且有一個parent classloader。Launcher初始化extension classloader,system classloader,并將system classloader設置成為context classloader,但是僅僅返回system classloader給JVM。

    這里怎么又出來一個context classloader呢?它有什么用呢?我們在建立一個線程Thread的時候,可以為這個線程通過setContextClassLoader方法來 指定一個合適的classloader作為這個線程的context classloader,當此線程運行的時候,我們可以通過getContextClassLoader方法來獲得此context classloader,就可以用它來載入我們所需要的Class。默認的是system classloader。利用這個特性,我們可以“打破”classloader委托機制了,父classloader可以獲得當前線程的context classloader,而這個context classloader可以是它的子classloader或者其他的classloader,那么父classloader就可以從其獲得所需的 Class,這就打破了只能向父classloader請求的限制了。這個機制可以滿足當我們的classpath是在運行時才確定,并由定制的 classloader加載的時候,由system classloader(即在jvm classpath中)加載的class可以通過context classloader獲得定制的classloader并加載入特定的class(通常是抽象類和接口,定制的classloader中是其實現),例 如web應用中的servlet就是用這種機制加載的.


    好了,現在我們了解了classloader的結構和工作原理,那么我們 如何實現在運行時的動態載入和更新呢?只要我們能夠動態改變類搜索路徑和清除classloader的cache中已經載入的Class就行了,有兩個方 案,一是我們繼承一個classloader,覆蓋loadclass方法,動態的尋找Class文件并使用defineClass方法來;另一個則非常 簡單實用,只要重新使用一個新的類搜索路徑來new一個classloader就行了,這樣即更新了類搜索路徑以便來載入新的Class,也重新生成了一 個空白的cache(當然,類搜索路徑不一定必須更改)。噢,太好了,我們幾乎不用做什么工作,java.netURLClassLoader正是一個符 合我們要求的classloader!我們可以直接使用或者繼承它就可以了!

    這是j2se1.4 API的doc中URLClassLoader的兩個構造器的描述:
    URLClassLoader(URL[] urls)
              Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
    URLClassLoader(URL[] urls, ClassLoader parent)
              Constructs a new URLClassLoader for the given URLs.
    其中URL[] urls就是我們要設置的類搜索路徑,parent就是這個classloader的parent classloader,默認的是system classloader。


    好,現在我們能夠動態的載入Class了,這樣我們就可以利用newInstance方法來獲得一個Object。但我們如何將此Object造型呢?可以將此Object造型成它本身的Class嗎?

    首先讓我們來分析一下java源文件的編譯,運行吧!javac命令是調用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方法來編譯:

        
    public static int compile(String as[]);

        
    public static int compile(String as[], PrintWriter printwriter);

    返回0表示編譯成功,字符串數組as則是我們用javac命令編譯時的參數,以空格劃分。例如:
    javac -classpath c:"foo"bar.jar;. -d c:" c:"Some.java
    則 字符串數組as為{"-classpath","c:""foo""bar.jar;.","-d","c:""","c:""Some.java"}, 如果帶有PrintWriter參數,則會把編譯信息出到這個指定的printWriter中。默認的輸出是System.err。

    其中 Main是由JVM使用Launcher初始化的system classloader載入的,根據全盤負責原則,編譯器在解析這個java源文件時所發現的它所依賴和引用的所有Class也將由system classloader載入,如果system classloader不能載入某個Class時,編譯器將拋出一個“cannot resolve symbol”錯誤。

    所以首先編譯就通不過,也就是編譯器無法編譯一個引用了不在CLASSPATH中的未知Class的java源文件,而由于拼寫錯誤或者沒有把所需類庫放到CLASSPATH中,大家一定經常看到這個“cannot resolve symbol”這個編譯錯誤吧!

    其 次,就是我們把這個Class放到編譯路徑中,成功的進行了編譯,然后在運行的時候不把它放入到CLASSPATH中而利用我們自己的 classloader來動態載入這個Class,這時候也會出現“java.lang.NoClassDefFoundError”的違例,為什么呢?

    我 們再來分析一下,首先調用這個造型語句的可執行的Class一定是由JVM使用Launcher初始化的system classloader載入的,根據全盤負責原則,當我們進行造型的時候,JVM也會使用system classloader來嘗試載入這個Class來對實例進行造型,自然在system classloader尋找不到這個Class時就會拋出“java.lang.NoClassDefFoundError”的違例。

    OK, 現在讓我們來總結一下,java文件的編譯和Class的載入執行,都是使用Launcher初始化的system classloader作為類載入器的,我們無法動態的改變system classloader,更無法讓JVM使用我們自己的classloader來替換system classloader,根據全盤負責原則,就限制了編譯和運行時,我們無法直接顯式的使用一個system classloader尋找不到的Class,即我們只能使用Java核心類庫,擴展類庫和CLASSPATH中的類庫中的Class。

    還 不死心!再嘗試一下這種情況,我們把這個Class也放入到CLASSPATH中,讓system classloader能夠識別和載入。然后我們通過自己的classloader來從指定的class文件中載入這個Class(不能夠委托 parent載入,因為這樣會被system classloader從CLASSPATH中將其載入),然后實例化一個Object,并造型成這個Class,這樣JVM也識別這個Class(因為 system classloader能夠定位和載入這個Class從CLASSPATH中),載入的也不是CLASSPATH中的這個Class,而是從 CLASSPATH外動態載入的,這樣總行了吧!十分不幸的是,這時會出現“java.lang.ClassCastException”違例。

    為 什么呢?我們也來分析一下,不錯,我們雖然從CLASSPATH外使用我們自己的classloader動態載入了這個Class,但將它的實例造型的時 候是JVM會使用system classloader來再次載入這個Class,并嘗試將使用我們的自己的classloader載入的Class的一個實例造型為system classloader載入的這個Class(另外的一個)。大家發現什么問題了嗎?也就是我們嘗試將從一個classloader載入的Class的一 個實例造型為另外一個classloader載入的Class,雖然這兩個Class的名字一樣,甚至是從同一個class文件中載入。但不幸的是JVM 卻認為這個兩個Class是不同的,即JVM認為不同的classloader載入的相同的名字的Class(即使是從同一個class文件中載入的)是 不同的!這樣做的原因我想大概也是主要出于安全性考慮,這樣就保證所有的核心Java類都是system classloader載入的,我們無法用自己的classloader載入的相同名字的Class的實例來替換它們的實例。

    看到這里,聰明的讀者一定想到了該如何動態載入我們的Class,實例化,造型并調用了吧!

    那 就是利用面向對象的基本特性之一的多形性。我們把我們動態載入的Class的實例造型成它的一個system classloader所能識別的父類就行了!這是為什么呢?我們還是要再來分析一次。當我們用我們自己的classloader來動態載入這我們只要把 這個Class的時候,發現它有一個父類Class,在載入它之前JVM先會載入這個父類Class,這個父類Class是system classloader所能識別的,根據委托機制,它將由system classloader載入,然后我們的classloader再載入這個Class,創建一個實例,造型為這個父類Class,注意了,造型成這個父類 Class的時候(也就是上溯)是面向對象的java語言所允許的并且JVM也支持的,JVM就使用system classloader再次載入這個父類Class,然后將此實例造型為這個父類Class。大家可以從這個過程發現這個父類Class都是由 system classloader載入的,也就是同一個class loader載入的同一個Class,所以造型的時候不會出現任何異常。而根據多形性,調用這個父類的方法時,真正執行的是這個Class(非父類 Class)的覆蓋了父類方法的方法。這些方法中也可以引用system classloader不能識別的Class,因為根據全盤負責原則,只要載入這個Class的classloader即我們自己定義的 classloader能夠定位和載入這些Class就行了。

    這樣我們就可以事先定義好一組接口或者基類并放入CLASSPATH中,然 后在執行的時候動態的載入實現或者繼承了這些接口或基類的子類。還不明白嗎?讓我們來想一想Servlet吧,web application server能夠載入任何繼承了Servlet的Class并正確的執行它們,不管它實際的Class是什么,就是都把它們實例化成為一個Servlet Class,然后執行Servlet的init,doPost,doGet和destroy等方法的,而不管這個Servlet是從web- inf/lib和web-inf/classes下由system classloader的子classloader(即定制的classloader)動態載入。說了這么多希望大家都明白了。在applet,ejb等 容器中,都是采用了這種機制.

    對于以上各種情況,希望大家實際編寫一些example來實驗一下。

    最后我再說點別 的,classloader雖然稱為類加載器,但并不意味著只能用來加載Class,我們還可以利用它也獲得圖片,音頻文件等資源的URL,當然,這些資 源必須在CLASSPATH中的jar類庫中或目錄下。我們來看API的doc中關于ClassLoader的兩個尋找資源和Class的方法描述吧:
    public URL getResource(String name)
    用指定的名字來查找資源,一個資源是一些能夠被class代碼訪問的在某種程度上依賴于代碼位置的數據(圖片,音頻,文本等等)。
                    一個資源的名字是以'/'號分隔確定資源的路徑名的。
                    這個方法將先請求parent classloader搜索資源,如果沒有parent,則會在內置在虛擬機中的classloader(即bootstrap classloader)的路徑中搜索。如果失敗,這個方法將調用findResource(String)來尋找資源。
    public static URL getSystemResource(String name)
                    從用來載入類的搜索路徑中查找一個指定名字的資源。這個方法使用system class loader來定位資源。即相當于ClassLoader.getSystemClassLoader().getResource(name)。

    例如:
        System.out.println(ClassLoader.getSystemResource("java/lang/String.class"));
    的結果為:
        jar:file:/C:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/String.class
    表明String.class文件在rt.jar的java/lang目錄中。
    因此我們可以將圖片等資源隨同Class一同打包到jar類庫中(當然,也可單獨打包這些資源)并添加它們到class loader的搜索路徑中,我們就可以無需關心這些資源的具體位置,讓class loader來幫我們尋找了!

    以上是轉自bea論壇的一篇文章,作者不清楚,估計是bea內部的大牛。是值得俺們頂禮膜拜的神一般的存在 XD



    最后 附上自己的幾點理解

    bootstrap classloader  -------  對應jvm中某c++寫的dll類
    Extenson ClassLoader ---------對應內部類ExtClassLoader
    System ClassLoader  ---------對應內部類AppClassLoader
    Custom ClassLoader  ----------對應任何URLClassLoader的子類(你也可以繼承SecureClassLoader或者更加nb一點 直接繼承ClassLoader,這樣的話你也是神一般的存在了 XD)


    以上四種classloder按照從上到下的順序,依次為下一個的parent

    這個第一概念

    第二個概念是幾個有關的classloader的類

               抽象類 ClassLoader
                      |
                SecureClassLoader
                       |
                URLClassloader
                 |           |                
     sun的ExtClassLoader   sun的AppClassLoader
    以上的類之間是繼承關系,與第一個概念說的parent是兩回事情,需要小心。

    第三個概念是Thread的ContextClassLoader
    其實從Context的名稱就可以看出來,這只是一個用以存儲任何classloader引用的臨時存儲空間,與classloader的層次沒有任何關系。


    第四 就是如何實現自己的classloader了,本來是要翻譯另外一篇文章Find a way out of the ClassLoader maze
    不過今天時間都花在《小夫妻天天惡戰》這篇神文上了 XD 強烈推薦任何和俺一樣期望彪悍人生的朋友都去看看

    翻譯明天補上,浪費時間是可恥的 XD

    匿鳥,晚上還有朋友推薦的 《lie with me》(與我同眠)要看。XD


    posted @ 2008-05-29 20:11 季失羽 閱讀(2260) | 評論 (1)編輯 收藏

    2008年5月28日 #

    關于extends 和 constructor的默認實現與覆蓋策略

    今天在TSS上又看到有人討論java多繼承的問題,是想起這個話題的原因。^_^


    java中任何類都默認繼承 Java.lang.Object,除非被另一個繼承覆蓋(override),hoho 俺一直這么稱呼override的,感覺更加貼切一些。
    請看以下代碼:

    package org.myth.test;

    public class SuperSon{
        
        SuperSon(){
            System.out.println(
    "this is super son");
        }

    }

    對于編譯器來說,這段代碼會被首先補全為:

    package org.myth.test;

    public class SuperSon extends Object{
        
        SuperSon(){
            System.out.println(
    "this is super son");
        }

    }

    對待任何一個類,編譯器會去檢查extends關鍵字,如果沒有,編譯器會默認添加extens Object

    extends Object就是一段默認隱藏的代碼,同樣在Constructor中,也有一段默認隱藏的代碼。

    package org.myth.test;

    public class SuperSon extends Object{
        
        SuperSon(){
            
    super();//這就是一段默認隱藏代碼
            System.out.println("this is super son");
        }

        //
    整個構造方法也是一段默認隱藏代碼

    }

    如同編譯類時編譯器回去檢查extends關鍵字一樣,編譯器會首先檢查是否存在constructor,如果沒有,默認增加ClassName()構造方法。
    在構造方法內部,編譯器會檢查第一行代碼是否為super構造方法,如果不是,默認添加super()

    這個就是為什么 new一個對象的時候,首先調用的是父類的構造方法。

    一個錯誤代碼示例:
    package org.myth.test;

    public class SuperMan {
        
        SuperMan(String s){
            System.out.println(
    "this is super man");
        }

    }



    package org.myth.test;

    public class SuperSon extends SuperMan{
        
        SuperSon(){
            System.out.println(
    "this is super son");
        }

    }


    嘿嘿 第一篇文章

    posted @ 2008-05-28 10:44 季失羽 閱讀(1297) | 評論 (10)編輯 收藏

    2008年5月26日 #

    Test Blog

    2008年5月26日
    初次使用blogjava



    posted @ 2008-05-26 17:35 季失羽| 編輯 收藏

    僅列出標題  
    主站蜘蛛池模板: 又黄又爽的视频免费看| 一个人免费高清在线观看| 亚洲国产午夜福利在线播放| 久久亚洲精品无码网站| 女人18毛片a级毛片免费视频| 日韩亚洲不卡在线视频中文字幕在线观看| 在线成人爽a毛片免费软件| 亚洲五月六月丁香激情| 7x7x7x免费在线观看| 亚洲精品午夜视频| 免费看韩国黄a片在线观看| 亚洲欧美国产国产一区二区三区 | 亚洲自偷自偷偷色无码中文| 一级午夜a毛片免费视频| 久久精品国产精品亚洲| 成人毛片100免费观看| 亚洲日韩精品无码专区加勒比☆| 99久久综合国产精品免费| 亚洲综合一区二区三区四区五区| 日本免费一二区在线电影| 污网站免费在线观看| 亚洲精品午夜国产VA久久成人| 免费观看91视频| 亚洲av成人一区二区三区| 日韩免费高清视频网站| 人人公开免费超级碰碰碰视频| 亚洲精品无码鲁网中文电影| 最近中文字幕大全免费视频| 亚洲人成色4444在线观看| 亚洲性日韩精品国产一区二区| 久久国产精品国产自线拍免费| 亚洲午夜久久久久久尤物| 国产gav成人免费播放视频| 国产在线精品免费aaa片| 狠狠色香婷婷久久亚洲精品| 免费在线一级毛片| 日韩精品人妻系列无码专区免费| 亚洲日韩一区二区三区| 亚洲精品无码乱码成人| 美女被免费视频网站a国产 | 亚洲综合伊人久久大杳蕉|