轉載自http://af8991.iteye.com/blog/1702741
webservice的 釋出一般都是使用WSDL(web service descriptive language)檔案的樣式來釋出的,在WSDL檔案裡面,包含這個webservice暴露在外面可供使用的接口。今天搜尋到了非常好的 webservice provider清單
http://www.webservicex.net/WCF/default.aspx
這上面列出了70多個包括很多方面的free webservice provider,utilities->global weather就可以擷取全球的天氣預報。
下面我們來看Java如何通過WSDL檔案來調用這些web service:
注意,以下的代碼并沒有經過真正的測試,隻是說明這些情況,不同版本的Axis相差很大,大家最好以apache網站上的例子為準,這裡僅僅用于說明其基本用法。
1,直接AXIS調用遠端的web service
我覺得這種方法比較适合那些高手,他們能直接看懂XML格式的WSDL檔案,我自己是看不懂的,尤其我不是專門搞這行的,即使一段時間看懂,後來也就忘記了。直接調用模式如下:
import java.util.Date;
import java.text.DateFormat;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;
import java.lang.Integer;
import javax.xml.rpc.ParameterMode;
public class caClient {
public static void main(String[] args) {
try {
String endpoint = "http://localhost:8080/ca3/services/caSynrochnized?wsdl";
// 直接引用遠端的wsdl檔案
// 以下都是套路
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(endpoint);
call.setOperationName("addUser");// WSDL裡面描述的接口名稱
call.addParameter("userName",
org.apache.axis.encoding.XMLType.XSD_DATE,
javax.xml.rpc.ParameterMode.IN);// 接口的參數
call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);// 設定傳回類型
String temp = "測試人員";
String result = (String) call.invoke(new Object[] { temp });
// 給方法傳遞參數,并且調用方法
System.out.println("result is " + result);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
2,直接SOAP調用遠端的webservice
這種模式我從來沒有見過,也沒有試過,但是網絡上有人貼出來,我也轉過來
import org.apache.soap.util.xml.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
import java.io.*;
import java.net.*;
import java.util.Vector;
public class caService {
public static String getService(String user) {
URL url = null;
try {
url = new URL(
"http://192.168.0.100:8080/ca3/services/caSynrochnized");
} catch (MalformedURLException mue) {
return mue.getMessage();
}
// This is the main SOAP object
Call soapCall = new Call();
// Use SOAP encoding
soapCall.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
// This is the remote object we're asking for the price
soapCall.setTargetObjectURI("urn:xmethods-caSynrochnized");
// This is the name of the method on the above object
soapCall.setMethodName("getUser");
// We need to send the ISBN number as an input parameter to the method
Vector soapParams = new Vector();
// name, type, value, encoding style
Parameter isbnParam = new Parameter("userName", String.class, user,
null);
soapParams.addElement(isbnParam);
soapCall.setParams(soapParams);
try {
// Invoke the remote method on the object
Response soapResponse = soapCall.invoke(url, "");
// Check to see if there is an error, return "N/A"
if (soapResponse.generatedFault()) {
Fault fault = soapResponse.getFault();
String f = fault.getFaultString();
return f;
} else {
// read result
Parameter soapResult = soapResponse.getReturnValue();
// get a string from the result
return soapResult.getValue().toString();
}
} catch (SOAPException se) {
return se.getMessage();
}
}
}
3,使用wsdl2java把WSDL檔案轉成本地類,然後像本地類一樣使用,即可。
這是像我這種懶人最喜歡的方式,仍然以前面的global weather report為例。
首先 java org.apache.axis.wsdl.WSDL2Java http://www.webservicex.net/globalweather.asmx.WSDL
原本的網址是http://www.webservicex.net/globalweather.asmx?WSDL,中間個各問号,但是Linux下面它不能解析,是以去掉問号,改為點号。
那麼就會出現4個檔案:
GlobalWeather.java
GlobalWeatherLocator.java
GlobalWeatherSoap.java
GlobalWeatherSoapStub.java
其中GlobalWeatherSoap.java是我們最為關心的接口檔案,如果你對RMI等SOAP實作的具體細節不感興趣,那麼你隻需要看接口檔案即可,
在使用的時候,引入這個接口即可,就好像使用本地類一樣。
一:webService介紹
1.什麼是webService
webService是一種使用http傳輸SOAP協定資料的遠端調用技術
2.webService三要素
SOAP:規範XML标簽
WSDL:服務端的使用說明書
UDDI:目錄

二:webService入門小程式
1.服務端
(1)、開發步驟
A、建立接口
package com.webservice.jaxws;
public interface WeatherService {
//查詢天氣的方法
public String queryWeather(String cityName);
}
B、建立實作類,在實作類上加入@WebService注解,該注解的作用是辨別該實作類是webservice的服務類,釋出該實作類中的public方法
package com.webservice.jaxws;
import javax.jws.WebService;
/**
* 天氣查詢的實作類
* @author Administrator
*
*/
@WebService
public class WeatherServiceImpl implements WeatherService {
//查詢天氣
public String queryWeather(String cityName) {
System.out.println(cityName + "天氣是:晴天");
return "晴";
}
}
C、釋出服務,使用EndPoint類中的publish()方法釋出,參數分别為服務通路的位址和服務的實作類
package com.webservice.jaxws;
import javax.jws.WebService;
/**
* 天氣查詢的實作類
* @author Administrator
*
*/
@WebService
public class WeatherServiceImpl implements WeatherService {
//查詢天氣
public String queryWeather(String cityName) {
System.out.println(cityName + "天氣是:晴天");
return "晴";
}
}
D、測試服務是否釋出成功,閱讀使用說明書,确認要調用的類、方法、參數等
● WSDL通路位址:
http://localhost:12345/weather?wsdl
● WSDL說明書閱讀方式:從下往上閱讀
E、如何釋出SOAP1.2版本的服務端
● 引入第三方jar包
● 在服務實作類上加入注解 @BindingType(SOAPBinding.SOAP12HTTP_BINDING)
package com.webservice.jaxws;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;
/**
* 天氣查詢的實作類
* @author Administrator
*
*/
@WebService
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public class WeatherServiceImpl implements WeatherService {
//查詢天氣
public String queryWeather(String cityName) {
System.out.println(cityName + "天氣是:晴天");
return "晴";
}
}
2.用戶端
(1)、開發步驟
A、在工作空間建立用于存放使用wsimport指令生成的用戶端代碼的java工程
B、使用jdk提供的wsimport指令生成用戶端代碼
● wsimport指令是jdk提供的,作用是根據使用說明書生成用戶端代碼,wsimport隻支援SOAP1.1用戶端的生成
● wsimport常用參數
-d:預設參數,用于生成.class檔案
-s:生成.java檔案
-p:指定生成java檔案的包名,不指定則為WSDL說明書中namespace值得倒寫
C、在doc視窗進入java工程項目的src目錄,執行wsimport指令
D、在Eclipse中重新整理java項目,将生成的用戶端代碼copy到用戶端工程中
E、建立服務視圖,類名從<service>标簽的name屬性擷取
F、擷取服務實作類,視圖執行個體調用getProt()方法,實作類的類名從portType的name屬性擷取
G、調用查詢方法,方法名從portType下的operation标簽的name屬性擷取
package com.webservice.client;
import com.webservice.jaxws.WeatherServiceImpl;
import com.webservice.jaxws.WeatherServiceImplService;
public class Client {
public static void main(String[] args) {
//建立視圖
WeatherServiceImplService wsis = new WeatherServiceImplService();
//擷取服務實作類
WeatherServiceImpl wsi = wsis.getPort(WeatherServiceImpl.class);
//調用查詢方法
String weather = wsi.queryWeather("北京");
System.out.println(weather);
}
}
三:webService三要素詳解
1.WSDL
(1)、定義
WSDL即web服務描述語言,它是服務端的使用說明書,是XML格式的文檔,說明服務位址、服務類、方法、參數和傳回值,是伴随服務釋出成功,自動生成的
(2)、文檔結構
● <service> 服務視圖,webservice的服務結點,它包括了服務端點
● <binding> 為每個服務端點定義消息格式和協定細節
● <portType> 服務端點,描述 web service可被執行的操作方法,以及相關的消息,通過binding指向portType
● <message> 定義一個操作(方法)的資料參數(可有多個參數)
● <types> 定義 web service 使用的全部資料類型
2.SOAP
(1)、定義
SOAP即簡單對象通路協定(Simple Object Access Protocol),使用http發送XML格式的資料,他不是webservice的專有協定
(2)、結構 SOAP = HTTP + XML
(3)、協定的格式
Envelope:必須有,此元素将整個 XML 文檔辨別為一條SOAP消息
Header:可選元素,包含頭部資訊
Body:必須有,包含所有調用和響應資訊
Fault:可選元素,提供有關在處理此消息時所發生的錯誤資訊
(4)、版本
A、SOAP1.1
● 請求
POST /weather HTTP/1.1
Accept: text/xml, multipart/related
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://jaxws.ws.itcast.cn/WeatherInterfaceImpl/queryWeatherRequest"
User-Agent: JAX-WS RI 2.2.4-b01
Host: 127.0.0.1:54321
Connection: keep-alive
Content-Length: 211
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body><ns2:queryWeather xmlns:ns2="http://jaxws.ws.itcast.cn/"><arg0>北京</arg0></ns2:queryWeather>
</S:Body>
</S:Envelope>
● 響應
HTTP/1.1 200 OK
Transfer-encoding: chunked
Content-type: text/xml; charset=utf-8
Date: Fri, 04 Dec 2015 03:45:56 GMT
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:queryWeatherResponse xmlns:ns2="http://jaxws.ws.itcast.cn/"><return>晴</return></ns2:queryWeatherResponse>
</S:Body>
</S:Envelope>
B、SOAP1.2
● 請求
POST /weather HTTP/1.1
Accept: application/soap+xml, multipart/related
Content-Type: application/soap+xml; charset=utf-8;
action="http://jaxws.ws.itcast.cn/WeatherInterfaceImpl/queryWeatherRequest"
User-Agent: JAX-WS RI 2.2.4-b01
Host: 127.0.0.1:54321
Connection: keep-alive
Content-Length: 209
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">
<S:Body><ns2:queryWeather xmlns:ns2="http://jaxws.ws.itcast.cn/"><arg0>北京</arg0></ns2:queryWeather>
</S:Body>
</S:Envelope>
● 響應
HTTP/1.1 200 OK
Transfer-encoding: chunked
Content-type: application/soap+xml; charset=utf-8
Date: Fri, 04 Dec 2015 03:55:49 GMT
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">
<S:Body>
<ns2:queryWeatherResponse xmlns:ns2="http://jaxws.ws.itcast.cn/"><return>晴</return></ns2:queryWeatherResponse>
</S:Body>
</S:Envelope>
C、SOAP1.1 和 SOAP1.2的差別
● 相同點
請求方式都是POST
協定格式都一樣,都有envelope和body
● 不同點
①、資料格式不同
SOAP1.1:text/xml;charset=utf-8
SOAP1.2:application/soap+xml;charset=utf-8
②、命名空間不同
四:webservice用戶端的四種調用方式
1.生成用戶端調用方式
(1)、開發步驟
A、wisimport生成用戶端代碼
B、建立服務視圖
C、擷取實作類
D、調用查詢方法
2.service程式設計實作調用
(1)、開發步驟
A、wisimport生成用戶端代碼
B、使用serivce類建立服務視圖
C、擷取服務實作類
D、調用查詢方法
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import cn.itcast.mobile.MobileCodeWSSoap;
/**
*
* <p>Title: ServiceClient.java</p>
* <p>Description:Service程式設計實作用戶端</p>
*/
public class ServiceClient {
public static void main(String[] args) throws IOException {
//建立WSDL位址,不是服務位址
URL url = new URL("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl");
//建立服務名稱
//1.namespaceURI - 命名空間位址
//2.localPart - 服務名稱
QName qname = new QName("http://WebXml.com.cn/", "MobileCodeWS");
//Service建立視圖
//參數:
//1.wsdlDocumentLocation - 使用說明書位址
//2.serviceName - 服務名稱
Service service = Service.create(url, qname);
//擷取實作類
MobileCodeWSSoap mobileCodeWSSoap = service.getPort(MobileCodeWSSoap.class);
//調用查詢方法
String result = mobileCodeWSSoap.getMobileCodeInfo("188888888", "");
System.out.println(result);
}
}
特點:友善管理,是一個标準的開發方式
3.HttpURLConnection調用方式
(1)、開發步驟
A、建立服務位址
B、打開服務位址的一個連接配接
C、設定連接配接參數
● 注意
a、POST必須大寫,如果小寫會出如下異常:
b、如果不設定輸入輸出,會報如下異常:
D、組織SOAP協定資料,發送給伺服器
E、接收服務端的響應
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
*
* <p>Title: HttpClient.java</p>
* <p>Description:HttpURLConnection調用方式</p>
*/
public class HttpClient {
public static void main(String[] args) throws IOException {
//1:建立服務位址
URL url = new URL("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx");
//2:打開到服務位址的一個連接配接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//3:設定連接配接參數
//3.1設定發送方式:POST必須大寫
connection.setRequestMethod("POST");
//3.2設定資料格式:Content-type
connection.setRequestProperty("content-type", "text/xml;charset=utf-8");
//3.3設定輸入輸出,新建立的connection預設是沒有讀寫權限的,
connection.setDoInput(true);
connection.setDoOutput(true);
//4:組織SOAP協定資料,發送給服務端
String soapXML = getXML("1866666666");
OutputStream os = connection.getOutputStream();
os.write(soapXML.getBytes());
//5:接收服務端的響應
int responseCode = connection.getResponseCode();
if(200 == responseCode){//表示服務端響應成功
InputStream is = connection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String temp = null;
while(null != (temp = br.readLine())){
sb.append(temp);
}
System.out.println(sb.toString());
is.close();
isr.close();
br.close();
}
os.close();
}
/**
* <?xml version="1.0" encoding="utf-8"?>
<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/">
<soap:Body>
<getMobileCodeInfo xmlns="http://WebXml.com.cn/">
<mobileCode>string</mobileCode>
<userID>string</userID>
</getMobileCodeInfo>
</soap:Body>
</soap:Envelope>
* @param phoneNum
* @return
*/
public static String getXML(String phoneNum){
String soapXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+"<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/\">"
+"<soap:Body>"
+"<getMobileCodeInfo xmlns=\"http://WebXml.com.cn/\">"
+"<mobileCode>"+phoneNum+"</mobileCode>"
+"<userID></userID>"
+"</getMobileCodeInfo>"
+" </soap:Body>"
+"</soap:Envelope>";
return soapXML;
}
}
4.Ajax調用方式
<!doctype html>
<html >
<head>
<title>Ajax調用方式</title>
<script type="text/javascript">
function queryMobile(){
//建立XMLHttpRequest對象
var xhr = new XMLHttpRequest();
//打開連結
xhr.open("post","http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx",true);
//設定content-type
xhr.setRequestHeader("content-type","text/xml;charset=utf-8");
//設定回調函數
xhr.onreadystatechange=function(){
//判斷用戶端發送成功&&服務端響應成功
if(4 == xhr.readyState && 200 == xhr.status){
alert(xhr.responseText);
}
}
//組織SOAP協定資料
var soapXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+"<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/\">"
+"<soap:Body>"
+"<getMobileCodeInfo xmlns=\"http://WebXml.com.cn/\">"
+"<mobileCode>"+document.getElementById("phoneNum").value+"</mobileCode>"
+"<userID></userID>"
+"</getMobileCodeInfo>"
+" </soap:Body>"
+"</soap:Envelope>";
alert(soapXML);
//發送請求
xhr.send(soapXML);
}
</script>
</head>
<body>
手機号歸屬地查詢:<input type="text" id="phoneNum" /><input type="button" value="查詢" onclick="javascript:queryMobile();"/>
</body>
</html>
五、深入開發:用注解修改WSDL内容
1.WebService的注解都位于javax.jws包下:
@WebService-定義服務,在public class上邊
targetNamespace:指定命名空間
name:portType的名稱
portName:port的名稱
serviceName:服務名稱
endpointInterface:SEI接口位址,如果一個服務類實作了多個接口,隻需要釋出一個接口的方法,可通過此注解指定要釋出服務的接口。
@WebMethod-定義方法,在公開方法上邊
operationName:方法名
exclude:設定為true表示此方法不是webservice方法,反之則表示webservice方法
@WebResult-定義傳回值,在方法傳回值前邊
name:傳回結果值的名稱
@WebParam-定義參數,在方法參數前邊
name:指定參數的名稱
作用:
通過注解,可以更加形像的描述Web服務。對自動生成的wsdl文檔進行修改,為使用者提供一個更加清晰的wsdl文檔。
當修改了WebService注解之後,會影響用戶端生成的代碼。調用的方法名和參數名也發生了變化,必須重新生成用戶端代碼
示例:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;
/**
*
* <p>Title: WeatherInterfaceImpl.java</p>
* <p>Description:SEI實作類</p>
*/
@WebService(
targetNamespace="http://service.itcast.cn",
name="WeatherWSSoap",
portName="WeatherWSSoapPort",
serviceName="WeatherWS"
)
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public class WeatherInterfaceImpl implements WeatherInterface {
@WebMethod(
operationName="getWeather",
exclude=false
)
@Override
public @WebResult(name="result")String queryWeather(@WebParam(name="cityName")String cityName) {
System.out.println("from client..."+cityName);
String weather = "晴";
return weather;
}
}