Web Service 运行原理详细介绍

所属分类: 软件编程 / java 阅读数: 45
收藏 0 赞 0 分享

     利用清明小假期,温习了一遍Web Service的相关内容,对其工作原理进行了简要总结。以供有需求的朋友和自己日后参考。文章若有不当之处,敬请朋友们提出宝贵建议,以求共勉。

      Web服务中,我们应该首先了解相关的术语含义:WSDL、UDDI....相关术语方面的介绍在此不再赘述,重点放在原理上。
在Web服务中,存在三个角色:服务提供者、服务请求者和服务中介,三者之间的关系如图1-1所示

    实现一个完整的Web服务包括以下步骤:

   ◆ Web服务提供者设计实现Web服务,并将调试正确后的Web服务通过Web服务中介者发布,并在UDDI注册中心注册; (发布)

   ◆ Web服务请求者向Web服务中介者请求特定的服务,中介者根据请求查询UDDI注册中心,为请求者寻找满足请求的服务; (发现)

   ◆ Web服务中介者向Web服务请求者返回满足条件的Web服务描述信息,该描述信息用WSDL写成,各种支持Web服务的机器都能阅读;(发现)

   ◆ 利用从Web服务中介者返回的描述信息(WSDL)生成相应的SOAP消息,发送给Web服务提供者,以实现Web服务的调用;(绑定)

   ◆ Web服务提供者按SOAP消息执行相应的Web服务,并将服务结果返回给Web服务请求者。(绑定)

  

图1-1 Web service的体系结构

     注:WSDL的作用就是一个Web服务说明书。服务请求者根据此WSDL生成相应的SOAP消息,服务提供者在收到SOAP请求消息后,进行服务的绑定。

     以下代码是在web.xml中的servlet配置

  <!-- 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。 -->
  <servlet>
  <servlet-name>UserService</servlet-name>
  <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
  <!-- 标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法;正数的值越小,该servlet的优先级越高,应用启动时就越先加载 -->
  <load-on-startup>1</load-on-startup>
  </servlet>
  <!-- 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName。
   但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。 -->
  <servlet-mapping>
   <servlet-name>UserService</servlet-name>
   <!-- 描述了相对于Web应用的根目录的URL。url-pattern元素的值必须以斜杠(/)起始。 -->
   <url-pattern>/user</url-pattern>
  </servlet-mapping>
  红色代码部分很重要,会在Web容器启动的时候加载相应的servlet。绿色部分为该服务的外部接口。以此找到相应的jax-ws.xml文件(如下所示)
  <endpoint name="UserPort" implementation="cn.ujn.service.UserService"
    url-pattern="/user">
  </endpoint>

    进而绑定到相关的相应的实现类cn.ujn.service.UserService中。客户端发送的SOAP请求消息消息体body中包含有客户端所请求的方法名和参数信息。

    以下为客户端封装的soap消息体(以Json方式与服务端进行数据传输)(SOAP Rerquest Envelope):

  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://ujn.cn/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <soapenv:Body>
-  <q0:login>
     <arg0>{"username":"shq","password":"shq"}</arg0>
 </q0:login>
 </soapenv:Body>
 </soapenv:Envelope>

以下为SOAP1.1协议调用Web服务

/** 
* 通过SOAP1.1协议调用Web服务 
* 
* text/xml 这是基于soap1.1协议 
* 
* @param wsdl WSDL路径 
* @param method方法名 
* @param namespace命名空间 
* @param headerParameters 头参数 
* @param bodyParameters  体参数 
* @param isBodyParametersNS 体参数是否有命名空间 
* @return String 
* @throws Exception 
*/ 
public static String invokeBySoap11(String wsdl, String method, 
String namespace, Map<String, String> headerParameters, 
Map<String, String> bodyParameters, boolean isBodyParametersNS) 
throws Exception { 
StringBuffer soapOfResult = null; 
 
// 去除 ?wsdl,获取方法列表 
int length = wsdl.length(); 
wsdl = wsdl.substring(0, length - 5); 
//以字符串为参数创建URL实例 
URL url = new URL(wsdl); 
//创建连接 
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
//设置请求方式 
conn.setRequestMethod("POST"); 
//如果打算使用 URL连接进行输入,则将 DoInput 标志设置为 true 
conn.setDoInput(true); 
//如果打算使用 URL连接进行输出,则将 DoInput 标志设置为 true 
conn.setDoOutput(true); 
//主要是设置HttpURLConnection请求头里面的属性(K-V) 
conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8"); 
//获取输入流(相对于客户端来说,使用的是OutputStream) 
OutputStream out = conn.getOutputStream(); 
// 获取soap1.1版本消息 
StringBuilder sb = new StringBuilder(); 
sb.append("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"  
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "); 
sb.append("xmlns:ns0=\"" + namespace + "\""); 
sb.append(">"); 
//拼装消息头 
if (headerParameters != null) { 
sb.append("<soap:Header>"); 
for (Entry<String, String> headerParameter : headerParameters 
.entrySet()) { 
sb.append("<ns0:"); 
sb.append(headerParameter.getKey()); 
sb.append(">"); 
sb.append(headerParameter.getValue()); 
sb.append("</ns0:"); 
sb.append(headerParameter.getKey()); 
sb.append(">"); 
} 
sb.append("</soap:Header>"); 
} 
//拼装消息体 
sb.append("<soap:Body><ns0:"); 
sb.append(method); 
sb.append(">"); 
// 输入参数 
if (bodyParameters != null) { 
for (Entry<String, String> inputParameter : bodyParameters 
.entrySet()) { 
if (isBodyParametersNS) { 
sb.append("<ns0:"); 
sb.append(inputParameter.getKey()); 
sb.append(">"); 
sb.append(inputParameter.getValue()); 
sb.append("</ns0:"); 
sb.append(inputParameter.getKey()); 
sb.append(">"); 
} else { 
sb.append("<"); 
sb.append(inputParameter.getKey()); 
sb.append(">"); 
sb.append(inputParameter.getValue()); 
sb.append("</"); 
sb.append(inputParameter.getKey()); 
sb.append(">"); 
} 
} 
} 
sb.append("</ns0:"); 
sb.append(method); 
sb.append("></soap:Body></soap:Envelope>"); 
//测试用 
System.out.println(sb.toString()); 
//写入SOAP消息(相对于客户端来说,使用的是out.write()) 
out.write(sb.toString().getBytes()); 
//获取服务器端的相应 
int code = conn.getResponseCode(); 
if (code == 200) { 
InputStream is = conn.getInputStream(); 
byte[] b = new byte[1024]; 
int len = 0; 
soapOfResult = new StringBuffer(); 
//从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数 
//如果因为流位于文件末尾而没有可用的字节,则返回值 -1; 
while ((len = is.read(b)) != -1) { 
//Converts the byte array to a string using the named charset.  
String s = new String(b, 0, len, "UTF-8"); 
soapOfResult.append(s); 
} 
} 
conn.disconnect(); 
return soapOfResult == null ? null : soapOfResult.toString(); 
} 

    注:在客户端发送SOAP请求消息后便处于阻塞状态。直至服务端返回状态码。

    以下为服务端进行响应(SOAP Response Envelope):

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
-<S:Body>
-<ns2:loginResponse xmlns:ns2="http://ujn.cn/">
 <return>1</return>
</ns2:loginResponse>
 </S:Body>
</S:Envelope>

    客户端接收到服务端发来的Json数据后会进行相应的解析操作。如下:

// 将Soap协议进行解析(DOM解析只能用于解析XML文档类型,而SOAP消息就是采用XML数据格式) 
Document doc = XmlUtil.string2Doc(result); 
Element ele = (Element) doc.getElementsByTagName("return").item(0); 
方法中使用到的string2Doc()方法体如下: 
public static Document string2Doc(String str) { 
//将XML文档解析成DOM树 
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
Document document = null; 
DocumentBuilder build; 
if (str == null || str.equals("")) { 
return null; 
} 
try { 
InputStream bais = new ByteArrayInputStream(str.getBytes("UTF-8")); 
build = factory.newDocumentBuilder(); 
//Parse the content of the given InputStream as an XML document and return a new DOM Document object.  
document = build.parse(bais); 
} catch (Exception e) { 
e.printStackTrace(); 
} 
return document; 
} 

    根据返回结果,客户端再进行相应的处理。

    以上是web服务的基本工作原理。

    感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多