动态回调基于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不支持以下的一些实现:
&#8226;继承:XML-RPC没能携带充足的信息决定那种类型可以沿着继承的层级结构传递。在远程过程调用和对象传递参数中都存在这种情况。因此,申明所有的类为final类型是一个好的编程习惯
& #8226;重载:XML-RPC不允许方法重载。依据这条规则,可以重载那些有原始类型声明的方法,但实际这个选择是不能满足的。当我们需要从方法的声明去推断结构类型是,我们不允许重载。我仅仅允许同一个方法有不同参数个数这种情况除向,因为所有的方法在远程过程调用期间是可用的。我么有以这个方式实现,而是使用了不同的方法名。注意:Web服务在这方面也不提供更多的灵活性。即使灵活性个那个靠的框架Axis也对重载有限制。
&#8226; 集合: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 拥有数学博士学位,超过五年的软件开发经验。他也是一位职业生涯中艺术级的导师。除了编程,他喜欢唱歌和运动。现在,他在编写一个编译器,这个编译器可以简单地转换IDL定义到其他适当的数据结构和其他语言的远程接口,例如Java,Ruby,或Python,在这些接口里潜在的调用协议都是XML-RPC。

参考资料
Matrix:http://www.matrix.org.cn
Javaworld: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