隨筆-128  評論-55  文章-5  trackbacks-0
           

          3.2.2 編譯源程序

          首先將兩個接口類Compute.classTask.class打成JAR包(compute.jar)傳到客戶端,或者向客戶端添加類路徑也行。這樣客戶端的類才能正確編譯。編譯過程不在詳述。

          需要被其他JVM動態加載的類要能夠通過網絡遠程訪問到,因此可以將它們放在一個Web服務器上,通過HTTP訪問。當然在本機上測試時也可以使用文件路徑,在此使用文件路徑,如file:/c:\home\user\public_html\classes/

          在本示例中注冊處需要動態從服務器端加載Compute.classTask.classComputeEngine_Stub.class

          存根類ComputeEngine_Stub是通過rmic命令生成的,在Dos窗口中,到ComputeEngine所在目錄(注意包路徑不包含在內),運行

          rmic engine. ComputeEngine  即可自動生成ComputeEngine_Stub.class

          注意此例中存根類是必須的,因為我們使用的是public static RemoteStub exportObject(Remote obj) throws RemoteException方法導出遠程對象的,如果采用其它方法則可以不需要,臨時動態生成動態代理(JDK5.0版本后)

          服務器端需要動態從客戶端加載類Pi.class

          客戶端需要動態從服務器端加載類ComputeEngine_Stub.class,而Compute.classTask.class本地路徑中已有。

           

          3.2.3 運行源程序

          服務器端和客戶端在運行時都需要加載一個安全管理器,因此我們首先需要編寫兩個安全策略文件,允許訪問鏈接,這里使用最簡單的策略文件。

          grant {

              permission java.security.AllPermission;

          };

          其中客戶端策略文件client.policy和服務器端策略文件server.policy是相同的。

          1.         啟動注冊表

          Dos窗口中運行 rmiregistry,此命令是JDK提供的。

          2.         啟動服務器 (命令如下,Windows環境)

          java  -cp c:\home\ann\src;c:\home\ann\public_html\classes\compute.jar

          -Djava.rmi.server.codebase=file:/c:\home\ann\public_html\classes\compute.jar

              -Djava.rmi.server.hostname=localhost

              -Djava.security.policy=server.policy

               engine.ComputeEngine

          java.rmi.server.codebase 指定了一個地址,所需要的類定義可以從此地址下載。

          java.rmi.server.hostname 指定了存放遠程對象所對應存根對象的主機名稱,客戶端在嘗試遠程對象方法調用時將用到此地址。

          java.security.policy 指定了所使用的安全策略文件。

          3.         啟動客戶端 (命令如下,Windows環境)

          java -cp c:\home\jones\src;c:\home\jones\public_html\classes\compute.jar

               -Djava.rmi.server.codebase=file:/c:/home/jones/public_html/classes/

               -Djava.security.policy=client.policy

                 client.ComputePi  localhost  45

          運行后會得到如下結果:

          3.141592653589793238462643383279502884197169399

          下圖描述了在程序執行過程中rmiregistryComputeEngine serverComputePi client在哪里獲得類定義。

          3.3 類加載圖

          四、JDK5.0中的動態代理類

          4.1 JDK5.0RMI的新特性

          如果是在JDK5.0下運行程序會發現一些有意思的事情,如不生成遠程對象的存根文件,程序在運行時不會報錯。仍然可以正確的運行得到結果(這一點困擾我很長時間)。這是因為在JDK5.0以前的版本中,需要用rmic命令來為遠程對象生成靜態的代理類(包括存根和骨架類),而在JDK5.0中,RMI框架會在運行時自動為遠程對象生成動態代理類(包括存根和骨架類),從而更徹底的封裝的RMI框架的實現細節,簡化了RMI框架的使用方式。

          JDK5.0中,如果無法加載遠程對象的stub類或將系統屬性 java.rmi.server.ignoreStubClasses 設置為 "true",并且我們使用除public static RemoteStub exportObject(Remote obj) throws RemoteException方法外的其它重載方法導出遠程對象時,程序運行時會構造一個遠程對象的動態代理,是類java.lang.reflect.Proxy的一個實例,這樣在向注冊表注冊時傳遞給注冊表的是一個Proxy對象,而不是ComputeEngine_Stub對象,因為Proxy類在JDK中已有類定義,因此注冊處并不需要從服務器端加載Proxy類定義,但Compute類和Task類還是需要從服務器端加載的。

          同樣的道理,客戶端從注冊處獲得Proxy動態代理對象后,也不需要從服務器端獲得其類定義,從而客戶端不會從服務器端動態加載任何類定義,也就是說客戶端的安全管理器可以省略。但服務器端仍然需要動態從客戶端加載Pi類定義,從而服務器端的安全管理器不能省略。

          但是一旦生成了ComputeEngine_Stub存根類,那么程序就不會動態生成動態代理,那么整個運行過程就如3.2中所述。

          4.2 JDK5.0中的動態代理類

          動態代理類的字節碼在程序運行時由Java反射機制動態生成,無須程序員手工編寫它的源代碼。java.lang.reflect包中的Proxy類和InvocationHandler接口提供了生成動態代理類的能力。

          Proxy 提供用于創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。

          l         public static Class<?> getProxyClass(ClassLoader loader,Class<?>[] interfaces) throws IllegalArgumentException 靜態方法負責創建動態代理類,并向其提供類加載器和接口數組。該代理類將由指定的類加載器定義,并將實現提供的所有接口。如果類加載器已經定義了具有相同排列接口的代理類,那么現有的代理類將被返回;否則,類加載器將動態生成并定義這些接口的代理類。

          l         public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException 靜態方法返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序。此方法相當于:Proxy.getProxyClass(loader, interfaces).getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

          Proxy類的靜態方法創建的動態代理類(以下成為代理類)具有以下特點:

          l         代理類是公共的、最終的,和非抽象類型的。

          l         未指定代理類的非限定名稱。但是,以字符串 "$Proxy" 開頭的類名空間應該為代理類保留。

          l         代理類繼承了 java.lang.reflect.Proxy類。

          l         代理類會按同一順序準確地實現其創建時指定的接口。

          l         如果代理類實現了非公共接口,那么它將在與該接口相同的包中定義。否則,代理類的包也是未指定的。注意,包密封將不阻止代理類在運行時在特定包中的成功定義,也不會阻止相同類加載器和帶有特定簽名的包所定義的類。

          l         由于代理類將實現所有在其創建時指定的接口,所以對其 Class 對象調用 getInterfaces 將返回一個包含相同接口列表的數組(按其創建時指定的順序),對其 Class 對象調用 getMethods 將返回一個包括這些接口中所有方法的 Method 對象的數組,并且調用 getMethod 將會在代理接口中找到期望的一些方法。

          l         Proxy類的isProxyClass(Class<?> cl) 靜態方法可用來判斷參數指定的類是否為動態代理類。只有由 Proxy.getProxyClass 返回的類,或由 Proxy.newProxyInstance 返回的對象的類才是動態代理類。

          l         代理類的 java.security.ProtectionDomain 與由引導類加載器(如 java.lang.Object)加載的系統類相同,原因是代理類的代碼由受信任的系統代碼生成。此保護域通常被授予 java.security.AllPermission

          l         每個代理類都有一個可以帶一個參數(接口 InvocationHandler 的實現)的公共構造方法,用于設置代理實例的調用處理程序。并非必須使用反射 API 才能訪問公共構造方法,通過調用 Proxy.newInstance 方法(將調用 Proxy.getProxyClass 的操作和調用帶有調用處理程序的構造方法結合在一起)也可以創建代理實例。

          l         每個代理類實例都和一個InvocationHandler實例關聯。Proxy類的getInvocationHandler(Object proxy)靜態方法返回與參數proxy指定的代理類實例所關聯的InvocationHandler對象。當調用代理類所實現接口的方法時,該方法會調用與它關聯的InvocationHandler對象的invoke()方法。

          InvacationHandler接口為方法調用接口,它聲明了負責調用任意一個方法的invoke()方法:Object invoke(Object proxy, Method method, Object[] args) throws Throwable,參數proxy指定動態代理類實例,參數method指定被調用的方法,參數args指定向被調用方法傳遞的參數,invoke方法的返回值表示被調用方法的返回值。

           

          五、Java RMI總結

          5.1 Java RMI的優點

          RMI機制給分布計算的系統設計、編程都帶來了極大的方便。只要按照RMI規則設計程序,可以不必再過問在RMI之下的網絡細節了,如:TCPSocket等等。任意兩臺計算機之間的通訊完全由RMI負責。調用遠程計算機上的對象就像本地對象一樣方便。

          1.         面向對象:

          RMI可將完整的對象作為參數和返回值進行傳遞,而不僅僅是預定義的數據類型。也就是說,可以將類似Java哈西表這樣的復雜類型作為一個參數進行傳遞。

          2.         可移動屬性:

          RMI可將屬性從客戶機移動到服務器,或者從服務器移動到客戶機。

          3.         設計方式:

          對象傳遞功能使您可以在分布式計算中充分利用面向對象技術的強大功能,如二層和三層結構系統。如果用戶能夠傳遞屬性,那么就可以在自己的解決方案中使用面向對象的設計方式。所有面向對象的設計方式無不依靠不同的屬性來發揮功能,如果不能傳遞完整的對象,包括實現和類型,就會失去設計方式上所提供的優點。

          4.         安全性:

          RMI使用Java內置的安全機制保證下載執行程序時用戶系統的安全。RMI使用專門為保護系統免遭惡意小程序侵害而設計的安全管理程序。

          5.         便于編寫和使用

          RMI使得Java遠程服務程序和訪問這些服務程序的Java客戶程序的編寫工作變得輕松、簡單。遠程接口實際上就是Java接口。為了實現RMI的功能必須創建遠程對象任何可以被遠程調用的對象必須實現遠程接口。但遠程接口本身并不包含任何方法。因而需要創建一個新的接口來擴展遠程接口。新接口將包含所有可以遠程調用的方法。遠程對象必須實現這個新接口,由于新的接口擴展了遠程接口,實現了新接口,就滿足了遠程對象對實現遠程接口的要求,所實現的每個對象都將作為遠程對象引用。

          5.2 Java RMI的缺點

          RMI雖然編寫起來比較方便,但運行的效率較低,而且RMI局限于Java之間的通信,無法與其他的語言平臺進行通信。因此出現了RMI-IIOP技術,RMI-IIOP綜合了RMI的簡單性和CORBA的多語言性(兼容性),其次RMI-IIOP克服了RMI只能用于Java的缺點和CORBA的復雜性(可以不用掌握IDL)。

           

          參考文獻

          [1] 李華琰、郭英奎 著:Java 中間件開發技術 . 中國水利水電出版社,2005

          [2] David Reilly , Michael Reilly著,沈鳳等譯.Java網絡編程與分布式計算.北京:機械工業出版社,2003

          [3]孫衛琴 著.Java網絡編程精解.北京:電子工業出版社,2007

          [4] Trail RMI (The Java™ Tutorials)2007

              http://java.sun.com/docs/books/tutorial/rmi/index.html

          [5] JDK5.0 中文文檔

           



          Author: orangelizq
          email: orangelizq@163.com

          歡迎大家訪問我的個人網站 萌萌的IT人
          posted on 2007-12-01 21:01 桔子汁 閱讀(1780) 評論(0)  編輯  收藏 所屬分類: J2SE
          主站蜘蛛池模板: 青河县| 昌平区| 鄂托克旗| 江都市| 吴堡县| 伊春市| 临澧县| 六盘水市| 政和县| 赤壁市| 鄱阳县| 莒南县| 东海县| 大安市| 庄浪县| 沂源县| 莫力| 上栗县| 玉龙| 衡阳市| 印江| 乾安县| 分宜县| 清河县| 常宁市| 兴义市| 通化县| 福安市| 什邡市| 临潭县| 吐鲁番市| 阜南县| 宝山区| 连平县| 资源县| 临邑县| 抚州市| 读书| 石林| 德保县| 四子王旗|