1. 程式人生 > >Java程式設計師從笨鳥到菜鳥(七十一)WebService 一種遠端呼叫技術

Java程式設計師從笨鳥到菜鳥(七十一)WebService 一種遠端呼叫技術

原文連結:https://blog.csdn.net/c99463904/article/details/76018436 非常感謝作者

前言

隨著 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 文件,如下圖:
在這裡插入圖片描述

客戶端實現

// 先使用工具生成客戶端程式碼

// wsimport 是 jdk 自帶的 WebService 客戶端工具,可以根據 wsdl 文件生成客戶端呼叫程式碼

// 1、建立一個客戶端孔祥明,cmd 進入 jdk bin 目錄下
使用如下命令生成客戶端程式碼:(注意 keep 後面網址換成自己客戶端釋出時的網址)
	wsimport -s 專案 src 路徑 -p client -keep http:localhost:8080/weather?wsdl
	CMD 顯示如下資訊:
	正在解析 WSDL...
	正在生成程式碼...
	正在編譯程式碼...
前往 eclipse 重新重新整理客戶端的專案,下圖會有生成之後的專案結構

在這裡插入圖片描述
在這裡插入圖片描述

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 是隨服務釋出成功,自動生成,無需編寫

文件結構

在這裡插入圖片描述

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 元素,提供相關在批處理此訊息所發生錯誤的資訊

在這裡插入圖片描述

先看下上面天氣程式傳送的資料的格式,這需要一個工具 TCP/IP Monitor,Eclipse 自帶的 Debug 工具之一,用於鋪貨 Http、TCP/IP 協議包。原理是一個代理伺服器,客戶端先把資料傳送到代理伺服器,然後代理伺服器再把資料傳送到服務端,這樣就能獲取請求資料和響應資料

設定代理伺服器
第一步:Windows -> Show View -> other
第二步:搜尋 TCP/IP Monitor,右上角下拉箭頭,選 property
第三步:詳細設定

第一個引數是代理伺服器埠,我們設定 12345
第二個引數是被代理伺服器的地址
第三個引數是被代理伺服器的埠
第四個引數要選擇為 TCP/IP

在這裡插入圖片描述
第四步:設定完之後在 TCP/IP Monitor 有剛才設定的資訊,需要注意 Status 那一項,如果是關閉停止狀態下就需要開啟,之後使用 代理代理埠獲取 wsdl 檔案

在這裡插入圖片描述

第五步:設定成功只有,需要更改一下客戶端要連線的伺服器,改成代理伺服器的埠

將 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標準的生成方式。    

在這裡插入圖片描述

第二步:檢視 wsdl 檔案,獲取我們需要的資訊

在這裡插入圖片描述

第三步:根據獲取到的服務名等資訊來建立我們的客戶端

在這裡插入圖片描述

這種方式使用簡單,但一些關鍵的元素在程式碼生成時寫在生成程式碼中,不方便維護

二、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 檔案,發現已經做了相應的更改
在這裡插入圖片描述

SOAP、HTTP、TCP/IP 之間的關係
在這裡插入圖片描述