動態回調基于XML的遠程過程調用
摘要
Java 反射機制為使用XML-RPC(XML-based Remote Procedure Call,基于XML的遠程過程調用)遠程過程調用提供了一種簡便又高效的實現方法,這種方法隱蔽掉了一些遠程過程調用過程中的復雜操作。在這篇文章里, Stephan Maier展示給你如何從反射包中使用一些類去包裝XML-RPC去調用遠程接口:Proxy類,Array類, 和BeanInfo類。這篇文章也將要討論這種方法的多重實現和在RMI(Remote Method Invocation,遠程方法調用)中可反射方法的使用。
因為它作為遠程方法調用的一個簡單協議,人們偶爾關注基于XML的遠程過程調用(XML-RPC)。它易于使用,又著可以運用的實現-Apache XML-RPC。
如果是一個小應用程序或應用程序中使用有限數量的遠程過程,那么不應該趨向于正式地定義遠程過程的名字和方法聲明,取而代之,應該直接使用XML-RPC。甚至,應用程序規模增大和遠程接口數量增加,將可發現對遠程方法和數據對象必要的約定必定是一種莫名的一致。在這篇文章里,將展示Java提供的所有需要去實現的定義遠程接口和訪問遠程方法:過程,使用Java接口定義的過程簽名,和使用XML-RPC的遠程過程調用-這種調用把一個通信信道的兩側封裝成僅僅是接口和相對的數據對象。
本文也展示當給定的描述遠程過程和數據的Java接口構建與 JavaBean規范一致,可以使用Java反射機制,把反射機制和JavaBean整合使用可以透明地調用遠程方法,輕松地在XML-RPC數據類型和Java數據類型之間進行轉換。
實際中隱藏復雜性是個好的事情。無需說,不是所有的復雜性能或者應該被隱藏。針對分布式計算,這個觀點已經在“分布式計算筆記”有了個著名的論著。(Sun Microsystems,1994.11)。本文中展現的框架不特意去隱藏分布式計算的復雜性,但這個框架幫助使用者減少在調用遠程過程時的繁瑣。簡單點說,本文只討論并發遠程過程調用,熱心的讀者可以自己去研究觳皆凍坦痰饔謾?
XML-RPC可以被看作RPC跨SOAP協議的一個簡化。擴展點說,本文所討論的這個簡易框架必須被認為SOAP引擎的簡化版本,就像Axis。本文主要以教學位目標:希望展示在目前的XML-RPC框架上層是怎樣通過反射來建立一個簡化的XML-RPC引擎。這些幫助讀者理解相似且更復雜的其他協議的引擎的內部實現機理,或怎樣應用反射去解決復雜問題。一個RPC引擎必須在SOAP引擎可實現的環境中使用,就好比中間件控件不可用,那么應用程序就不能通過Web服務器發布給廣大用戶。Roy Miller’s “XML-RPC Java 編程”對這個作了很好的解釋。
在本文中,使用XML-RPC的Apache組件去安裝這個框架。讀者不需奧區知道XML-RPC,也不需要理解Apache XML-RPC框架,就是只有一個基本的了解都能使你理解下面的講述。本文重點放在框架內部精確的運作機理,但不涉及協議的細節。
版權聲明:任何獲得Matrix授權的網站,轉載時請務必保留以下作者信息和鏈接作者:
steven_guo(作者的blog:
http://blog.matrix.org.cn/page/steven_guo)
原文:
http://www.matrix.org.cn/resource/article/44/44437_XML-RPC.html關鍵字:XML-RPC;Reflective
消除習慣有時候,我更喜歡非傳統的編程。談到這點,我必須使你確信,我不是一個總打破舊習俗的人,我不反對好的編程習慣;恰恰與此完全相反。“非傳統”在這里主要表明我喜歡去避免程序中到處的定義字符串,而且這些代碼可以在可編程的API里定義。考慮下面代碼片斷。
代碼1. 調用遠程過程
Vector paras = new Vector();
paras.add("Herbert");
Object result = client.execute("app.PersonHome.getName", paras);
代碼1展示了通過Apzche XML-RPC的實現如何調用遠程過程。可以注意到,使用者需要去指導遠程過程和傳遞到遠程方法中參數的名字。也必須知道遠程過程返回的對象類型。除非你已經實現了一個類去驗證你使用的這些名字(app.PersonHome 和 getName)是否正確,那么你局需要去查找這些名字和聲明,通常這些信息保存在文本文件或常量接口中(一個接口提供所有參數名字)。也可能放置到 Javadoc中適當的地方。大家可以發現,這種約定有造成運行時錯誤的隱患,因為這些錯誤只在運行時才能顯現出來,而在編譯時不能顯現。
現在,對比考慮下面一段代碼:
代碼2調用遠程過程
Person person = ((PersonHome)Invocator.getProxy(PersonHome.class)).getPerson("Herbert");
在這,我們調用Invocator類的靜態方法getProxy()去檢索PersonHome接口的實現。在這個接口里,可以調用getPerson(),得到Person對象。
代碼2相對于代碼1是一個較簡潔的方式。在代碼2種,使用簡潔的定義在接口中的方法,在接口里可以把可用的方法,方法聲明,返回類型都一起定義。因為不需要強制轉換對象,所以也是類型安全。因為不需要額外的像Vector類的構造函數,代碼可讀性也較好。
而且,如果你使用了一個功能強大的IDE,代碼助手將羅列所有可用的方法和他們的實現。因此,在類型安全遠程方法調用上從IDE獲得支持。
我必須承認,沒有規范我們不能作任何事。我們必須堅持(除非我們準備接受高昂的成本和復雜化)的一個規則就是:假設所有的數據對象都遵從JavaBean規范。簡單地說,對象的屬性操作必須通過getter/setter方法。在我們討論把XML-RPC數據結構轉換成Java對象時,這個假設的重要性將顯現出來。
把所有數據對象遵從JavaBean規約是比在XML-RPC應用程序中的規約更高級一些,因為后者是一個通用規范。它也是所有Java 程序員的的通用規范。在本文結尾部分,我討論XML-RPC的限制,還要建議其他一些有用的規范,而且可以更好的讓你理解那些限制。
隨后的部分,一起瀏覽Invocator類的實現和一個提供框架通信信道的一個本地服務器。
實現調用讓我們首先看看提供一個接口實現的方法。
代碼3??創建代理
public static Object getProxy(Class ifType) {
?? if (!ifType.isInterface()) {
??????throw new AssertionError("Type must be an interface");
?? }
?? return Proxy.newProxyInstance(Invocator.class.getClassLoader(),
??????new Class[]{ifType}, new XMLRPCInvocationHandler(ifType));
}
所有的實現邏輯隱藏在了Proxy.newProxyInstance()方法中。Proxy類從Java1.3開始已經是Java Reflection包的一部分。經由newProxyInstance()方法,一些列的接口可以自動被實現。當然,一個proxy類不知道怎樣處理方法調用。因此,它必須把調用傳送給一個合適的處理器 – 一個實現 java.lang.reflect.InvocationHandler類的作業中。在這里,我已經選擇取調用這個實現類 XMLRPCInvocationHandler。InvocationHandler接口定義單一的方法,如代碼4所示。
代碼4 InvocationHandler
public interface InvocationHandler {
?? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
當一個proxy實例調用了一個方法,proxy實例傳遞這個方法和他的參數叨處理器類的invoke()方法,同時要識別它。讓我們看看處理器的實現:
代碼5 InvocationHandler
private static class XMLRPCInvocationHandler implements InvocationHandler {
?? private Class type;
?? public XMLRPCInvocationHandler(Class ifType) {
??????this.type = ifType;
?? }
?? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
??????XmlRpcClient client = getClient(); // Get a reference to the client
??????Vector paras = null; // This will hold the list of parameters
??????if (args != null){
???????? paras = new Vector();
???????? for (int i = 0; i < args.length; i++) {
?????????????? paras.add(ValueConverter.convertFromType(args[i]));
????????????}
???????? }
???????? else{
????????????paras = new Vector(); // The vector holding the parameters must not be null
???????? }
???????? Class retType = method.getReturnType();
???????? Object ret = client.execute(type.getName() + '.' + method.getName(), paras);
???????? return ValueConverter.convertToType(ret, retType);
?? }
}
在創建過程中,XMLRPCInvocationHandler就是有遠程接口的實現類。我們使用這個類僅僅是為了獲得遠程接口的名字和可供調用的方法名。可以觀察到遠程方法的調用總是動態的:我們既不需要在樁類里調用方法,也不需要從外部獲得任何接口的信息。
Client類從getClient()方法獲得:
代碼6. 獲得client類
protected static XmlRpcClient getClient() throws MalformedURLException {
?? return new XmlRpcClient("localhost", 8080);
}
這里,我們能使用Apache XML-RPC去獲得處理遠程調用的Client類??梢钥吹轿覀兎祷匾粋€Client類而沒有考慮我們所有調用的方法的接口。
更重要的代碼是在ValueConverter類中用靜態方法調用。在那個方法里反射提供了這種方式的魅力所在。在下面部分我們分析這些代碼:
XML-RPC 和Java之間的轉換這部分解釋XML-RPC架構的核心。這個框架需要作兩件事:轉換Java對對象成能被XML-RPC所理解的數據結構和一個反向的轉換處理。
我開始展示如何把一個Java對象轉換為能被XML-RPC理解的數據結構:
代碼7. Java 到 XML-RPC轉換
public static Object convertFromType(Object obj) throws IllegalArgumentException,
??????IllegalAccessException, InvocationTargetException, IntrospectionException {
?? if (obj == null) {
??????return null;
?? }
?? Class type = obj.getClass();
?? if (type.equals(Integer.class)
??????|| type.equals(Double.class)
??????|| type.equals(Boolean.class)
??????|| type.equals(String.class)
??????|| type.equals(Date.class)) {
??????return obj;
?? else if (type.isArray() && type.getComponentType().equals(byte.class)) {
??????return obj;
?? }
?? else if (type.isArray()) {
??????int length = Array.getLength(obj);
??????Vector res = new Vector();
??????for (int i = 0; i < length; i++) {
???????? res.add(convertFromType(Array.get(obj, i)));
??????}
??????return res;
?? }
?? else {
??????Hashtable res = new Hashtable();
??????BeanInfo info = Introspector.getBeanInfo(type, Object.class);
??????PropertyDescriptor[] props = info.getPropertyDescriptors();
??????for (int i = 0; i < props.length; i++) {
???????? String propName = props[i].getName();
???????? Object value = null;
???????? value = convertFromType(props[i].getReadMethod().invoke(obj, null));
???????? if (value != null) res.put(propName, value);
??????}
??????return res;
?? }
}
轉換Java對對象成能被XML-RPC所理解的數據結構,需要考慮如上面代碼展示的5種情況:
1. Null:如果要轉換的對象是Null值,我們必須返回一個Null。
2. 原始類型:如果要轉換的對象是原始類型(或這些原始類的包裝類)-- int、double、Boolean、string或date, 那么就返回該對象即可。
3. base64:如果對象是一個字節數組,那么可以認為這個數組是base64類型數據。只需把數組直接返回即可。
4. Array:如果對象不是一個字節數組,我們能從Java Reflection包中使用Array工具類獲得數組長度。使用這個長度值,在一個循環中讀數組的每個數據單元。把每個數據單元的數據傳入ValueConverter封裝進入一個Vector中。
5.復雜類型:如果對象不是上面所敘述的類型,我們可以假定它是一個JavaBean – 在程序開始我們可以設立這么一個共同遵守的假定原則。我們把屬性插入HashTable內。要訪問這些屬性,可以使用JavaBean框架自舉的機制:使用工具類Introspector去獲得封裝在BeanInfo對象里的信息。特別,我們可以循環訪問PropertyDescriptor對象數組獲得 Bean的屬性。從這樣一個屬性描述器里,可以檢索到屬性的名字,這些名字也是訪問HashTable的鍵。我們通過讀屬性描述器獲得這些鍵值,例如屬性值。
看到如此容易地使用JavaBean框架從Bean里提取信息。我不需要知道所想轉換的類型,只需知道這個Bean即可。這個假設是我們的框架完美地運行其倆的先決條件。
現在,讓我們來看看相反的轉換 – 把XML-RPC結構數據轉換為Java對象:
代碼8. 從XML-RPC轉換到Java對象
public static Object convertToType(Object object, Class type) throws IllegalArgumentException,
??????IllegalAccessException, InvocationTargetException, IntrospectionException, InstantiationException {
?? if (type.equals(int.class)
??????|| type.equals(double.class)
??????|| type.equals(boolean.class)
??????|| type.equals(String.class)
??????|| type.equals(Date.class)) {
??????return object;
?? }
?? else if (type.isArray() && type.getComponentType().equals(byte.class)) {
??????return object;
?? }
?? else if (type.isArray()) {
??????int length = ((Vector) object).size();
??????Class compType = type.getComponentType();
??????Object res = Array.newInstance(compType, length);
??????for (int i = 0; i < length; i++) {
???????? Object value = ((Vector) object).get(i);
???????? Array.set(res, i, convertToType(value, compType));
??????}
??????return res;
?? }
?? else {
??????Object res = type.newInstance();
??????BeanInfo info = Introspector.getBeanInfo(type, Object.class);
??????PropertyDescriptor[] props = info.getPropertyDescriptors();
??????for (int i = 0; i < props.length; i++) {
???????? String propName = props[i].getName();
???????? if (((Hashtable) object).containsKey(propName)) {
????????????Class propType = props[i].getPropertyType();
????????????props[i].getWriteMethod().
?????????????? invoke(res, new Object[]
??????????????????{ convertToType(((Hashtable) object).get(propName), propType)});
???????? }
??????}
??????return res;
?? }
}
轉換成一個Java類型需要更多的了解這個我們所要轉換的值,我們也必須了解那個值要轉換到這個Java類型。這個解釋了在代碼8中 convertToType()方法的第二個參數的存在性。知道了類型,我們使用Java的自舉機制,把XML-RPC數據類型轉換成Java類型。下面的列表展示了完成多種轉換的約定:
1.Null:XML-RPC不傳遞空值,這個限制在稍后又更詳細說明。我們不需要考慮這種情況。
2.原始類型:如果對象是原始類型(或原始類型的包裝類)- int, double, Boolean, string, 或 date ,那么我們可以把對象本社返回,作為XML-RPC可以識別的原始類型。
3.base64:如果對象是一個二進制數組,可以認為代表一個base64類型的實例。我們可以再次把數組本書返回。
4.數組:如果對象是一個數組,但不是一個二進制數組,我們首先要在數組類確定所存項目的類型。我們可以根據對象斷定類型??梢允褂?getComponentType()。下一步,我們使用Array工具類基于給定的組件類型創建一個新的數組。我們可以使用Array工具類循環遍歷數組,設置各個域,使用ValueConveter從每個數組項中獲得正確的值。在XML-RPC框架中,可以發現我們所期望的數組數據結構是一個 Vector。
5.復雜類型:如果一個對象不是上面所述的類型,我們假設它是一個JavaBean(依照我們的基本約定)。再次,我們使用 Introspector查找Bean的屬性描述器,使用屬性描述器設置實際的屬性通過訪問write()方法。需要注意的是,框架給了我們一個儲存在 HashTable的屬性。當然,屬性類型可能是復雜類型,我們必須使用ValueConverter去獲得正確的Java對象。
理解了數據轉換的約定,我們可以看看處理服務是如何實現的。
實現服務處理已經解釋了一個遠程服務是如何被調用和在XML-RPC和Java對象之間轉換時包含什么。我了解了所迷惑不解的問題的剩下部分:在一個服務端如何處理請求
這里是一個為這篇文章所實現簡單的服務器實現代碼:
代碼9. 服務器
public class Server {
?? private WebServer webserver = null;
?? public void start() {
??????webserver = new WebServer(8080);
??????webserver.addHandler
??????????(PersonHome.class.getName(),
???????? new Handler(PersonHome.class,
???????? new PersonHomeImpl()));
??????webserver.setParanoid(false);
??????webserver.start();
?? }
?? public void stop() {
???????? webserver.shutdown();
???????? webserver = null;
?? }
?? private static class Handler implements XmlRpcHandler {
??????private Object instance;
??????private Class type;
??????public Handler(Class ifType, Object impl) {
???????? if (!ifType.isInterface()) {
????????????throw new AssertionError("Type must be an interface");
???????? }
???????? if (!ifType.isAssignableFrom(impl.getClass())) {
????????????throw new AssertionError("Handler must implement interface");
???????? }
???????? this.type = ifType;
???????? this.instance = impl;
??????}
??????public Object execute(String method, Vector arguments) throws Exception {
???????? String mName = method.substring(method.lastIndexOf('.') + 1);
???????? Method[] methods = type.getMethods();
???????? for (int i = 0; i < methods.length; i++) {
????????????if (methods[i].getName().equals(mName)){
?????????????? try {
??????????????????Object[] args = new Object[arguments.size()];
??????????????????for (int j = 0; j < args.length; j++) {
???????????????????? args[j] = ValueConverter.convertToType
???????????????????????? (arguments.get(j), methods[i].getParameterTypes()[j]);
??????????????????}
??????????????????return ValueConverter.convertFromType(methods[i].invoke(instance,args));
?????????????? }
?????????????? catch (Exception e) {
??????????????????if (e.getCause() instanceof XmlRpcException){
???????????????????? throw (XmlRpcException)e.getCause();
??????????????????}
??????????????????else{
???????????????????? throw new XmlRpcException(-1, e.getMessage());
??????????????????}
?????????????? }
????????????}
???????? }
???????? throw new NoSuchMethodException(mName);
??????}
?? }
?? public static void main(String[] args){
??????Server server = new Server();
??????System.out.println("Starting server...");
??????server.start();
??????try {
???????? Thread.sleep(30000);
??????}
??????catch (InterruptedException e) {
???????? e.printStackTrace();??????
??????}
??????System.out.println("Stopping server...");
??????server.stop();
?? }
}
關鍵類是ApacheXML-RPC包中的WebServer類。黑體字代碼展示了我們主要的需求:我們必須注冊一個服務句柄。這個句柄經由 XmlRpcHandler接口定義,這個接口就像代理機制中InvocationHandler接口,有個方法對應方法調用的委派。在這里,叫做 execute()方法,有著和InvocationHandler接口相同的實現精髓。最大的不同是,我們須注冊聯系接口和他的實現的一個句柄,不需要提供服務接口的實現(一樁程序形式)。然而,在服務器里,我們需要定義那塊代碼負責處理到來的請求。最后,可以看到,通過循環遍歷接口方法調用服務方法,我們使用普通的方式去發現了爭取的方法。這里,我們不依靠標準的自舉JavaBean,因為服務方法不是僅僅有setter和getter方法。
后記在這個部分,主要討論在先前討論中所處想的以下問題。我看到XML-RPC協議和這個文章所述的框架的局限性,但我也考慮這些方式的一定先進性。
局限性XML-RPC是一個簡單協議,很明顯它不能為代表面向對象系統特色的遠程過程調用實現可編程API。特別地,這樣一個API不支持以下的一些實現:
•繼承:XML-RPC沒能攜帶充足的信息決定那種類型可以沿著繼承的層級結構傳遞。在遠程過程調用和對象傳遞參數中都存在這種情況。因此,申明所有的類為final類型是一個好的編程習慣
& #8226;重載:XML-RPC不允許方法重載。依據這條規則,可以重載那些有原始類型聲明的方法,但實際這個選擇是不能滿足的。當我們需要從方法的聲明去推斷結構類型是,我們不允許重載。我僅僅允許同一個方法有不同參數個數這種情況除向,因為所有的方法在遠程過程調用期間是可用的。我么有以這個方式實現,而是使用了不同的方法名。注意:Web服務在這方面也不提供更多的靈活性。即使靈活性個那個靠的框架Axis也對重載有限制。
• 集合:XML-RPC不允許結合類型出現。和重載相同的原因,我們必須從被給定的集合類型推斷集合中項目的類型,這是不可能的。(JDK1.5之前版本)。取而代之,我們使用數組,可以查詢組件類型。雖然,Web服務在遠程方法調用方面比XML-RPC更強大,但更多的意見是反對使用集合類型。參看 “Web Services Programming Tips and Tricks: Use Collection Types with SOAP and JAX-RPC” Russell Butek and Richard Scheuerle, Jr(2002.4)
& #8226;Null值:XML-RPC不支持空值。這可能是這個協議中最令人尷尬的瑕疵,因為這個意味著在數組里不能保存空值。在XML-RPC有過關于Null值得提議,但大多數的實現不支持Null值。無需去說,如果通信連接的兩邊均是與Java程序交互,通過手動方式在消息里加入一些元數據可以克服這個缺點。而且,這意味著濫用協議,不是一個好的建議。
序列化控制序列化在下面的場景中出現。特別地,文中所提議的框架在發現屬性去自動序列化。有時,你可以在序列化中阻止一些屬性值得傳遞。
設想一個Person對象引用多個不同類型的Address對象。特別地,這些Address對象之一是郵件地址,而其他對象在其他的上下文環境中有意義。你可能希望通過Person類的Person.getMailingAddress()方法可以獲得郵件地址,這樣增強你的Person類。標準的自舉機制看到的是一個新的屬性-mailingAddress,這個屬性在序列化的時候可使用眾多的地址列表中初始化。在這種情況下,一個對應的 Person.setMailingAddress()方法將執行這樣的操作,不管地址序列化的順序如何,反序列化將返回一個對應的Address類。當然,你的方法應該如何序列化是無關竟要的,但即使你寫的方法是正確的,在其程序接口編程的有些人可能不清楚你想的是什么,增加了發生問題的可能性。在任何情況下,你應該容忍兩次序列化郵件地址。
但是,這里有個幫助,Introspector可能告訴你,要查找一個類的屬性時要去使用反射,而要使用給定的信息。這些信息可以在BeanInfo類里找到,如果你的類名是MyClass,那么你的BeanInfo類應該叫做 MyClassBeanInfo。BeanInfo類因該在MyClass類的相同包里,或者在BeanInfo的搜索路徑里。搜索路徑可以在 Introspector里設置。作為一個BeanInfo類,應該提供如下的屬性:
代碼10. BeanInfo 例子 1
public class MyClassBeanInfo extends SimpleBeanInfo {
?? public PropertyDescriptor[] getPropertyDescriptors() {
??????try {
???????? BeanInfo superInfo = Introspector.getBeanInfo(MyClass.class.getSuperclass());
???????? List list = new ArrayList();
???????? for (int i = 0; i < superInfo.getPropertyDescriptors().length; i++) {
????????????list.add(superInfo.getPropertyDescriptors()[i]);
???????? }
???????? //
???????? list.add(new PropertyDescriptor("myProperty", MyClass.class));
???????? //
???????? return (PropertyDescriptor[])list.toArray(new PropertyDescriptor[list.size()]);
??????} catch (IntrospectionException e) {
???????? return null;
??????}
?? }
}
getPropertyDescriptors()方法必須返回屬性描述器所代表的屬性。首先,在你的超類里增加這個屬性,增加這個你希望發布的這個屬性到你的類里,如粗體部分顯示。
這是一個嚴重的缺陷:上面的提議包含了很多固定代碼,而這些是編程的時候應盡量避免的。正好,增加的這些被序列化的屬性是比羅列顯示這些屬性能更好的運作。當然,一個方式是使用Introspector通過反射機制調用Introspector.getBeanInfo(MyClass.class, Introspector.IGNORE_ALL_BEANINFO)獲得所有的屬性。你能增加一個過濾器在你的返回結果時。這種方式看起來象如下展示:
代碼11. BeanInfo例子 2
public class MyClassBeanInfo extends SimpleBeanInfo {
?? public PropertyDescriptor[] getPropertyDescriptors() {
??????try {
????????????BeanInfo infoByReflection = Introspector.getBeanInfo(MyClass.class,
????????????Introspector.IGNORE_ALL_BEANINFO); PropetyDescriptor allProperies =
????????????infoByReflection.getPropertyDescriptors();
????????????return filter(allProperies);
??????} catch (IntrospectionException e) {
???????? return null;
??????}
?? }
?? protected PropertyDescriptor[] filter(PropertyDescriptor[] props){
??????// Remove properties which must not be exposed
?? }
}
一個好的方法是使用接口定義語言(IDL)構造一個框架,這樣允許你手動去產生Bean和擴阿占屬性和方法。這個產生器負責提供通過IDL過濾屬性的BeanInfo類。繼續看一個這樣實現的例子。
增加值當我們隱藏了實際的傳送機制,很容易在收到和發送的消息中增加信息。假若我們需要在每個遠程方法調用中傳送Session信息。這個信息就可以在調用者那里增加上,處理者把它作為第一個參數(包裝所有必需的信息成一個適當德Bean)。在其他的調用里,這些信息能從參數Vector里刪除,在方法調用里分別處理。在文后的資源引用中找到更多的可用代碼,可以更好的使用這個框架。
其他語言如果你正視弱點,它可能變成支點。XML-RPC的簡易導致了上面所描述的限制。然而,XML-RPC已有了多種語言的實現,例如Ruby,Python,或函數性怨言 Haskell。不是所有的語言支持面向對象系統中所支持的繼承,不是所有的語言支持重載。有些語言,例如Haskell,有靈活的列表類型,從Java 語言的角度看,它的這種類型介于數組和列表之間。因此,XML-RPC內在的限制使它適宜于跨語言通信。
當選擇XML-RPC作為跨越 Java和其他語言的橋梁時,你仍可以使用這個框架,但你僅僅能在與Java通信的一側使用。而且,可以擴展這個框架去覆蓋其他語言。例如,你可以用其他語言重寫這個框架,增加對Java接口和數據對象與其他語言對應對象之間轉換的支持。另外的方式,我已在上面暗示過,就是寫一個編譯器把IDL的適當形式轉換到其他多種語言的形式,Java是其中之一。在下面,我給出一個例子。
無需任何訴說,這種方式擴展文中所提的框架將是比框架更棘手的事,但它們將協同運行。
刪除或替換XML-RPC實現一個有效率的系統更愿意避免使用XML-RPC中間框架,反而通過把XML-RPC的XML數據直接轉換成適當的對象。你可能認為,在潛藏在后面的接口里的抽象方法調用能用多種XML-RPC實現。當我認為不需去做什么事時,那就不能實現這些功能。再者,你是被吸引,而去適應這個框架,以滿足你的需要。
遠程方法調用伴隨著J2SE1.5,RMI將使用代理機制。將不再需要使用RMI編譯器產生樁類(除非你要與一些舊的系統協作)。因此,如果不能夾在一個樁類,那么遠程對象的樁就被認為是一個java.lang.reflect.Proxy實例。
接口定義語言去處要查看大兩Bean實現規約和XML-RPC限制的麻煩,正如上面所述,就是避免寫接口和Bean。取而代之,適用適當的IDL去創建它們。這樣的語言看起來就像下面的代碼:
代碼12 . IDL
module partner;
exception NoPartnerException < 123 : "No partner found" >;
struct Partner {
?? int id;
?? string name;
?? int age;
?? date birthday;
};
interface PartnerHome {
?? Partner getPartner(int id) throws NoPartnerException;
?? Partner[] findPartner(string name, date bday) throws NoPartnerException;
};
基于IDL編寫一個解析器和代碼產生器,使得交叉語言通信更加簡易。
總結在這片文章里,展示了如何使用Java反射機制透明地包裝經由XML-RPC實現遠程方法調用。已經重點展示了已經整合在Proxy類,Array類和 Introspector類中的實現機制。基于這些工具類,一個可適用于多種用途的遠程方法調用的中間件框架已經實構造出來。
關于作者Stephan Maier 擁有數學博士學位,超過五年的軟件開發經驗。他也是一位職業生涯中藝術級的導師。除了編程,他喜歡唱歌和運動?,F在,他在編寫一個編譯器,這個編譯器可以簡單地轉換IDL定義到其他適當的數據結構和其他語言的遠程接口,例如Java,Ruby,或Python,在這些接口里潛在的調用協議都是XML-RPC。
參考資料Matrix:
http://www.matrix.org.cnJavaworld:
http://www.Javaworld.com"Web Services Programming Tips and Tricks: Roundtrip Issues in Java Coding Conventions," Russell Butek, Richard Scheuerle, Jr. (developerWorks, April 2004):
http://www-106.ibm.com/developerworks/xml/library/ws-tip-roundtrip2.html
"XML-RPC in Java Programming," Roy Miller (developerWorks, January 2004):
http://www-106.ibm.com/developerworks/library/j-xmlrpc.html
"Web Services Programming Tips and Tricks: Use Collection Types with SOAP and JAX-RPC" by Andre Tost and Tony Cowan (developerWorks, May 2004):
http://www-106.ibm.com/developerworks/library/ws-tip-coding.html?ca=dnx-420
"A Note on Distributed Computing," Jim Waldo, Geoff Wyant, Ann Wollrath, and Sam Kendall (Sun 1994):
http://research.sun.com/techrep/1994/smli_tr-94-29.pdf
XML-RPC homepage:
http://www.xmlrpc.com
Apache XML-RPC implementation:
http://ws.apache.org/xmlrpc