一、WSDL(web service definition lanuage)
首先我们知道在xml
文档中,targetName
相当于java
中的package
,而xmlns
相当于java
中的import
。
这里我们使用上一个例子(工程WS_Server03
)进行说明,首先使用地址http://localhost:8080/myService?wsdl
访问可以得到这样一份WebService
的实现文档:
<definitions
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://impl.ws.cxf.fkjava.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://impl.ws.cxf.fkjava.org/"
name="HelloWorldWs">
<import namespace="http://ws.cxf.fkjava.org/" location="http://localhost:8080/myService?wsdl=1" />
<binding xmlns:ns1="http://ws.cxf.fkjava.org/" name="HelloWorldWsPortBinding"
type="ns1:HelloWorld">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<operation name="sayHi">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="getCatsByUser">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="getAllCats">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="HelloWorldWs">
<port name="HelloWorldWsPort" binding="tns:HelloWorldWsPortBinding">
<soap:address location="http://localhost:8080/myService" />
</port>
</service>
</definitions>
说明:
- 1.
<definitions>
是所有WSDL
文档的根元素。其中包括三个子元素:<binding>
、<service>
、<import>
。 - 2.
<definitions>
中的targetNamespace="http://impl.ws.cxf.fkjava.org/" name="HelloWorldWs"
表示的是服务端的实现类是HelloWorlds.java
,而targetNamespace
相当于java
中的package
。 - 3.
<import>
子元素用来指定实现类的接口地址。 - 4.
<binding>
元素中有N个<operation>
子元素,详细定义了每个operation
(WebService
操作)。也就是接口中的所有方法,这里我们定义了三个方法:
String sayHi(String name);
List<Cat> getCatsByUser(User user);
@XmlJavaTypeAdapter(FkXmlAdapter.class) Map<String, Cat> getAllCats();
这里还使用SOAP
定义输入和输出,我们在后面讲。
- 5.
<service>
元素中包含<port>
子元素,说明WebService
绑定的地址。
如果我们使用接口地址http://localhost:8080/myService?wsdl=1
访问服务端则会得到下面WebService
接口的WSDL
文档:
<definitions xmlns:tns="http://ws.cxf.fkjava.org/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://ws.cxf.fkjava.org/">
<types>
<xsd:schema>
<xsd:import namespace="http://ws.cxf.fkjava.org/"
schemaLocation="http://localhost:8080/myService?xsd=1" />
</xsd:schema>
</types>
<message name="sayHi">
<part name="parameters" element="tns:sayHi" />
</message>
<message name="sayHiResponse">
<part name="parameters" element="tns:sayHiResponse" />
</message>
<message name="getCatsByUser">
<part name="parameters" element="tns:getCatsByUser" />
</message>
<message name="getCatsByUserResponse">
<part name="parameters" element="tns:getCatsByUserResponse" />
</message>
<message name="getAllCats">
<part name="parameters" element="tns:getAllCats" />
</message>
<message name="getAllCatsResponse">
<part name="parameters" element="tns:getAllCatsResponse" />
</message>
<portType name="HelloWorld">
<operation name="sayHi">
<input xmlns:ns1="http://www.w3.org/2007/05/addressing/metadata"
ns1:Action="http://ws.cxf.fkjava.org/HelloWorld/sayHiRequest"
message="tns:sayHi" />
<output xmlns:ns2="http://www.w3.org/2007/05/addressing/metadata"
ns2:Action="http://ws.cxf.fkjava.org/HelloWorld/sayHiResponse"
message="tns:sayHiResponse" />
</operation>
<operation name="getCatsByUser">
<input xmlns:ns3="http://www.w3.org/2007/05/addressing/metadata"
ns3:Action="http://ws.cxf.fkjava.org/HelloWorld/getCatsByUserRequest"
message="tns:getCatsByUser" />
<output xmlns:ns4="http://www.w3.org/2007/05/addressing/metadata"
ns4:Action="http://ws.cxf.fkjava.org/HelloWorld/getCatsByUserResponse"
message="tns:getCatsByUserResponse" />
</operation>
<operation name="getAllCats">
<input xmlns:ns5="http://www.w3.org/2007/05/addressing/metadata"
ns5:Action="http://ws.cxf.fkjava.org/HelloWorld/getAllCatsRequest"
message="tns:getAllCats" />
<output xmlns:ns6="http://www.w3.org/2007/05/addressing/metadata"
ns6:Action="http://ws.cxf.fkjava.org/HelloWorld/getAllCatsResponse"
message="tns:getAllCatsResponse" />
</operation>
</portType>
</definitions>
说明:
- 1.这里我们可以看到接口的
WSDL
文档包含<types>、<message>、<portType>
三个子元素。 - 2.
<types>
标签是规定接口文档的schema
文档,我们使用其地址
schemaLocation="http://localhost:8080/myService?xsd=1"
就可以得到其详细文档说明。 - 3.而接口中一个方法对应两个
<message>
,因为我们在访问服务端的方法时会有一个发送消息和一个返回消息,于是一般有2N个<message>
标签。 - 4.一次
WebService
的调用其实并不是方法调用,而是发送SOAP
消息(即XML
文档片段)。 - 5.
<portType>
表示方法接口,里面规定有多少个操作,由子标签<operation>
说明。
二、SOAP(Simple Object Access Protocol
)简单对象访问协议
接着上面的分析,对于一次sayHi
操作来说,传入消息是
<sayHi>
<arg0>字符串</arg0>
</sayHi>
返回的消息是
<sayHiResponse>
<return>字符串</return>
</sayHiResponse>
这是通过接口WSDL
文档和schema
文档得出来的,我们使用地址http://localhost:8080/myService?xsd=1
访问可以得到相关的schema
文档:
<xs:schema xmlns:tns="http://ws.cxf.fkjava.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0" targetNamespace="http://ws.cxf.fkjava.org/">
<xs:element name="getAllCats" type="tns:getAllCats" />
<xs:element name="getAllCatsResponse" type="tns:getAllCatsResponse" />
<xs:element name="getCatsByUser" type="tns:getCatsByUser" />
<xs:element name="getCatsByUserResponse" type="tns:getCatsByUserResponse" />
<xs:element name="sayHi" type="tns:sayHi" />
<xs:element name="sayHiResponse" type="tns:sayHiResponse" />
<xs:complexType name="sayHi">
<xs:sequence>
<xs:element name="arg0" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHiResponse">
<xs:sequence>
<xs:element name="return" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="getCatsByUser">
<xs:sequence>
<xs:element name="arg0" type="tns:user" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="user">
<xs:sequence>
<xs:element name="address" type="xs:string" minOccurs="0" />
<xs:element name="id" type="xs:int" minOccurs="0" />
<xs:element name="name" type="xs:string" minOccurs="0" />
<xs:element name="password" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="getCatsByUserResponse">
<xs:sequence>
<xs:element name="return" type="tns:cat" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="cat">
<xs:sequence>
<xs:element name="color" type="xs:string" minOccurs="0" />
<xs:element name="id" type="xs:int" minOccurs="0" />
<xs:element name="name" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="getAllCats">
<xs:sequence />
</xs:complexType>
<xs:complexType name="getAllCatsResponse">
<xs:sequence>
<xs:element name="return" type="tns:stringCat" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="stringCat">
<xs:sequence>
<xs:element name="entries" type="tns:entry" nillable="true"
minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="entry">
<xs:sequence>
<xs:element name="key" type="xs:string" minOccurs="0" />
<xs:element name="value" type="tns:cat" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:schema>
执行过程是,首先在接口WSDL
文档中找到sayHi
的<message>
标签,发现element="tns:sayHi"
,然后在schema
文档中找到<xs:element name="sayHi" type="tns:sayHi"/>
进而找到<xs:complexType name="sayHi">
在这个标签中我们可以看到name="arg0" type="xs:string" minOccurs="0"
即参数是arg0
,类型是字符串,参数个数最少0个,最多一般没有说明就默认是一个,而如果规定maxOccurs="unbounded"
就表示最多无限多个。于是我们得到发送的XML
片段。其他的操作同理。
注意:对于请求是有输入参数的,而没有返回值;对于响应才会有返回值,于是会指定name="arg0"
。
对于getCatsByUser
操作的返回消息:
<getCatsByUserResponse>
<return>
<color>字符串</color>
<id>int</id>
<name>字符串</name>
</return>
</getCatsByUserResponse>
但是注意这里的return
可以出现无限次,对应我们的返回值List
类型。
对于getAllCats
操作,是没有输入参数的,但是返回类型较为复杂,我们从schema
文档中可以看到分了多个层次进行定义。
注:对于第三大技术基础UUDI
现在用处不大。
三、调用一次WebService的本质
1.客户端把调用方法参数,转换成
XML
文档片段(SOAP
消息,input
消息)<-->
该文档片段符合WSDL
定义的格式。2.通过网络把
XML
文档片段传给服务器。3.服务器接收到
XML
文档片段4.服务器解析
XML
文档片段,提取其中的数据,并把数据转换成调用WebService
所需的参数值。5.服务器执行方法。
6.把执行方法得到的返回值再次转换为
XML
文档片段(SOAP
消息,output
消息)<-->
该文档片段也符合WSDL
定义的格式。7.通过网络把
XML
文档片段传给客户端。8.客户端接收到
XML
文档片段。9客户端解析
XML
文档片段,提取其中的数据,并把数据转换成调用WebService
所需的返回值。
总结:从上面调用本质来看,要一个语言支持WebService
,唯一的要求是该语言支持XML
文档解析、生成、支持网络传输。
总的来说,WSDL
文档描述如下三个方面:该WebService
包含了什么操作;该WebService
的操作应该怎样调用;该WebService
的服务器地址。