昨天记录了WebSocket,今天写下WebService。虽然这两个东西名字有点像,但它们是完全不同的东西,之所以连续记录之前有说过,因为这段时间有空(辞职 :) 了),把之前感兴趣的技术都学习了解下。
介绍
WebService是一种远程调用技术,通过SOAP协议在互联网上传输数据。由于采用通用的XML的数据格式,所以有跨编程语言和跨平台的特性。说实话,有点像RESTful API,都是用于远程调用,熟悉API接口调用的同学应该比较好理解。他们的区别是RESTful API是面向结果资源,不需要知道方法名和调用过程,而WebService面向过程,会暴露出方法名等。从易用度上来说,RESTful API比较容易。但从安全性来说,WebService比较好。在使用WebService之前需要了解的几个概念,XML、SOAP、WSDL和UDDI 。
XML(Extensible Markup Language):扩展型可标记语言。面向短期的临时数据处理、面向万维网络,是Soap的基础。
SOAP(Simple Object Access Protoco):简单对象存取协议。是XML Web Service 的通信协议。当用户通过UDDI找到你的WSDL描述文档后,他通过可以SOAP调用你建立的Web服务中的一个或多个操作。SOAP是XML文档形式的调用方法的规范,它可以支持不同的底层接口,像HTTP(S)或者SMTP。也就是说也通过HTTP传送,与HTTP之间的关系用邮局来比喻的话,HTTP协议像是邮局规定寄信时信封需要写明发送方信息,接收方信息等。但是SOAP就像是信封里面的内容,规定了用XML这种语言来写。发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议规定的。简单来说,SOAP协议可以理解为:SOAP = RPC + HTTP + XML,即采用HTTP作为通信协议,RPC(Remote Procedure Call Protocol - 远程过程调用协议)作为一致性的调用途径,XML作为数据传送的格式,从而允许服务提供者和服务客户经过防火墙在Internet上进行通信交互。
WSDL(Web Services Description Language):WSDL 文件是一个 XML 文档,用于说明一组 SOAP 消息以及如何交换这些消息。大多数情况下由软件自动生成和使用。
UDDI (Universal Description, Discovery, and Integration):是一个主要针对Web服务供应商和使用者的新项目。在用户能够调用Web服务之前,必须确定这个服务内包含哪些商务方法,找到被调用的接口定义,还要在服务端来编制软件,UDDI是一种根据描述文档来引导系统查找相应服务的机制。UDDI利用SOAP消息机制(标准的XML/HTTP)来发布,编辑,浏览以及查找注册信息。它采用XML格式来封装各种不同类型的数据,并且发送到注册中心或者由注册中心来返回需要的数据。
运行原理
在解析运行过程之前要说明几个角色。
服务提供者(Provider):既是发布服务的服务端
请求服务者(Requester):需要调用服务的客户端
注册中心(Registry):发布服务到注册中心,提供服务发现,这里指的就是UDDI。(联想到Eureka了,还是必要性的吐槽下,Eureka2.0干嘛闭源啊!)
他们之间的关系结构如下图:
运行步骤:
1. 发布,服务提供者发布服务到注册中心
2. 发现,请求服务者到注册中心查询需要的服务
3. 发现,注册中心返回请求服务者需要用的服务的说明书,为了让不同的平台和语言都看懂,所以用了他们都看得懂得WSDL。
4. 绑定,请求服务者看了WSDL说明说后,就要用规定的SOAP协议发送请求给服务提供者获取数据
5. 绑定,服务提供者接收到请求后,解析SOAP,其实里面就是一堆XML数据格式,包括调用方法的参数等。然后执行相应的服务,得到数据后封装成XML以SOAP协议返回给服务请求者。
调用过程如下图:
实例
接下来写一个WebService简单的HelloWorld,按照以上的运行步骤,需要先写一个服务提供者。
编写并发布服务
建立一个新的工程,JDK建议1.6以上,目录结构如下。
新建一个HelloWebService的Class
package com.xio.service;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
@WebService
public class HelloWebService {
public String HelloService(String name){
return name + " Hello WebService!";
}
public static void main(String args[]){
Endpoint.publish("http://192.168.0.103:2020/helloWord",new HelloWebService());
System.out.println("发布成功");
}
}
运行主方法,发布服务。浏览器访问http://192.168.0.103:2020/helloWord?wsdl
看到一堆 XML文档的描述信息就是发布成功了。请求服务者会根据这堆XML文档生成Stub代码。
接着访问http://192.168.0.103:2020/helloWord?xsd=1
就会看到刚才发布的方法说明,包括传参数,返回数据等。
<xs:complexType name="HelloService">
<xs:sequence>
<xs:element name="arg0" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
其中name="arg0"
表示第一个参数 ,type="xs:string"
表示参数类型是String类型。
<xs:complexType name="HelloServiceResponse">
<xs:sequence>
<xs:element name="return" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
其中name="return"
表示返回的数据 ,type="xs:string"
表示返回数据参数类型是String类型。
编写请求服务者
创建一个名为WebServiceClient普通的java项目,然后右键项目 New -> Other
,选择Web Service Client类型
把WSDL地址复制到Service definition上去,点击下一步
勾上创建默认的包,点击确认后生成代码
生成代码后的目录结构
随后创建一个测试类WebServiceClient
package com.xio.service;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
public class WebServiceClient {
public static void main(String[] args) throws ServiceException, RemoteException {
HelloWebService client = new HelloWebServiceServiceLocator().getHelloWebServicePort();
String info = client.helloService("xio");
System.out.println(info);
}
}
输出xio Hello WebService!
表示调用成功。
其他
为了一探其中WebService调用的奥妙,可以尝试看下源码,如果不感兴趣的可以打住。在测试类中,调用client.helloService("xio");
的实际上是调用了HelloWebServicePortBindingStub
实现类中的helloService
方法,而里面组装了一个Call
类后又是调用了它的invoke()
方法。 可以看出,其中较为核心的类就是org.apache.axis.client.Call
,通过查看源码中的invoke()
方法
public Object invoke(Object[] params)
throws RemoteException
{
long t0 = 0L;long t1 = 0L;
if (tlog.isDebugEnabled()) {
t0 = System.currentTimeMillis();
}
SOAPEnvelope env = null;
for (int i = 0; (params != null) && (i < params.length); i++) {
if (!(params[i] instanceof SOAPBodyElement)) {
break;
}
}
if ((params != null) && (params.length > 0) && (i == params.length))
{
this.isMsg = true;
env = new SOAPEnvelope(this.msgContext.getSOAPConstants(), this.msgContext.getSchemaVersion());
for (i = 0; i < params.length; i++) {
env.addBodyElement((SOAPBodyElement)params[i]);
}
org.apache.axis.Message msg = new org.apache.axis.Message(env);
setRequestMessage(msg);
invoke();
msg = this.msgContext.getResponseMessage();
if (msg == null)
{
if (this.msgContext.isPropertyTrue("call.FaultOnNoResponse", false)) {
throw new AxisFault(Messages.getMessage("nullResponse00"));
}
return null;
}
env = msg.getSOAPEnvelope();
return env.getBodyElements();
}
if (this.operationName == null) {
throw new AxisFault(Messages.getMessage("noOperation00"));
}
try
{
Object res = invoke(this.operationName.getNamespaceURI(), this.operationName.getLocalPart(), params);
if (tlog.isDebugEnabled())
{
t1 = System.currentTimeMillis();
tlog.debug("axis.Call.invoke: " + (t1 - t0) + " " + this.operationName);
}
return res;
}
catch (AxisFault af)
{
if ((af.detail != null) && ((af.detail instanceof RemoteException))) {
throw ((RemoteException)af.detail);
}
throw af;
}
catch (Exception exp)
{
entLog.debug(Messages.getMessage("toAxisFault00"), exp);
throw AxisFault.makeFault(exp);
}
}
可以看出在执行远程调用之前,组装了SOAP的消息体,在此可以更加确定的说明WebService使用的是SOAP协议的远程调用。
根据
Object res = invoke(this.operationName.getNamespaceURI(), this.operationName.getLocalPart(), params);
//...
return res;
看出又是调用了invoke(String namespace, String method, Object[] args)
这个方法,以下为源码:
public Object invoke(String namespace, String method, Object[] args)
throws AxisFault
{
if (log.isDebugEnabled()) {
log.debug("Enter: Call::invoke(ns, meth, args)");
}
if ((getReturnType() != null) && (args != null) && (args.length != 0) && (this.operation.getNumParams() == 0)) {
throw new AxisFault(Messages.getMessage("mustSpecifyParms"));
}
RPCElement body = new RPCElement(namespace, method, getParamList(args));
Object ret = invoke(body);
if (log.isDebugEnabled()) {
log.debug("Exit: Call::invoke(ns, meth, args)");
}
return ret;
}
可以看到封装了 RPCElement
这个对象,顺着这个对象往下看,发现它继承了SOAPBodyElement
、MessageElement
、NodeImpl
这些对象,其中NodeImpl
实现了XML节点的接口javax.xml.soap.Node
,确认SOAP其中的数据格式为XML。
好了,源码太多,先看到这了,感兴趣的可以自行摸索。
参考:
https://www.cnblogs.com/Jessy/p/3528341.html
https://www.cnblogs.com/shaohz2014/p/3671929.html