频道直达 - 专题 - 新闻 - 技巧 - 组网 - 开发 - 安全 - web编程 - 图像 - 操作系统 - 数据库 - 教育 - 旅游 - 健康 - 时尚 - 驱动 - 软件 - 游戏 - 多媒体 - ERP - 讨论组

SOAP净化有线协议(四):简化客户程序 

来源: 作者: 出处:巧巧读书 2006-10-01 进入讨论组

  Java 2平台1.3版本为Java映像API(Reflection API)增加了一个极其实用的扩展:动态代理类。一个动态代理类就是一个实现了一系列运行时指定的接口的类。这个代理可以象它真正实现了这些接口一样使用。
换句话说,可以直接在代理对象上调用任意接口的任意方法——当然,必须先进行必要的类型定型(casting)。由此,我们可以用动态代理类为一组接口创建一个类型安全的代理对象,且不必象使用编译时工具一样预先生成代理(有关动态代理类更详细的说明,请参见本文最后的参考资源)。

接下来我将介绍一个以动态代理类为基础的框架,这个框架使得SOAP(简单对象访问协议)客户程序的创建更加简单和直观。SOAP是一种用XML编码数据的有线协议。在本系列文章的第二篇、第三篇构造SOAP服务的过程中,我们发现客户程序的开发者必须多做许多原来不必做的工作。为帮助回忆,你可以看一下第二篇文章中的SOAP服务代码,看看和客户程序代码相比较时,服务程序的SOAP代码是多么微不足道。本系列文章前几篇所创建的简单SOAP服务显示出,基于SOAP的服务只包含无论用不用SOAP都必须提供的代码。服务程序的开发者要编写的额外代码很少,而客户程序开发者却有许多额外工作要做。本文介绍的类将把这些额外工作减到最少。

一、介绍SOAP代理类
首先,我要给出如果客户程序使用了本文创建的框架,它将变成什么样子:

package hello;
import soapproxy.*;
public class Client
{
public static void main(String[] args)
{
try
{
Class[] interfaces = new Class[] {hello.Hello.class};
Hello hello = (Hello)(Proxy.newInstance("urn:Hello",interfaces));
// 调用sayHelloTo方法
// 这个sayHelloTo方法需要一个字符串参数
System.out.println(hello.sayHelloTo("John"));
// 调用sayHelloTo方法
// 这个sayHelloTo方法需要一个Name JavaBean参数
Name theName = new Name();
theName.setName("Mala");
System.out.println(hello.sayHelloTo(theName));
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

也许是出于我的个人爱好,我认为上面的客户代码比第二篇和第三篇文章中的客户代码更好。如果你现在不能理解上面的代码,这很正常,但我想待到本文结束时你会理解的。

要理解客户程序的代码,你必须深入了解SOAP Proxy类,它在soapproxy包内,可以在Proxy.java内找到(参见本文最后的参考资源)。Proxy类有一个私有的构造函数,它意味着Proxy实例不能从Proxy之外创建;新建Proxy实例的唯一方法是通过静态的newInstance()方法。newInstance()方法有两个参数:SOAP服务的对象ID,以及一个数组,数组中包含一组该代理要实现的接口的名字。对象ID很简单,但这些接口名字是什么?从哪里去得到这些接口的名字?SOAP服务的开发者直接把服务上所有可被客户程序调用的方法堆在一起得到一个接口。相当简单,不是吗?

现在我们为HelloWorld服务定义一个接口。第二篇文章中,这个服务的最终版本有sayHelloTo()方法的两个重载版本:一个版本的参数是一个字符串,另一个版本的参数是一个Name JavaBean。这两个方法就可以构成一个接口,称为Hello,如下所示:

package hello;
public interface Hello
{
public String sayHelloTo(String name);
public String sayHelloTo(Name name);
}

服务开发者决定要创建多少接口,以及为这些接口取什么样的名字。例如,你可以为HelloWorld服务创建两个接口,每一个接口包含一个方法。一般地,你应该避免创建方法数量大于七个的接口。另外,注意只把那些看来有必要放在一起的方法用一个接口组织起来。例如,如果HelloWorld服务还有一个返回定制的Good-Bye消息给调用者的sayByeTo()方法,设计两个独立的接口也许比较明智:一个接口用于sayHelloTo()方法,一个接口用于sayByeTo()方法。

现在我们有了定义HelloWorld服务和客户程序之间契约的接口,下面返回来看newInstance()方法。如前所述,newInstance()方法创建Proxy类的一个新实例。newInstance()方法可以创建新实例是因为它属于Proxy类,能够访问私有的构造函数。newInstance()方法为新创建的实例调用initialize()方法。initialize()值得关注,因为动态代理就是在这里创建和返回。initialize()的代码如下所示:

private Object initialize(Class[] interfaces)
{
return(java.lang.reflect.Proxy.newProxyInstance(getClass().getClassLoader()
,interfaces,this));
}

注意newProxyInstance()方法的应用。创建动态代理类实例的唯一办法是调用该类(即java.lang.reflect.Proxy类)静态的newProxyInstance()方法。java.lang.reflect.Proxy类为创建动态代理类提供了静态方法,而且它还是所有由这些方法创建的动态代理类的超类。换句话说,它不仅是一个创建动态代理类的工厂,而且它本身也是一个动态代理类!因此,在我们的例子中,SOAP代理不是动态代理;相反,这个动态代理实际上是newProxyInstance静态方法返回的java.lang.reflect.Proxy类的一个实例。从本文后面可以看到,这个动态代理实际上通过SOAP代理实现的invoke()方法完成它的所有工作。那么,这个动态代理如何建立和SOAP代理的联系呢?因为有一个对SOAP代理的引用传递给了newProxyInstance()方法。也许现在这听起来有点费解,但只要你分析一下invoke()方法,这一切就很明白了。

java.lang.reflect.Proxy类构造函数的第一个参数是一个类装载器实例,第二个参数是需要动态实现的接口的数组(它就是客户程序传递给newInstance()的数组),第三个参数是一个实现了java.lang.reflect.InvocationHandler接口的类的实例。因为SOAP Proxy类实现了InvocationHandler接口,所以第三个参数是代理实例本身(即this)。InvocationHandler接口有一个方法invoke()。当动态代理的动态实现的接口被调用时,Java运行时环境调用invoke()方法。因此,举例来说,当客户程序调用动态代理的Hello接口的sayHelloTo()方法时,Java运行时环境将调用SOAP代理的invoke()方法。

你可能已经发现,SOAP代理的newInstance()方法不返回SOAP代理的实例;相反,它返回newInsance()刚刚创建的动态代理,而动态代理动态地实现客户程序传入的接口数组。客户程序可以将这个返回的动态代理定型为传入newInstance()的任意接口类型,在动态代理上调用接口所定义的各个方法,就象动态代理真地实现了那些接口一样。

.
.
try
{
Class[] interfaces = new Class[] {hello.Hello.class};
Hello hello = (Hello)(Proxy.newInstance("urn:Hello",interfaces));
// 调用参数为字符串的sayHelloTo方法
System.out.println(hello.sayHelloTo("John"));
// 调用参数为Name JavaBean的sayHelloTo方法
Name theName = new Name();
theName.setName("Mala");
System.out.println(hello.sayHelloTo(theName));
}
.
.

在上面的代码中,invoke()方法将被调用两次,每次调用sayHelloTo()方法时执行一次。现在我们来看看invoke()方法。简而言之,invoke()方法的工作正是第二篇文章中每一个客户程序必须手工完成的工作,其中包括:用合适的调用参数设置一个Call对象,定制的调用参数所需要的类型映射。由于SOAP代理中的invoke()方法担负了所有这些任务,客户程序释放了这份负担。

在invoke()方法接收到的三个参数中,我们只对后面两个感兴趣。第二个参数,即Method对象,给出了被调用方法的名字。记住,被调用方法的名字对应着一个SOAP服务导出的已知方法。服务的对象ID作为参数传递给newInstance()方法,所以invoke()方法已经拥有该对象ID。invoke()方法利用这些信息,按照如下方式设置Call对象:

Call call = new Call();
call.setTargetObjectURI(urn);
call.setMethodName(m.getName());
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);

现在要做的是为远程服务调用设置参数。为此,我们要用到invoke()方法的第三个参数:传入动态代理上被调用方法的一个参数数组。数组中索引为0的参数是方法调用中最左边的参数,索引为1的参数是方法的第二个参数,依此类推。举例来说,如果客户程序调用了sayHelloTo(String name)方法,那么参数数组就是包含一个字符串的数组。invoke()方法处理该数组的每一个元素,创建一个由Parameter对象构成的向量(Vector)(正如第二篇文章中客户程序所做的那样):

java.util.Vector params = new java.util.Vector();
for( int i=0; i<args.length; i++ )
{
if( isSimple(args[i]) || isSimpleArray(args[i]) )
{
params.add(new Parameter(_paramName+(i+1),
args[i].getClass(),args[i],null));
}
else if( isVector(args[i]) )
{
add请保留地址 http://www.qqread.com/xml-soap/f227086.html 更多文章 更多内容请看有线与无线组网专题TCP/IP协议SIP协议---NGN网络的核心协议专题,或进入讨论组讨论。
收藏此文】【 】【打印】【关闭
相关图文阅读
频道图文推荐
健 康 咨 询
时 尚 咨 询
巧巧读书宗旨
相关专题
讨论组问题推荐
站内各频道最新更新文档
站内最新制作专题
热门关键字导读
Photoshop教 程照片处理 照片制作 PS快捷键 抠图
计 算 机 故 障XP系统修复
艺 术 与 设 计设计 流媒体 设计欣赏 边框
计 算 机 安 全ARP
站内频道文章精选