最近项目工作内容需要与其他公司进行对接,对方公司采用了WebService,对这方面不太熟悉,因此快速学习了下,笔记及实战内容如下。
简介
WebService是一种跨编程语言和操作系统的远程调用技术,它是自描述、 自包含的可用网络模块, 可以执行具体的业务功能。
SOA
面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。SOA是一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。SOA可以看作是B/S模型、XML(标准通用标记语言的子集)/Web Service技术之后的自然延伸。
要求
(1)WebService平台需要一套协议来实现分布式应用程序的创建。任何平台都有它的数据表示方法和类型系统。要实现互操作性,WebService平台 必须提供一套标准的类型系统,用于沟通不同平台、编程语言和组件模型中的不同类型系统。
(2)Web service平台必须提供一种标准来描述 Web service,让客户可以得到足够的信息来调用这个Web service。
(3)WebService还必须有一种方法来对这个Web service进行远 程调用,这种方法实际是一种远程过程调用协议(RPC)。为了达到互操作性,这种RPC协议还必须与平台和编程语言无关。
组成
(1)XML+XSD:WebService通过Http传送数据,通过与平台、厂商无关的XML来包装数据。被包装的数据包含的信息有:调用的方法、传递的参数、返回值。XML解决了数据表示的问题,但它没有定义一套标准的数据类型,更没有说怎么去扩展这套数据类型。XML Schema(XSD)就是专门解决这个问题的一套标准。它定义了一套标准的数据类型,并给出了一种语言来扩展这套数据类型。WebService平台就 是用XSD来作为其数据类型系统的。WSDL 文件保存在Web服务器上,通过一个url地址就可以访问到它。
(2)WSDL(Web Services Description Language):WebService描述语言。每一个WebService会有一个唯一的wsdl文档。WebService客户端要调用一个WebService服务,首先要知道这个服务地址在哪,有什么服务可以调用,方法参数和返回值是什么。WSDL就是这样一个基于XML的语言,用来描述WebService及其函数、参数、返回值。
WSDL构成(具体内容参考案例中wsdl样式):
a. <portType>:WebService 执行的操作,可以把 <portType> 元素比作传统编程语言中的一个函数库(或一个模块、或一个类),而把每个操作比作传统编程语言中的一个函数。
b. <message>:WebService 使用的消息,每个消息均由一个或多个部件组成。可以把这些部件比作传统编程语言中一个函数调用的参数或返回值。
c. <types>:WebService 使用的数据类型
d. <binding>:WebService 使用的通信协议
(3)SOAP:SOAP协议 = Http + XML数据格式,简单对象访问协议,是一种通信协议。WebService通过Http发送请求和接收结果时,发送的内容和接收的结果都是通过XML进行封装,并增加了一些特定的http头,以说明XML的格式,这些特定的Http头和xml内容就是SOAP。
SOAP构成
a. <soap:Envelope>:必需的 Envelope 元素,可把此 XML 文档标识为一条 SOAP 消息
b. <soap:Header>:可选的 Header 元素,包含头部信息
c. <soap:Body>:必需的 Body 元素,包含所有的调用和响应信息
d. <soap:Fault>:可选的 Fault 元素,提供有关在处理此消息所发生错误的信息
(4)UDDI(Universal Description, Discovery and Integration):UDDI 是一种用于存储有关 web services 的信息的目录,用于通用描述、发现及整合。
使用案例
(1)服务端:把公司内部系统的业务方法发布成WebService服务,供远程合作单位和个人调用。
package issueservice;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public interface SimpleService {
@WebMethod
void simpleInput(String input);
@WebMethod
String simpleWrite(String input);
}
package issueservice;
import javax.jws.WebService;
@WebService
public class SimpleServiceImpl implements SimpleService {
@Override
public void simpleInput(String input) {
System.out.println("调用webService-input:" + input );
}
@Override
public String simpleWrite(String input) {
System.out.println("调用webService-write:" + input );
return "调用成功";
}
}
package issueservice;
import javax.xml.ws.Endpoint;
public class IssueWebService {
public static void main(String[] args) {
String address = "http://localhost:80/testwebservice";
SimpleService service = new SimpleServiceImpl();
Endpoint.publish(address, service);
System.out.println("发布服务成功");
}
}
运行main
发布服务成功
(2)wsdl:访问地址为——http://localhost/testwebservice?wsdl
<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://issueservice/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://issueservice/" name="SimpleServiceImplService">
<types>
<xsd:schema>
<xsd:import namespace="http://issueservice/" schemaLocation="http://localhost/testwebservice?xsd=1"/>
</xsd:schema>
</types>
<message name="simpleInput">
<part name="parameters" element="tns:simpleInput"/>
</message>
<message name="simpleInputResponse">
<part name="parameters" element="tns:simpleInputResponse"/>
</message>
<message name="simpleWrite">
<part name="parameters" element="tns:simpleWrite"/>
</message>
<message name="simpleWriteResponse">
<part name="parameters" element="tns:simpleWriteResponse"/>
</message>
<portType name="SimpleServiceImpl">
<operation name="simpleInput">
<input wsam:Action="http://issueservice/SimpleServiceImpl/simpleInputRequest" message="tns:simpleInput"/>
<output wsam:Action="http://issueservice/SimpleServiceImpl/simpleInputResponse" message="tns:simpleInputResponse"/>
</operation>
<operation name="simpleWrite">
<input wsam:Action="http://issueservice/SimpleServiceImpl/simpleWriteRequest" message="tns:simpleWrite"/>
<output wsam:Action="http://issueservice/SimpleServiceImpl/simpleWriteResponse" message="tns:simpleWriteResponse"/>
</operation>
</portType>
<binding name="SimpleServiceImplPortBinding" type="tns:SimpleServiceImpl">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="simpleInput">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name="simpleWrite">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="SimpleServiceImplService">
<port name="SimpleServiceImplPort" binding="tns:SimpleServiceImplPortBinding">
<soap:address location="http://localhost/testwebservice"/>
</port>
</service>
</definitions>
(3)客户端:通过服务端提供的wsdl文件的url地址,生成底层的代理类,通过代理类访问服务。
通过jdk中wsimport生成对应类,此处我的命令为wsimport -keep http://localhost/testwebservice?wsdl,生成具体的类如下,具体命令可以百度学习。
根据生成的类,简单处理即可完成一个访问webSebvice的客户端。
package testwebservice.test;
import testwebservice.domain.SimpleServiceImpl;
import testwebservice.domain.SimpleServiceImplService;
public class TestServiceMain {
public static void main(String[] args) {
SimpleServiceImplService service = new SimpleServiceImplService();
SimpleServiceImpl port = service.getSimpleServiceImplPort();
port.simpleInput("测试input");
System.out.println("测试input");
System.out.println("-------------------------------");
String simpleWrite = port.simpleWrite("测试write");
System.out.println("测试write:" + simpleWrite);
}
}
// 客户端
测试input
-------------------------------
测试write:调用成功
==================================
// 服务端
发布服务成功
调用webService-input:测试input
调用webService-write:测试write
这里有一个地方需要注意的是,SimpleServiceImpl中有一个注解。
package testwebservice.domain;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.Action;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
@WebService(name = "SimpleServiceImpl", targetNamespace = "http://issueservice/")
//@XmlSeeAlso({
// ObjectFactory.class
//})
public interface SimpleServiceImpl {
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "simpleWrite", targetNamespace = "http://issueservice/", className = "issueservice.SimpleWrite")
@ResponseWrapper(localName = "simpleWriteResponse", targetNamespace = "http://issueservice/", className = "issueservice.SimpleWriteResponse")
@Action(input = "http://issueservice/SimpleServiceImpl/simpleWriteRequest", output = "http://issueservice/SimpleServiceImpl/simpleWriteResponse")
public String simpleWrite(
@WebParam(name = "arg0", targetNamespace = "")
String arg0);
@WebMethod
@RequestWrapper(localName = "simpleInput", targetNamespace = "http://issueservice/", className = "issueservice.SimpleInput")
@ResponseWrapper(localName = "simpleInputResponse", targetNamespace = "http://issueservice/", className = "issueservice.SimpleInputResponse")
@Action(input = "http://issueservice/SimpleServiceImpl/simpleInputRequest", output = "http://issueservice/SimpleServiceImpl/simpleInputResponse")
public void simpleInput(
@WebParam(name = "arg0", targetNamespace = "")
String arg0);
}
将该处@XmlSeeAlso注解放开,执行的时候会报错:两个类具有相同的XML类型名称。具体原因因为时间有限,没有详细去了解,后续补充,把它注释掉后,正常执行。
场景:跨平台、跨语言、远程调用