前段時間項目需求快速開發了一個webservice的服務端,看到此文很不錯故轉載。
合抱之木,生於毫末。九層之臺,起於累土。千里之行,始於足下
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介紹
webService是一種使用http傳輸SOAP協議數據的遠程調用技術
SOAP:規範XML標籤
WSDL:服務端的使用說明書
UDDI:目錄
二:webService入門小程序
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.xml.ws.Endpoint;
public class ServerPoint {
public static void main(String[] args) {
//參數1:服務地址,weather爲服務的名稱
//參數2:服務實現類
Endpoint.publish("http://127.0.0.1:12345/weather", new WeatherServiceImpl());
}
}
D、測試服務是否發佈成功,閱讀使用說明書,確認要調用的類、方法、參數等
● WSDL訪問地址:
http://localhost:12345/weather?wsdl
● WSDL說明書閱讀方式:從下往上閱讀
● 引入第三方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 "晴";
}
}
A、在工作空間創建用於存放使用wsimport命令生成的客戶端代碼的java工程
● wsimport命令是jdk提供的,作用是根據使用說明書生成客戶端代碼,wsimport只支持SOAP1.1客戶端的生成
-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三要素詳解
WSDL即web服務描述語言,它是服務端的使用說明書,是XML格式的文檔,說明服務地址、服務類、方法、參數和返回值,是伴隨服務發佈成功,自動生成的
● <service> 服務視圖,webservice的服務結點,它包括了服務端點
● <binding> 爲每個服務端點定義消息格式和協議細節
● <portType> 服務端點,描述 web service可被執行的操作方法,以及相關的消息,通過binding指向portType
● <message> 定義一個操作(方法)的數據參數(可有多個參數)
● <types> 定義 web service 使用的全部數據類型
SOAP即簡單對象訪問協議(Simple Object Access Protocol),使用http發送XML格式的數據,他不是webservice的專有協議
Envelope:必須有,此元素將整個 XML 文檔標識爲一條SOAP消息
Header:可選元素,包含頭部信息
Body:必須有,包含所有調用和響應信息
Fault:可選元素,提供有關在處理此消息時所發生的錯誤信息
● 請求
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>
● 請求
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>
● 相同點
請求方式都是POST
協議格式都一樣,都有envelope和body
● 不同點
①、數據格式不同
SOAP1.1:text/xml;charset=utf-8
SOAP1.2:application/soap+xml;charset=utf-8
②、命名空間不同
四:webservice客戶端的四種調用方式
A、wisimport生成客戶端代碼
B、創建服務視圖
C、獲取實現類
D、調用查詢方法
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);
}
}
特點:方便管理,是一個標準的開發方式
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;
}
}
<!doctype html>
<html lang="en">
<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="查詢" οnclick="javascript:queryMobile();"/>
</body>
</html>
五、深入開發:用註解修改WSDL內容
1.WebService的註解都位於javax.jws包下:
@WebService-定義服務,在public class上邊
targetNamespace:指定命名空間
name:portType的名稱
portName:port的名稱
serviceName:服務名稱
endpointInterface:SEI接口地址,如果一個服務類實現了多個接口,只需要發佈一個接口的方法,可通過此註解指定要發佈服務的接口。
operationName:方法名
exclude:設置爲true表示此方法不是webservice方法,反之則表示webservice方法
name:返回結果值的名稱
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;
}
}