WebService介绍及简单实例

昨天记录了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干嘛闭源啊!)

他们之间的关系结构如下图:
基本的WebService架构流程图

运行步骤:
1. 发布,服务提供者发布服务到注册中心
2. 发现,请求服务者到注册中心查询需要的服务
3. 发现,注册中心返回请求服务者需要用的服务的说明书,为了让不同的平台和语言都看懂,所以用了他们都看得懂得WSDL。
4. 绑定,请求服务者看了WSDL说明说后,就要用规定的SOAP协议发送请求给服务提供者获取数据
5. 绑定,服务提供者接收到请求后,解析SOAP,其实里面就是一堆XML数据格式,包括调用方法的参数等。然后执行相应的服务,得到数据后封装成XML以SOAP协议返回给服务请求者。

调用过程如下图:
WebService调用过程

实例

接下来写一个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代码。
WSDL说明书

接着访问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这个对象,顺着这个对象往下看,发现它继承了SOAPBodyElementMessageElementNodeImpl这些对象,其中NodeImpl实现了XML节点的接口javax.xml.soap.Node,确认SOAP其中的数据格式为XML。
好了,源码太多,先看到这了,感兴趣的可以自行摸索。

参考:
https://www.cnblogs.com/Jessy/p/3528341.html
https://www.cnblogs.com/shaohz2014/p/3671929.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值