Leo's Blog

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            13 隨筆 :: 3 文章 :: 18 評論 :: 0 Trackbacks

          #

          時間:2005-11-04
          作者:juxtapose


           如果大家對一般類的裝載器熟悉的話,就知道在java中類的裝載采用“代理機制”,即子裝載器如果需要裝載一個類文件,首先會將此任務提交給父 裝載器,如果父裝載器找不到此類文件,才有子裝載器來裝載類文件,如果子裝載器也找不到,那么就會報告ClassNotFoundException異 常。下面簡單談一下我對weblogic server的類裝載器原理的了解,希望能和大家分享。

          1.Weblogic允許定制的類裝載器,同時也有一個默認的類裝載器。其默認的裝載器的結構分層如下:

           

            當部署一個應用的時候,weblogic server會自動創建一個具有層次結構的類裝載器。在圖中,a.Application Classloader負責裝載應用中的所有的EJB JAR文件;

            b.Web Application Classloader負責裝載所有的Web application 中的WAR 文件(所有得jsp文件除外);

            c.Jsp Classloader 負責裝載Web application 中的所有的jsp 文件;

            這樣的分層結構有一個好處,就是在Jsp,Servlet中可以直接訪問EJB的接口。這種上層裝載EJB,下層裝載servlet等,最下面裝載jsp文件的結構,使得經常變動的jsp,servlet等可以被重新裝載而不會涉及到EJB層。

          在這種默認的類裝載器結構下,有一點需要提出的是:

            a. 我們的應用必須打包成一個EAR文件,才會允許我們應用中的jsp和servlet文件直接訪問ejb;如果將WAR與JAR文件分別打包。 Weblogic server會為他們分別生成一個類裝載器,作為兄弟節點,這時如果需要在jsp或者servlet中使用ejb,就必須將EJB的Home接口與 remote接口打包到WAR中才可以。后面這種情況,適合用在將EJB的客戶端和EJB部署在不同的JVM中;

            b.web application classloader中,不會裝載jsp文件,jsp文件由web application classloader的子裝載器Jsp classloader負責裝載,因為jsp文件經常的變動,通過為jsp設立一個單獨的classloader可以避免對jsp的裝載影響到其他的 java class或者ejb;

          默認裝載器的優點:

            a. 調用ejb的時候可以采用call-by-referrence的方式;

            b. 允許web module獨立的裝載,不影響其它的web module;

          • 通過在將整個應用打包成一個EAR文件,可以方便的不用再web module中包含EJB的home和remote接口,就可以方便的通過call-by-referrence來調用ejb;

          2. 定制classloader
            如果覺得默認的類裝載器不能滿足需要,weblogic server支持定制的類裝載器。在weblogic的文檔中指出,自定義的classloader多用于開發者使用,當應用發布之后,不推薦使用。自定 義的類裝載器通過xml文件來描述。描述文件放在weblogic-application.xml中。Weblogic官方提供的DTD描述文件如下:

          <classloader-structure> 
              
          <module-ref> 
                  
          <module-uri>ejba.jar</module-uri> 
              
          </module-ref> 
              
          <module-ref> 
                  
          <module-uri>webc.war</module-uri> 
              
          </module-ref>
              
          <classloader-structure> 
                  
          <module-ref> 
                      
          <module-uri>weba.war</module-uri> 
                  
          </module-ref> 
              
          </classloader-structure>
              
          <classloader-structure> 
                  
          <module-ref> 
                      
          <module-uri>ejbc.jar</module-uri> 
                  
          </module-ref> 
                  
          <module-ref> 
                      
          <module-uri>webb.war</module-uri> 
                  
          </module-ref>
                  
          <classloader-structure> 
                      
          <module-ref> 
              
          <module-uri>webd.war</module-uri> 
                      
          </module-ref> 
                  
          </classloader-structure> 
                  
          <classloader-structure> 
                      
          <module-ref> 
              
          <module-uri>ejbb.jar</module-uri> 
                      
          </module-ref> 
                  
          </classloader-structure>
              
          </classloader-structure>
          </classloader-structure>

            通過我們給出的配置文件,我們自定義的classloader的層次結構如下圖:

           

            在J2EE的規范中明確的指出,J2EE應用不應該依賴于任一個給定的類裝載器。所以,我們自定義的類裝載器,在開發過程中還是可以使用的,但一定不要應用于發布后的應用中。

          自定義的類裝載器有如下得限制:
            a.不能夠裝載servlet;

            b.嵌套的深度最大為3,也就是說,最多只能夠嵌套三層;

            c.自定義裝載器的module類型僅限于 Web和 EJB這兩種;

            d.Jsp Classloader不受此自定義類裝載器的影響,它永遠都是web module的子類裝載器;

          • 相同的類可能導致部署異常;
          • 在自定義的類裝載器中,如果要使用EJB,就必須將EJB的home和remote接口打包到相應的web module中去;
          •  

          3.Ejb的單獨加載
            
          有時候我們可能需要單獨加載某個EJB,這個時候我們可以通過以下兩種方法來實現:

            第一:將應用需要的jar文件放在APP-INF/lib中,或者將類文件放在APP-INF/classes中,這些類文件和JAR文件會被root classloader進行裝載,可以被多個應用共享;

            第二:可以通過META-INF/MANIFEST.MF文件來指定需要的classes。通常的用法是在META-INF/MANIFEST.MF文件中增加Class-Path:一行。舉例如下:

            Class-Path:/d:ejb/add.jar

            這樣就會在當前的jar包中可以找到我們需要的add.jar文件。需要說明的是,在Class-Path:行的最后一定要有一個換行,否則會發生錯誤。還有,通過Class-Path只能指定本地的JAR文件。

            如果能對應用服務器的類裝載原理有了較清楚地了解,會對我們的應用移植,在開發中避免不必要的類裝載的錯誤會有很大的幫助。

          轉載自:http://dev2dev.bea.com.cn/bbsdoc/20051104121.html

          posted @ 2006-02-23 18:37 Leo 閱讀(399) | 評論 (0)編輯 收藏

          作者:劉學超

          一、引言

          Java虛擬機(JVM)的類裝載就是指將包含在類文件中的字節碼裝載到JVM中, 并使其成為JVM一部分的過程。JVM的類動態裝載技術能夠在運行時刻動態地加載或者替換系統的某些功能模塊, 而不影響系統其他功能模塊的正常運行。本文將分析JVM中的類裝載系統,探討JVM中類裝載的原理、實現以及應用。

          二、Java虛擬機的類裝載實現與應用

          2.1  裝載過程簡介

          所謂裝載就是尋找一個類或是一個接口的二進制形式并用該二進制形式來構造代表這個類或是這個接口的class對象的過程,其中類或接口的名稱是給定了的。當然名稱也可以通過計算得到,但是更常見的是通過搜索源代碼經過編譯器編譯后所得到的二進制形式來構造。

          在Java中,類裝載器把一個類裝入Java虛擬機中,要經過三個步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗、準備和解析三步,除了解析外,其它步驟是嚴格按照順序完成的,各個步驟的主要工作如下:

          • 裝載:查找和導入類或接口的二進制數據;
          • 鏈接:執行下面的校驗、準備和解析步驟,其中解析步驟是可以選擇的;
          • 校驗:檢查導入類或接口的二進制數據的正確性;
          • 準備:給類的靜態變量分配并初始化存儲空間;
          • 解析:將符號引用轉成直接引用;
          • 初始化:激活類的靜態變量的初始化Java代碼和靜態Java代碼塊。

          至于在類裝載和虛擬機啟動的過程中的具體細節和可能會拋出的錯誤,請參看《Java虛擬機規范》以及《深入Java虛擬機》,它們在網絡上面的資源地址是: http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.htmlhttp://www.artima.com/insidejvm/ed2/index.html。 由于本文的討論重點不在此就不再多敘述。

          2.2  裝載的實現

          JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader 是一個重要的Java運行時系統組件。它負責在運行時查找和裝入類文件的類。

          在Java中,ClassLoader是一個抽象類,它在包java.lang中,可以這樣說,只要了解了在 ClassLoader中的一些重要的方法,再結合上面所介紹的JVM中類裝載的具體的過程,對動態裝載類這項技術就有了一個比較大概的掌握,這些重要的 方法包括以下幾個:

          ①loadCass方法  loadClass(String name ,boolean resolve)其中name參數指定了JVM需要的類的名稱,該名稱以包表示法表示,如Java.lang.Object;resolve參數告訴方法 是否需要解析類,在初始化類之前,應考慮類解析,并不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要解析。這個 方法是ClassLoader 的入口點。

          ②defineClass方法  這個方法接受類文件的字節數組并把它轉換成Class對象。字節數組可以是從本地文件系統或網絡裝入的數據。它把字節碼分析成運行時數據結構、校驗有效性等等。

          ③findSystemClass方法  findSystemClass方法從本地文件系統裝入文件。它在本地文 件系統中尋找類文件,如果存在,就使用defineClass將字節數組轉換成Class對象,以將該文件轉換成類。當運行Java應用程序時,這是 JVM 正常裝入類的缺省機制。

          ④resolveClass方法  resolveClass(Class c)方法解析裝入的類,如果該類已經被解析過那么將不做處理。當調用loadClass方法時,通過它的resolve 參數決定是否要進行解析。

          ⑤findLoadedClass方法  當調用loadClass方法裝入類時,調用 findLoadedClass 方法來查看ClassLoader是否已裝入這個類,如果已裝入,那么返回Class對象,否則返回NULL。如果強行裝載已存在的類,將會拋出鏈接錯 誤。

          2.3  裝載的應用

          一般來說,我們使用虛擬機的類裝載時需要繼承抽象類java.lang.ClassLoader,其中必須實現的方 法是loadClass(),對于這個方法需要實現如下操作:(1) 確認類的名稱;(2) 檢查請求要裝載的類是否已經被裝載;(3) 檢查請求加載的類是否是系統類;(4) 嘗試從類裝載器的存儲區獲取所請求的類;(5) 在虛擬機中定義所請求的類;(6) 解析所請求的類;(7) 返回所請求的類。

          所有的Java 虛擬機都包括一個內置的類裝載器,這個內置的類庫裝載器被稱為根裝載器(bootstrap ClassLoader)。根裝載器的特殊之處是它只能夠裝載在設計時刻已知的類,因此虛擬機假定由根裝載器所裝載的類都是安全的、可信任的,可以不經過 安全認證而直接運行。當應用程序需要加載并不是設計時就知道的類時,必須使用用戶自定義的裝載器(user-defined ClassLoader)。下面我們舉例說明它的應用。

           1 public abstract class MultiClassLoader extends ClassLoader{
           2     
           3     public synchronized Class loadClass(String s, boolean flag)
           4         throws ClassNotFoundException
           5     {
           6         /* 檢查類s是否已經在本地內存*/
           7         Class class1 = (Class)classes.get(s);
           8 
           9         /* 類s已經在本地內存*/
          10         if(class1 != null)  return class1; 
          11         try/*用默認的ClassLoader 裝入類*/  {
          12             class1 = super.findSystemClass(s);
          13             return class1;
          14         }
          15         catch(ClassNotFoundException _ex)  {
          16             System.out.println(">> Not a system class.");
          17         }
          18 
          19         /* 取得類s的字節數組*/
          20         byte abyte0[] = loadClassBytes(s);
          21         if(abyte0 == null)   throw new ClassNotFoundException();
          22 
          23         /* 將類字節數組轉換為類*/
          24         class1 = defineClass(null, abyte0, 0, abyte0.length);
          25         if(class1 == nullthrow new ClassFormatError();
          26         if(flag)   resolveClass(class1); /*解析類*/
          27 
          28         /* 將新加載的類放入本地內存*/
          29         classes.put(s, class1);
          30         System.out.println(">> Returning newly loaded class.");
          31 
          32         /* 返回已裝載、解析的類*/
          33         return class1;
          34     }
          35     
          36 }

          三、Java虛擬機的類裝載原理

          前面我們已經知道,一個Java應用程序使用兩種類型的類裝載器:根裝載器(bootstrap)和用戶定義的裝載 器(user-defined)。根裝載器是Java虛擬機實現的一部分,舉個例子來說,如果一個Java虛擬機是在現在已經存在并且正在被使用的操作系 統的頂部用C程序來實現的,那么根裝載器將是那些C程序的一部分。根裝載器以某種默認的方式將類裝入,包括那些Java API的類。在運行期間一個Java程序能安裝用戶自己定義的類裝載器。根裝載器是虛擬機固有的一部分,而用戶定義的類裝載器則不是,它是用Java語言 寫的,被編譯成class文件之后然后再被裝入到虛擬機,并像其它的任何對象一樣可以被實例化。 Java類裝載器的體系結構如下所示:

          圖1  Java的類裝載的體系結構

          Java的類裝載模型是一種代理(delegation)模型。當JVM 要求類裝載器CL(ClassLoader)裝載一個類時,CL首先將這個類裝載請求轉發給他的父裝載器。只有當父裝載器沒有裝載并無法裝載這個類時, CL才獲得裝載這個類的機會。這樣, 所有類裝載器的代理關系構成了一種樹狀的關系。樹的根是類的根裝載器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根裝載器以外的類裝載器有且僅有一個父裝載器。在創建一個裝載器時, 如果沒有顯式地給出父裝載器, 那么JVM將默認系統裝載器為其父裝載器。Java的基本類裝載器代理結構如圖2所示:

          圖2  Java類裝載的代理結構

          下面針對各種類裝載器分別進行詳細的說明。

          根(Bootstrap) 裝載器:該裝載器沒有父裝載器,它是JVM實現的一部分,從sun.boot.class.path裝載運行時庫的核心代碼。

          擴展(Extension) 裝載器:繼承的父裝載器為根裝載器,不像根裝載器可能與運行時的操作系統有關,這個類裝載器是用純Java代碼實現的,它從java.ext.dirs (擴展目錄)中裝載代碼。

          系統(System or Application) 裝載器:裝載器為擴展裝載器,我們都知道在安裝JDK的時候要設置環境變量(CLASSPATH ),這個類裝載器就是從java.class.path(CLASSPATH 環境變量)中裝載代碼的,它也是用純Java代碼實現的,同時還是用戶自定義類裝載器的缺省父裝載器。

          小應用程序(Applet) 裝載器: 裝載器為系統裝載器,它從用戶指定的網絡上的特定目錄裝載小應用程序代碼。

          在設計一個類裝載器的時候,應該滿足以下兩個條件:

          1. 對于相同的類名,類裝載器所返回的對象應該是同一個類對象
          2. 如果類裝載器CL1將裝載類C的請求轉給類裝載器CL2,那么對于以下的類或接口,CL1和CL2應該返回同一個類對象:a)S為C的 直接超類;b)S為C的直接超接口;c)S為C的成員變量的類型;d)S為C的成員方法或構建器的參數類型;e)S為C的成員方法的返回類型。

          每個已經裝載到JVM中的類都隱式含有裝載它的類裝載器的信息。類方法getClassLoader 可以得到裝載這個類的類裝載器。一個類裝載器認識的類包括它的父裝載器認識的類和它自己裝載的類,可見類裝載器認識的類是它自己裝載的類的超集。注意我們 可以得到類裝載器的有關的信息,但是已經裝載到JVM中的類是不能更改它的類裝載器的。

          Java中的類的裝載過程也就是代理裝載的過程。比如:Web瀏覽器中的JVM需要裝載一個小應用程序 TestApplet。JVM調用小應用程序裝載器ACL(Applet ClassLoader)來完成裝載。ACL首先請求它的父裝載器, 即系統裝載器裝載TestApplet是否裝載了這個類, 由于TestApplet不在系統裝載器的裝載路徑中, 所以系統裝載器沒有找到這個類, 也就沒有裝載成功。接著ACL自己裝載TestApplet。ACL通過網絡成功地找到了TestApplet.class 文件并將它導入到了JVM中。在裝載過程中, JVM發現TestAppet是從超類java.applet.Applet繼承的。所以JVM再次調用ACL來裝載 java.applet.Applet類。ACL又再次按上面的順序裝載Applet類, 結果ACL發現他的父裝載器已經裝載了這個類, 所以ACL就直接將這個已經裝載的類返回給了JVM , 完成了Applet類的裝載。接下來,Applet類的超類也一樣處理。最后, TestApplet及所有有關的類都裝載到了JVM中。

          四、結論

          類的動態裝載機制是JVM的一項核心技術, 也是容易被忽視而引起很多誤解的地方。本文介紹了JVM中類裝載的原理、實現以及應用,尤其分析了ClassLoader的結構、用途以及如何利用自定義 的ClassLoader裝載并執行Java類,希望能使讀者對JVM中的類裝載有一個比較深入的理解。

          轉載自:http://gceclub.sun.com.cn/yuanchuang/week-8/jvm.html

          posted @ 2006-02-23 18:31 Leo 閱讀(366) | 評論 (0)編輯 收藏

          昨天在整合Spring MVC和Velocity,Sitemesh時,又碰到了久違的中文問題。唉,JSP, mysql, struts,每次都會碰到這樣的問題,總是以為這種以后不會碰到這種看似初級的問題了,結果還是躲不過。網上沒查到相關資料,于是開始動手跟蹤Spring和Velocity的源碼,弄了一天終于搞定。后來一個同學告訴我這個問題在Spring中文論壇里有精華貼,跟我最后的解決方案一樣的,氣死我也。不過跟蹤Spring的源代碼收獲還是不錯的,現在又對Spring的MVC framework有了更深的認識。這里把以前碰到的中文問題大概列一下,方便以后參考。

          1、JSP頁面顯示的中文問題
          這是最初級的東西,網上到處都有,不過還是列一下吧:
          Page的第一行改成:<%@ page contentType="text/html; charset=gb2312" %>
          Head里加:
          2、頁面Form 內容提交的中文問題
          在web.xml里加入:

          CharacterEncodingFilter
          Character Encoding Filter
          no description
          org.springframework.web.filter.CharacterEncodingFilter

          encoding
          GB2312


          forceEncoding
          true



          CharacterEncodingFilter
          /*

          呵呵,這是個簡單得要命的filter,如果不用Spring的話,完全可以自己寫一個。其實任何的interceptor機制都可以處理這個的,不管用Webwork還是Spring的interceptor,甚至用AOP,只要在取參數前加那么一句:request.setCharacterEncoding("GB2312");就行了。以前我用struts就是在它的RequestProcessor的populate之前加了這么一行。
          3、request 的parameter里要傳中文參數的問題
          這個問題跟Web Container有關系,記得以前我同學用WebLogic時好象沒出現這樣的問題。(我一般不傳中文參數,呵呵)。
          Tomcat里的解決方案是在server.xml里Connector port="8080"的attribute里加URIEncoding="GB2312"
          當然還有最土的解決方案,雖然不太會用到,不過還是列出來,以備最無奈的時候使用:
          String encodeStr=new String(fieldValue.getBytes("8859_1"), "gb2312");
          4、mysql的中文問題
          首先要修改mysql配置文件的encoding為GB2312,這部分的操作不記得了,畢竟好久沒用mysql了。不過據說新版的mysql里有wizard可以設的。然后把jdbc connection改成如下:
          jdbc:mysql://localhost:3306/bsfbookstore?useUnicode=true;characterEncoding=GB2312
          另外在寫程序成盡量用PrepareStatement,少用Statement,好象jdbc驅動在解析statement里的SQL包含中文時會有問題。(用PrepareStatement也是好習慣, hibernate里全用PrepareStatement的,哈哈)
          5、Spring與Veclocity結合的中文問題
          第一步:
          在"velocityConfig"里配置velocity.propeties文件,加下面一行:
          /WEB-INF/velocity.properties
          呵呵,也可以在config里直接用Map把參數寫進去,這樣就不用properties文件,這個Spring的文檔里都有。
          然后在velocity.properties里寫:
          input.encoding=GB2312
          output.encoding=GB2312
          default.contentType=text/html; charset=GB2312(ms這一行沒有用處,Spring有個地方讀進這個參數,不過后來又覆蓋掉了)
          第二步:
          接下來就是我昨天調了半天的那個地方,最后的解決方案很簡單,在viewResolver配置里加一行:
          text/html; charset=GB2312
          呵呵,就這么一行害我debug了好久,跟蹤了Velocity的Context設置,甚至改了Spring的源碼,用了Filter,Spring的Handler interceptor來設置reponse的contentType就是沒效果,結果發現Spring在Velocity View的render里加了這么一行:
          response.setContentType(getContentType());
          呵呵,原先設好的contentType都被沖掉了,因為render的時機是在postHandler之后,呵呵。
          這個參數對jsp是沒有用的,因為jsp會根據自己頁面的contentType設定的,所以每個JSP必須設置自己的contentType,Velocity就不用啦。難怪以前用JSP的時候沒碰到這個問題。

          posted @ 2005-09-14 10:37 Leo 閱讀(1088) | 評論 (3)編輯 收藏

          僅列出標題
          共2頁: 上一頁 1 2 
          主站蜘蛛池模板: 墨江| 宜宾市| 玉林市| 织金县| 平和县| 夏邑县| 彩票| 黎川县| 阳朔县| 浦东新区| 柳州市| 洛阳市| 开平市| 卫辉市| 青田县| 宁安市| 杭锦后旗| 拜泉县| 游戏| 东乡| 玉溪市| 石台县| 安国市| 日土县| 蕲春县| 寿阳县| 怀集县| 临西县| 江达县| 江源县| 陆良县| 香港| 岚皋县| 龙南县| 黔江区| 文山县| 德钦县| 库尔勒市| 通城县| 浏阳市| 郓城县|