一、Axis简介


1. 什么是SOAP

SOAP是一个基于XML的用于应用程序之间通信数据编码的传输协议。最初由微软和Userland Software提出,随着不断地完善和改进,SOAP很快被业界广泛应用,目前完全发布版本是1.1。在其发展过程中,W3C XML标准工作小组积极促成SOAP成为一个真正的开放标准。在写作此文档之时,SOAP1.2草案已经发布,1.2对1.1中相对混乱的部分做了改进。
SOAP被广泛作为新一代跨平台、跨语言分布计算Web Services的重要部分。


2. 什么是Axis

Axis是Apache组织推出的SOAP引擎,Axis项目是Apache组织著名的SOAP项目的后继项目,目前最新版本是采用Java开发的1.1版本,C++的版本正在开发之中。Axis v1.1软件包可以从http://ws.apache.org/axis/dist/1_1/下载得到。
但是Axis不仅仅是一个SOAP引擎,它还包括:
    一个独立运行的SOAP服务器
    一个servlet引擎的插件,这个servlet引擎可以是Tomcat
    对WSDL的扩展支持
    一个将WSDL的描述生成JAVA类的工具
    一些示例代码
    还有一个监控TCP/IP包的工具
 
二、Axis的安装

应用Axis开发Web Services,你需要安装如下软件:
1.JDK1.4.2
2.一个支持Servlet的服务器引擎,比如广为人知的Tomcat。

当安装好Tomcat之后,只需将下载的Axis软件包解压缩,将其中的“webapps”目录下的“axis”目录整个拷贝到Tomcat安装目录下的“webapps”目录下即可。

三、Axis的配置

Axis基于Java开发,可以部署于多种操作系统,使用前需要配置一系列的系统变量,在此假定你已经在本机上装好了Tomcat 4.0以上的版本,需要配置的系统变量如下表所示:

CATALINA_HOME
C:\Tomcat_4_1

(此处应为Tomcat的安装位置,注意路径名中不要有空格)

AXIS_HOME
%CATALINA_HOME%\webapps\axis

AXIS_LIB
%AXIS_HOME%\lib

AXISCLASSPATH
%AXIS_LIB%\axis.jar;%AXIS_LIB%\commons-discovery.jar; %AXIS_LIB%\commons-logging.jar;%AXIS_LIB%\jaxrpc.jar; %AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar; %AXIS_LIB%\xml-apis.jar;%AXIS_LIB%\xercesImpl.jar

CLASSPATH 中加入:
%AXIS_LIB%\axis.jar;%AXIS_LIB%\commons-discovery.jar; %AXIS_LIB%\commons-logging.jar;%AXIS_LIB%\jaxrpc.jar; %AXIS_LIB%\saaj.jar;%AXIS_LIB%\log4j-1.2.8.jar; %AXIS_LIB%\xml-apis.jar;%AXIS_LIB%\xercesImpl.jar

四、Axis的测试

安装配置完毕后,应测试一下是否Axis可以正确运行了。

  启动Tomcat服务器,在浏览器中访问http://localhost:8080/axis/happyaxis.jsp,如果页面显示有错误,则需要回头检查一下相关配置是否正确,如果浏览页面能正确显示出系统组件、属性等参数配置信息,则表示安装成功。现在可以开始开发你的Web Services应用了。

五、服务的发布
Axis提供了两种服务发布方式,一种是即时发布(Instant Deployment),一种是定制发布(Custom Deployment)。

1. 使用即时发布 Java Web Service(JWS)

  对即时发布的支持是Axis的特色之一,使用即时发布使用户只需有提供服务的Java类的源代码,即可将其迅速发布成Web服务。每当用户调用这类服务的时候,Axis会自动进行编译,即使服务器重启了也不必对其做任何处理,使用非常简单快捷。

  使用即时发布首先需要一个实现服务功能的Java源文件,将其扩展名改为.jws(Java Web Service的缩写),然后将该文件放到“……\webapps\axis”目录下即可。
    在此给出一个从英里到公里的长度单位转换的服务,其源码如下:

文件Distance.jws

public class Distance
{
    public double convertMile2Kilometre( double mile )
    { 
 return mile * 1.609;  //实现英里到公里的距离转换
    }
}

将其放到“……\webapps\axis”目录,通过访问http://localhost:8080/axis/Distance.jws?wsdl可以看到这个服务的WSDL描述文件,这说明Distance服务被成功发布了。描述的WDSL代码如下:
<?xml version="1.0" encoding="UTF-8" ?>
- <wsdl:definitions targetNamespace="http://192.168.0.26:8080/axis/Distance.jws" xmlns="http://schemas.xmlsoap.org/wsdl/
   xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://192.168.0.26:8080/axis/Distance.jws" xmlns:intf="http://192.168.0.26:8080/axis/Distance.jws" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <wsdl:message name="convertMile2KilometreRequest">
  <wsdl:part name="mile" type="xsd:double" />
  </wsdl:message>
- <wsdl:message name="convertMile2KilometreResponse">
  <wsdl:part name="convertMile2KilometreReturn" type="xsd:double" />
  </wsdl:message>
- <wsdl:portType name="Distance">
- <wsdl:operation name="convertMile2Kilometre" parameterOrder="mile">
  <wsdl:input message="impl:convertMile2KilometreRequest" name="convertMile2KilometreRequest" />
  <wsdl:output message="impl:convertMile2KilometreResponse" name="convertMile2KilometreResponse" />
  </wsdl:operation>
  </wsdl:portType>
- <wsdl:binding name="DistanceSoapBinding" type="impl:Distance">
  <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
- <wsdl:operation name="convertMile2Kilometre">
  <wsdlsoap:operation soapAction="" />
- <wsdl:input name="convertMile2KilometreRequest">
  <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded" />
  </wsdl:input>
- <wsdl:output name="convertMile2KilometreResponse">
  <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://192.168.0.26:8080/axis/Distance.jws" use="encoded" />
  </wsdl:output>
  </wsdl:operation>
  </wsdl:binding>
- <wsdl:service name="DistanceService">
- <wsdl:port binding="impl:DistanceSoapBinding" name="Distance">
  <wsdlsoap:address location="http://192.168.0.26:8080/axis/Distance.jws" />
  </wsdl:port>
  </wsdl:service>
  </wsdl:definitions>

需要注意的是:JWS的web服务发布是一个很简单的Web服务发布方式,在页面中你不能使用包,而且由于代码是在运行期被编译的,所以在部署之后,你也很难找到错误所在。

2. 使用定制发布 Web Service Deployment Descriptor(WSDD)

  即时发布是一项令人激动的技术,它使Web服务的开发变得如此简单;然而即时发布并不总是最好的选择,比如有些应用系统是第三方提供的,我们没有购买源代码,只有.class文件,但我们又希望将这个应用系统的一些功能对外发布成Web服务,使其能够在更大范围内产生作用,这个时候即时发布技术就无能为力了。此外,即时发布技术并不灵活,无法进行更多的服务配置,这使得它并不能满足一些特定系统的需求。

  因此,Axis提供了另一种服务发布方式,这就是定制发布。

  在此给出一个从加仑到升的容积单位转换的服务,其源码如下:

  文件Capacity.java
    package samples.capacity;
    public class Capacity
    {
        public double convertGallon2Litre( double gallon )
 {
     return gallon * 4.546;//实现加仑到升的容积转换
 }//convertGallon2Litre()

    }/* Capacity */
 
    将其编译成.class文件,放置到“……\webapps\axis\samples\capacity”目录下,即可着手进行发布。
    定制发布不需要构建.jws文件,但必须创建服务发布描述文件deploy.wsdd(Web Service Deployment Descriptor的缩写),这个文件负责描述服务的名称、入口等信息,其内容如下:
    文件deploy.wsdd

    <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
 <service name="Capacity" provider="java:RPC">
 <parameter name="className" value="samples.capacity.Capacity"/>
 <parameter name="allowedMethods" value="*"/>
 </service>
    </deployment>

    在这里服务的提供者是“java:RPC”,它被内建在Axis中,而且指明了一个JAVA RPC服务,做这个处理的类是org.apache.axis.providers.java.RPCProvider。
    我们是通过一个<parameter>标签告诉RPC服务应该调用的类,而另外一个<parameter>标签则告诉引擎,它可以调用这个类中的任何的Public方法。你也可以指定通过使用名字空间或者一些可以调用的方法列表,来指明那些方法可以被调用。

  将该文件也放到“……\webapps\axis\samples\capacity”目录下,然后可以采用Axis提供的一个客户端管理工具——AdminClient来完成服务的定制发布。
   在“……\webapps\axis\samples\capacity”目录下,运行:

   JAVA-cp %AXISCLASSPATH% org.apache.axis.client.AdminClient deploy.wsdd

   如果运行时说没有找到类,那么可能是类路径没有配置好,建议将所有的与axis相关的jar都写到classpath中去。这样只要运行:

   java org.apache.axis.client.AdminClient deploy.wsdd
   可以看到以下运行结果:

 Processing file deploy.wsdd
 <Admin>Doneprocessing</Admin>

  这表明Capacity服务定制发布完成。
 
   你也可以调用:
 java org.apache.axis.client.AdminClient undeploy.wsdd  来取消部署。

   你也可以调用:
 java org.apache.axis.client.AdminClient list 来获得所有的已经部署的服务的列表。在这里你会看到services, handlers, transports等等,注意这个调用只是列出了WEB-INF\server-config.wsdd的文件内容。

   一定要注意:编译后的class文件要拷贝到web-inf/classes的目录中,如果该文件中有包存在的话,别忘了还要在classes目录下创建包的目录
   通过访问http://localhost:8080/axis/services/Capacity?wsdl可以看到这个服务的WSDL描述文件,这说明Capacity服务被成功发布了。
   你也可以通过访问http://localhost:8080/axis/servlet/AxisServlet查看所有定制发布的服务。
   WDSL如下:
  <?xml version="1.0" encoding="UTF-8" ?>
- <wsdl:definitions targetNamespace="http://localhost:8080/axis/services/Capacity" xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://localhost:8080/axis/services/Capacity" xmlns:intf="http://localhost:8080/axis/services/Capacity" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <wsdl:message name="convertGallon2LitreResponse">
  <wsdl:part name="convertGallon2LitreReturn" type="xsd:double" />
  </wsdl:message>
- <wsdl:message name="convertGallon2LitreRequest">
  <wsdl:part name="in0" type="xsd:double" />
  </wsdl:message>
- <wsdl:portType name="Capacity">
- <wsdl:operation name="convertGallon2Litre" parameterOrder="in0">
  <wsdl:input message="impl:convertGallon2LitreRequest" name="convertGallon2LitreRequest" />
  <wsdl:output message="impl:convertGallon2LitreResponse" name="convertGallon2LitreResponse" />
  </wsdl:operation>
  </wsdl:portType>
- <wsdl:binding name="CapacitySoapBinding" type="impl:Capacity">
  <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
- <wsdl:operation name="convertGallon2Litre">
  <wsdlsoap:operation soapAction="" />
- <wsdl:input name="convertGallon2LitreRequest">
  <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://capacity.samples" use="encoded" />
  </wsdl:input>
- <wsdl:output name="convertGallon2LitreResponse">
  <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/services/Capacity" use="encoded" />
  </wsdl:output>
  </wsdl:operation>
  </wsdl:binding>
- <wsdl:service name="CapacityService">
- <wsdl:port binding="impl:CapacitySoapBinding" name="Capacity">
  <wsdlsoap:address location="http://localhost:8080/axis/services/Capacity" />
  </wsdl:port>
  </wsdl:service>
  </wsdl:definitions>

  WSDD 的高级功能:
  (1) AXIS支持三种对象范围:
     "request"范围:这是默认的情况,每一次Soap请求的时候都将创建新的对象。
     "application"范围:将会为所有的请求生成一个单独的共享的对象。
     "session"范围:为每一个session期的客户端请求创建一个对象。
  指定方法如下:
  <service name="MyService"...>
  <parameter name="scope" value="value"/>
  ...
  </service>
  (2) 更多部署内容:Handlers and Chains
  让我们来深入挖掘一下Axis引擎的更强大的特性。如果我们想跟踪我们的服务被调用了多少次,那么我们只需要包含一个简单的handler,它存放在 samples/log目录下。要使用这个handler,你就首先应该部署这个handler,然后使用在部署服务时给它指定的名字。下面是一个 wsdd文件的例子:
  <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
   <!-- define the logging handler configuration -->
    <handler name="track" type="java:samples.userguide.example4.LogHandler">
      <parameter name="filename" value="MyService.log"/>
    </handler>
 
   <!-- define the service, using the log handler we just defined -->
    <service name="LogTestService" provider="java:RPC">
     <requestFlow>
       <handler type="track"/>
     </requestFlow>
 
     <parameter name="className" value="samples.userguide.example4.Service"/>
     <parameter name="allowedMethods" value="*"/>
    </service>
  </deployment>

  第一段指明了一个名为track的handler,它是samples.userguide.example4.LogHandler类的实例。另外还指定了一个参数filename,用于指定日志文件名称。
  下面的那段指明了一个RPC服务,和我们在上面的例子中看到的一样,所不同的是在service标签中的<requestFlow>标签,它指定了一些在调用服务前应该被调用的handler集。也就是由于我们在部署中插入了这个"track"的引用,所以我们确定每次调用服务的时候都将被记录到日志文件中去。
 
  (3)远程管理:
  <service name="AdminService" provider="java:MSG">
 <parameter name="className" value="org.apache.axis.util.Admin"/>
 <parameter name="allowedMethods" value="*"/>
 <parameter name="enableRemoteAdmin" value="true"/>
  </service>
  WARNING: enabling remote administration may give unauthorized parties access to your machine. If you do this, please make sure to add security to your configuration!
  所以远程管理涉及到安全问题,不建议采用。

六、服务的访问

  GET方式的服务访问

  一般的SOAP消息都是采用POST方式实现传递,但也可以通过GET方式来访问。比如以下给出的一个服务——“HelloWorld”,其源码如下:

文件HelloWorld.jws
public class HelloWorld
{
    public String helloWorld()
    {
          System.out.println( "Hello World!" );//在服务器端打印输出 
          return "Hello World!";//返回相应字符串
    }
}

  这个服务给出一个名为“helloWorld”的无入口参数的操作,返回一个内容为“Hello World!的字符串”,同时在服务器端打印“Hello World!”,将该文件放到“……\webapps\axis”目录下,即可通过GET方法直接访问该服务,访问的地址为http://localhost:8080/axis/HelloWorld.jws?method=helloWorld,可以看到返回的SOAP信封消息,同时服务器端给出了相应的显示信息“Hello World!”这表明HelloWorld服务被成功访问了,生成的SOAP信封消息为:
 <?xml version="1.0" encoding="UTF-8" ?>
- <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <soapenv:Body>
- <helloWorldResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <helloWorldReturn xsi:type="xsd:string">Hello World!</helloWorldReturn>
  </helloWorldResponse>
  </soapenv:Body>
  </soapenv:Envelope>


七、客户端服务访问编程

  Axis提供了一套API来实现SOAP,从http://localhost:8080/axis/docs/apiDocs/index.html可以看到Axis的API文档。

  其中,org.apache.axis.client.Call和org.apache.axis.client.Service是两个比较常用的类,一般的客户端程序欲访问一个Web Service时,都要生成一个客户端的Service对象和Call对象,在访问服务之前,首先要对Call对象设置相应的参数,包括服务的位置、操作名、入口参数、返回值类型等,最后调用Call对象的invoke方法访问服务。

  以下给出了一个客户端访问Web服务的例程——AXISTest.java:

  文件AXISTest.java

package axisexercise;

import org.apache.axis.client.Call;

import org.apache.axis.client.Service;

import org.apache.axis.encoding.XMLType;

import javax.xml.rpc.ParameterMode;

public class AXISTest
{
    public static void main( String[] args ) throws Exception
    {
         创建service和call对象,这些对象是标准的JAX-RPC对象,这些对象用于存储服务调用的数据(metadata)。

 Service service = new Service();
 Call call = ( Call ) service.createCall();
 //////////访问即时发布的Distance服务

 //设置访问点
 call.setTargetEndpointAddress( "http://localhost:8080/axis/Distance.jws" );

 //设置操作名
 call.setOperationName( "convertMile2Kilometre" );

 //设置入口参数
 call.addParameter( "op1", XMLType.XSD_DOUBLE, ParameterMode.IN );

 //设置返回参数类型
 call.setReturnType( XMLType.XSD_DOUBLE );
 Double d1 = new Double( 190 );

 //调用服务,在invoke方法中传入的是包含调用参数的数组
 System.out.println( d1 + " 英里相当于 " +
 call.invoke( new Object[] {d1} ) + " 公里!" );

 //////////访问定制发布的Capacity服务
 call = ( Call ) service.createCall();

 //设置访问点
 call.setTargetEndpointAddress( "http://localhost:8080/axis/services/Capacity" );

 //设置操作名
 call.setOperationName( "convertGallon2Litre" );

 //设置入口参数
 call.addParameter( "op1", XMLType.XSD_DOUBLE, ParameterMode.IN );
 call.setReturnType( XMLType.XSD_DOUBLE );
 d1 = new Double( 10.00 );

 //调用服务
 System.out.println( d1 + " 加仑相当于 " +
 call.invoke( new Object[] {d1} ) + " 升!" );

    } //main()

}/* AXISTest */

编译运行后运行可以看到以下的结果:

190.0英里相当于305.71公里!

10.0加仑相当于45.46升!
 
  注意程序在访问即时发布的Distance服务和定制发布的Capacity服务时的不同,前者的服务访问点地址为http://localhost:8080/axis/HelloWorld.jws,而后者的则为http://localhost:8080/axis/services/Capacity


八、服务类型:RPC, Document, Wrapped, and Message
在Axis中,有四种服务类型:

1. RPC服务:
PRC服务是AXIS中的默认服务,当你通过<service ... provider="java:RPC"> or <service ... style="RPC">标签进行部署的时候,使用的就是RPC服务。RPC服务遵循SOAP RPC和其编码规范。AXIS可以将XML反序列化成java对象,并将其传给服务的方法。并且可以将服务的方法返回的JAVA对象序列化成XML。

2.Document / Wrapped services
Document services and wrapped services are similar in that neither uses the SOAP encoding for data; it's just plain old XML schema. In both cases, however, Axis still "binds" Java representations to the XML (see the databinding section for more), so you end up dealing with Java objects, not directly with XML constructs.

Document和Wrapped服务都不使用SOAP编码数据,这一点是他们相似的地方。他们仅仅使用旧的XML模式。然而,在这两种服务中,AXIS将捆绑Java的表示到XML文档中,所以你最终处理的是java对象而不直接处理XML。

下面是包含定单的一个简单的SOAP消息,你可以看到Document 和 Wrapped 服务的区别所在:
<soap:Envelope xmlns="http://xml.apache.org/axis/wsdd/"
   xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
  <soap:Body>
 <myNS:PurchaseOrder xmlns:myNS="http://commerce.com/PO">
   <item>SK001</item>
   <quantity>1</quantity>
   <description>Sushi Knife</description>
 </myNS:PurchaseOrder>
  </soap:Body>
</soap:Envelope>

相应的XML模式如下:

<schema targetNamespace="http://commerce.com/PO">
  <complexType name="POType">
 <sequence>
   <element name="item" type="xsd:string"/>
   <element name="quantity" type="xsd:int"/>
   <element name="description" type="xsd:string"/>
 </sequence>
  </complexType>
  <element name="PurchaseOrder" type="POType"/>
</deployment>

对于Document类型服务,他将映射成下面的方法:
public void method(PurchaseOrder po)


八、soap envolop(soap信封)
1. soap信封
在客户端发出服务请求以及服务端返回请求结果的时候,在网络中传输的是SOAP信封。首先客户端程序将请求参数及请求的方法序列到XML的文件中(SOAP信封),并将SOAP信封传送到服务器端。服务器端接受到SOAP信封后,将解析这个SOAP信封并反序列化调用参数及方法并将该方法的调用结果封装成SOAP信封(序列到XML的文件中)返回给客户端,客户端同样将SOAP信封中封装的返回结果反序列化为想要得到的结果。

我们来看下面这段客户端程序的SOAP信封:
(1) 客户端程序:
  import org.apache.axis.client.Call;
  import org.apache.axis.client.Service;
  import javax.xml.namespace.QName;
 
  public class TestClient
  {
     public static void main(String [] args)
     {
        try
        {
             String endpoint = "http://nagoya.apache.org:5049/axis/services/echo";
             Service  service = new Service();
             Call  call = (Call) service.createCall();
 
             call.setTargetEndpointAddress( new java.net.URL(endpoint) );
             call.setOperationName(new QName("http://soapinterop.org/", "echoString"));
 
             String ret = (String) call.invoke( new Object[] { "Hello!" } );
 
             System.out.println("Sent 'Hello!', got '" + ret + "'");
         }
  catch (Exception e)
  {
             System.err.println(e.toString());
         }
     }
  }

(2) SOAP信封:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SOAP-ENV:Body>
 <ns1:echoString xmlns:ns1="http://soapinterop.org/">
   <arg0 xsi:type="xsd:string">Hello!</arg0>
 </ns1:echoString>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

2. 参数命名:
在上面的代码中,AXIS自动将在Soap消息中的函数调用参数命名为arg0,arg1等等,如果你想按照自己定义的参数名调用方法的话,很简单,在你调用invoke函数之前只要调用addParameter()函数即可。如下所示:

   call.addParameter("testParam",
      org.apache.axis.Constants.XSD_STRING,
      javax.xml.rpc.ParameterMode.IN);
   call.setReturnType(org.apache.axis.Constants.XSD_STRING);
将testParam定义为调用函数的第一个参数(这里也只有一个参数),这里也可以同时定义该参数的类型以及该参数是输入、输出还是输入输出类型。在这里它是一个输入类型,现在当你运行程序,你将得到下面的消息:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SOAP-ENV:Body>
 <ns1:echoString xmlns:ns1="http://soapinterop.org/">
   <testParam xsi:type="xsd:string">Hello!</testParam>
 </ns1:echoString>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

3. 返回类型
在上面的代码中我们知道echoString函数将返回一个String对象,而且我们也希望通过客户端的调用能够返回预期的String对象。下面是一个典型的通过调用echoString函数后获得的Soap信封(消息)。
<?xml version="1.0" encoding="UTF-8"?>
  <SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SOAP-ENV:Body>
   <ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/">
 <result xsi:type="xsd:string">Hello!</result>
   </ns1:echoStringResponse>
  </SOAP-ENV:Body>
 </SOAP-ENV:Envelope>
我们可以看到这里已经声明了返回类型(<result xsi:type="xsd:string">)是一个String对象。这样Axis就可以将返回结果反序列化成我们想要的String对象了。
很多工具都会将这种确定的类型信息放到XML文件中,以生成消息的“自我描述部分”,另一方面,还有一些工具是象下面这样返回响应的:(Many toolkits put this kind of explicit typing information in the XML to make the message "self-describing". On the other hand, some toolkits return responses that look like this:)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <SOAP-ENV:Body>
  <ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/">
   <result>Hello, I'm a string!</result>
  </ns1:echoStringResponse>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
在这里并没有返回类型,那么我们怎么知道应该将返回结果反序列化成什么类型的结果呢?答案是metadata,在这种情况下,我们需要一个描述来指明我们期望的返回类型,下面这行代码说明了在客户端应该如何去做:
call.setReturnType( org.apache.axis.Constants.XSD_STRING );
这个方法将会告诉Axis客户端,如果返回的结果没有指明类型的话,那么Axis将会把返回类型指明为xsi:type属性所预定义的SOAP类型,在这里XSD_STRING属性所指明的是String类型。

所以也有这样一个相似的方法,允许你指定所期望返回的Java的类。
call.setReturnClass(String.class);