前言

随着 web 应用程序的广泛使用,不同应用程序之间的通信也变得更加频繁,如支付宝获取银行接口来获取相应的账户信息,各种天气预报软件获取气象局天气信息的接口来呈现给用户等等。


WebService 简介

简单的说,WebService 就是一种跨编程语言和跨操作系统平台的远程调用技术。远程调用技术就是一台计算机的应用可以调用其他计算机上的应用


WebService 原理

XML、SOAP 和 WSDL 就是构成 WebService 平台的三大技术

  • WebService 采用 http 协议来在客户端和服务端之间传输数据,WebService 使用 XML 来封装数据,XML 主要的优点就是跨平台
  • WebService 通过 http 协议发送请求和接收结果时,发送的请求内容和结果内容都采用 XML 格式封装,并增加了一些特定的 HTTP 消息头,以说明 HTTP 消息的内容格式,这些特定的 HTTP 消息头和 XML 内容格式就是 SOAP 协议规定的
  • WebService 服务器端首先要通过一个 WSDL 文件来说明自己有什么服务可以对外调用。简单的说,WSDL 就像一个说明书,用于描述 WebService 及其方法、参数和返回值。WSDL 文件保存在 Web 服务器上,通过一个 URL 就可以访问。客户端在调用一个 WebService 的服务之前,要知道该服务的 WSDL 文件的地址。WebService 服务提供商可以通过两种方式来暴露 WSDL 文件地址:1、注册到 UDDI 服务器,以便被人查找;2、直接告诉客户端调用者。

WebService 入门案例

以模拟获取所在地区的天气为例

服务端实现:
天气查询接口:

  1. 编写SEI(Service Endpoint Interface),SEI在webservice中称为portType,在java中就是普通接口
package server;
/**
 * @author 明割
 * @Description 编写 SEI(Service EndPoint Interface) SEI 在 web service中称为portType,在java中就是普通接口
 * @date 2018年12月20日 下午3:55:15
 * @since JDK 1.8
 * @version 1.0
 * @Copyright 2018 All rights reserved
 **/
public interface WeatherInterface {
	public String queryWeather(String cityName);
}

天气查询接口实现:
2. 编写SEI实现类,此类作为webservice提供服务类

package server;

import javax.jws.WebService;

/**
 * @author 明割
 * @Description web service 服务端
 * @date 2018年12月20日 下午3:49:20
 * @since JDK 1.8
 * @version 1.0
 * @Copyright 2018 All rights reserved
 **/
@WebService // @WebService 表示该类是一个服务类,需要发布其中的 public 方法
public class WeatherInterfaceImpl implements WeatherInterface{

	@Override
	public String queryWeather(String cityName) {
		// TODO Auto-generated method stub
		System.out.println("获取城市名:" + cityName);
		String weather = "天晴";
		return weather;
	}
	
}

主函数(发布应用):
3. 第三步:发布服务,Endpoint类发布服务,publish方法,两个参数:1.服务地址;2.服务实现类

package server;

import javax.xml.ws.Endpoint;

/**
 * @author 明割
 * @Description 发布应用
 * @date 2018年12月20日 下午4:00:27
 * @since JDK 1.8
 * @version 1.0
 * @Copyright 2018 All rights reserved
 **/
public class WeatherServer {
	public static void main(String[] args) {
		Endpoint.publish("http://localhost:8080/weather", new WeatherInterfaceImpl());
		System.out.println("weather service publish success-");
	}
}

4、测试服务是否开启成功,通过阅读wsdl,确定客户端调用的接口、方法、参数和返回值存在,证明服务发布成功

// 在浏览器端输入 http://localhost:8080/weather?wsdl 来获取 wsdl 文件进行阅读

输入网址之后,服务开启成功,显示 wsdl 文档,如下图:

java 调用远程服务器shell脚本 java远程调用技术_java 调用远程服务器shell脚本

客户端实现

// 先使用工具生成客户端代码

// wsimport 是 jdk 自带的 WebService 客户端工具,可以根据 wsdl 文档生成客户端调用代码

// 1、创建一个客户端孔祥明,cmd 进入 jdk bin 目录下
使用如下命令生成客户端代码:(注意 keep 后面网址换成自己客户端发布时的网址)
	wsimport -s 项目 src 路径 -p client -keep http:localhost:8080/weather?wsdl
	CMD 显示如下信息:
	正在解析 WSDL...
	正在生成代码...
	正在编译代码...
前往 eclipse 重新刷新客户端的项目,下图会有生成之后的项目结构

java 调用远程服务器shell脚本 java远程调用技术_客户端_02


java 调用远程服务器shell脚本 java远程调用技术_java 调用远程服务器shell脚本_03

2、编写客户端代码
package client;
/**
 * @author 明割
 * @Description 客户端代码
 * @date 2018年12月20日 下午4:23:31
 * @since JDK 1.8
 * @version 1.0
 * @Copyright 2018 All rights reserved
 **/
public class WeaherClient {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 创建服务器视图,视图是从 wsdl 文件的 service 标签的 name 属性获取
		WeatherInterfaceImplService weatherInterfaceImplService = new WeatherInterfaceImplService();
		
		// 获取服务实现类,实现类从 wsdl 文件的 portType 的 name 属性获取
		WeatherInterfaceImpl weatherInterfaceImpl = weatherInterfaceImplService.getPort(WeatherInterfaceImpl.class);
	
		// 获取查询方法,从 portType 的 operation 标签获取
		String weather = weatherInterfaceImpl.queryWeather("北京");
		System.out.println("天气为:" + weather);
	}

}

运行结果:

天气为:天晴

至此,我们完成了客户端可以获取远程服务端的数据,接下来详解各个部分。


WSDL

wsdl(Web Service Description Language),web 服务描述语言,是 webservice 服务端使用说明书,说明服务端接口、方法、参数和返回值,WSDL 是随服务发布成功,自动生成,无需编写

文档结构

java 调用远程服务器shell脚本 java远程调用技术_java_04

1、Service:相关端口的集合,包括其关联的接口、操作、消息等
2、Binding:特定端口类型的具体协议和数据格式规范
3、portType:服务端点,描述 web service 可被执行的操作方法,以及相关消息,通过 binding 指向 portType
4、message:定义一个操作(方法)的数据参数
5、type:定义 web service 使用的全部数据类型

阅读方式

WSDL 文档应该从下往上阅读:

  1. 先看 service 标签,看相应 port 的 binding 属性,然后通过值查找上面的 binding 标签
  2. 通过 binding 标签可以获得具体协议等信息,然后查看 binding 的 type 属性
  3. 通过 binding 的 type 属性,查找对应的 portType,可以获得可操作的方法和参数、返回值等
  4. 通过 portType 下的 operation 标签的 message 属性,可以向上查找 message 获取具体的数据参数信息

SOAP

SOAP 即简单对象访问协议,他使用 http 发送的 XML 格式的数据,它可以跨平台,跨防火墙,SOAP 不是 webservice 的专有协议。

SOAP = http + xml
SOAP 结构

1、必需的 Envelop 元素,可把此 XML 文档标识为一条 SOAP 消息
2、可选的 Header 元素,包含头部信息
3、必需的 Body 元素,包含所有的调用和响应信息
4、可选的 Fault 元素,提供相关在批处理此消息所发生错误的信息

java 调用远程服务器shell脚本 java远程调用技术_WebService_05

先看下上面天气程序发送的数据的格式,这需要一个工具 TCP/IP Monitor,Eclipse 自带的 Debug 工具之一,用于铺货 Http、TCP/IP 协议包。原理是一个代理服务器,客户端先把数据发送到代理服务器,然后代理服务器再把数据发送到服务端,这样就能获取请求数据和响应数据

设置代理服务器
第一步:Windows -> Show View -> other
第二步:搜索 TCP/IP Monitor,右上角下拉箭头,选 property
第三步:详细设置

第一个参数是代理服务器端口,我们设置 12345
第二个参数是被代理服务器的地址
第三个参数是被代理服务器的端口
第四个参数要选择为 TCP/IP

java 调用远程服务器shell脚本 java远程调用技术_服务端_06


第四步:设置完之后在 TCP/IP Monitor 有刚才设置的信息,需要注意 Status 那一项,如果是关闭停止状态下就需要开启,之后使用 代理代理端口获取 wsdl 文件

java 调用远程服务器shell脚本 java远程调用技术_服务端_07

第五步:设置成功只有,需要更改一下客户端要连接的服务器,改成代理服务器的端口

将 WeatherInterfaceImplService 里的所有原来的地址的端口改为 12345

UDDI

UDDI 是一种目录服务,企业可以使用它对 Web Service 进行注册和搜索,如果我们需要一种服务,但是不知道地址(wsdl 等),就可以在 UDDI 中查找,但是在大部分情况下,都是直到服务地址的

WebService 的客户端调用方式

一、生成客户端的方式

wsimport是jdk自带的webservice客户端工具,可以根据wsdl文档生成客户端调用代码(java代码).
wsimport.exe位于JAVA_HOME\bin目录下 
常用参数为:
        -d<目录>  - 将生成.class文件。默认参数。
        -s<目录> - 将生成.java文件。
        -p<生成的新包名> -将生成的类,放于指定的包下

例子:
调用公网的手机归属地查询服务
公网服务地址(里面有很多免费调用的服务)
http://www.webxml.com.cn/zh_cn/index.aspx

第一步:wsimport 生成客户端代码
wsimport -p cn.cad.mobile -s 项目src路径 -p client -keep http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl

会出现一些警告,是因为服务端提供的一些方法是SOAP1.2标准的,这个工具没有实现SOAP1.2标准的生成方式。

java 调用远程服务器shell脚本 java远程调用技术_WebService_08

第二步:查看 wsdl 文件,获取我们需要的信息

java 调用远程服务器shell脚本 java远程调用技术_java 调用远程服务器shell脚本_09

第三步:根据获取到的服务名等信息来创建我们的客户端

java 调用远程服务器shell脚本 java远程调用技术_java 调用远程服务器shell脚本_10

这种方式使用简单,但一些关键的元素在代码生成时写在生成代码中,不方便维护

二、service 编程调用方式

/**
 * @author 明割
 * @Description service 编程调用方式
 * @date 2018年12月21日 下午3:11:17
 * @since JDK 1.8
 * @version 1.0
 * @Copyright 2018 All rights reserved
 **/
public class MobileClient2 {
	public static void main(String[] args) throws IOException{
		// 创建 WSDL 文件的 URL
		URL wsdlDocumentLocation = new URL("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl");
		
		// 创建服务名称
		// 1、namespaceURL - 命名空间地址
		// 2、localPart - 服务视图名
		QName serviceName = new QName("http://WebXml.com.cn/", "MobileCodeWS");
		Service service = Service.create(wsdlDocumentLocation, serviceName);
		
		// 获取服务实现类
		MobileCodeWSSoap mobileCodeWSSoap = service.getPort(MobileCodeWSSoap.class);
		// 调用方法
		String message = mobileCodeWSSoap.getMobileCodeInfo("18720996704", null);
		System.out.println("号码归属地为:" + message);
	}
}

这种方式可以自定义命名空间,服务视图名等元素,方便以后维护,是一种标准的开发方式

三、HttpURLConnection 调用方式
这种方式是自己编写客户端,不再由工具生成,稍微麻烦

开发步骤:
	第一步:创建服务地址
	第二步:打开一个通向服务地址的连接
	第三步:设置参数
	第四步:组织 SOAP 数据,发送请求
	第五步:接收服务端响应

使用注解修改 WSDL 内容

作用:
使用注解,可以更加形象的描述 web 服务。对自动生成的 wsdl 文档进行修改,为使用者提供一个更加清晰的 wsdl 文档

WebService 的注解都位于 javax.jws 包下:

@WebService - 定义服务,在类上边
	targetNameSpace: 指定命名空间
	name: portType 的名称
	portName: port 的名称
	serviceName: 服务名称
	endpointInterface: SEI 接口地址,如果一个服务类实现了多个接口,只需发布一个接口的方法,可通过此注解指定发布服务的接口。

@WebMethod - 定义方法,在公开方法上边
	operationName: 方法名
	exclude: 这只为 true 表示此方法不是 webservice 方法,反之则表示 webservice 方法,默认是 false

@WebResult -  定义返回值,在方法返回值前边
	name: 返回结果值的名称

代码实现:

package server;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

/**
 * @author 明割
 * @Description web service 服务端
 * @date 2018年12月20日 下午3:49:20
 * @since JDK 1.8
 * @version 1.0
 * @Copyright 2018 All rights reserved
 **/
//@WebService 表示该类是一个服务类,需要发布其中的 public 方法
@WebService(
		targetNamespace = "http://service.cad.cnm",
		portName = "WeatherSOAPPort",
		serviceName = "WeatherWSS",
		name = "WeatherSOAP"
		)

public class WeatherInterfaceImpl implements WeatherInterface{

	@WebMethod(
			operationName = "getWeather",
			exclude = false
			)
	
	
	@Override
	public @WebResult(name = "result") String queryWeather(@WebParam(name = "cityName")String cityName) {
		// TODO Auto-generated method stub
		System.out.println("获取城市名:" + cityName);
		String weather = "天晴";
		return weather;
	}
	
}

重新发布服务之后,查看 wsdl 文件,发现已经做了相应的更改

java 调用远程服务器shell脚本 java远程调用技术_WebService_11

SOAP、HTTP、TCP/IP 之间的关系

java 调用远程服务器shell脚本 java远程调用技术_java 调用远程服务器shell脚本_12