java調(diào)用WebService服務(wù)的四種方法總結(jié)
一、前言
本來(lái)不想寫(xiě)這個(gè)的,因?yàn)榫W(wǎng)上類(lèi)似的是在是太多了。但是想想自己前面段時(shí)間用過(guò),而且以后可能再也沒(méi)機(jī)會(huì)用了。所以還是記錄一下吧。我這兒是以C語(yǔ)言生成的WebService為例。因?yàn)橥ǔ?lái)說(shuō),兩個(gè)java端之間的互相通訊沒(méi)必要寫(xiě)成WebService的方式,太麻煩。除非有一方已經(jīng)固定了是webService的方式(常見(jiàn)于牛逼的甲方)。而且就算寫(xiě)成了WebService方式兩個(gè)java端直接調(diào)用也相對(duì)比較簡(jiǎn)單,因?yàn)橛胘ava的話很多規(guī)范都是自動(dòng)生成好的,而其他語(yǔ)言就不是這樣了,有時(shí)候?qū)Ψ綁焊筒皇钦_的規(guī)范,你還不能讓對(duì)方改?。。。。∥矣X(jué)得webService這個(gè)東西常用于不同語(yǔ)言編寫(xiě)的服務(wù)器之間進(jìn)行數(shù)據(jù)交互。因?yàn)槭腔赪SDL的。我知道的主要的方法有以下幾種(我這兒全部以C語(yǔ)言編寫(xiě)的WebService做優(yōu)缺點(diǎn)比較):
二、簡(jiǎn)介 ?
1、通過(guò)axis2將WebService提供的wsdl文件生成對(duì)應(yīng)的java類(lèi),這樣就可以相當(dāng)調(diào)用本地類(lèi)一樣調(diào)用webService提供的接口。
??? 優(yōu)點(diǎn):調(diào)用簡(jiǎn)單,無(wú)需自己編寫(xiě)太多的東西。
??? 缺點(diǎn):大部分情況根據(jù)對(duì)應(yīng)的webService生成的服務(wù)中地址是固定的,不易更改,而且生成的代碼過(guò)于龐大 ,不便于閱讀。同時(shí)必須得有webservice對(duì)應(yīng)的的wsdl文件,不太可控。
2、通過(guò)RPC方式調(diào)用(推薦使用)
??? 優(yōu)點(diǎn):自己編寫(xiě)部分調(diào)用代碼,可靈活更換調(diào)用的路徑,適合分布式部署的服務(wù)器,只需要知道webservice服務(wù)的方法名、命名空間、以及對(duì)應(yīng)的參數(shù)就好。
??? 缺點(diǎn):部分特殊情況下可能可以調(diào)用成功,但是無(wú)法獲取返回值。稍后會(huì)進(jìn)行說(shuō)明。
3、通過(guò)HttpURLConnection進(jìn)行調(diào)用,可用于補(bǔ)充第二種方法的不足之處。
??? 優(yōu)點(diǎn):補(bǔ)充RPC方式的不足,代碼編寫(xiě)量較少。
??? 缺點(diǎn):(C語(yǔ)言的WebService服務(wù))大部分時(shí)候要自己編寫(xiě)輸入的報(bào)文頭,自己解析返回的報(bào)文。需要事先抓包查看報(bào)文。
4、通過(guò)httpclient調(diào)用。
??? 和HttpURLConnection原理一樣,只是用不同方法實(shí)現(xiàn)。優(yōu)缺點(diǎn)也差不多。
三、具體解析
第一種方式,首先得下載axis2的jar包,Axis2提供了一個(gè)wsdl2java.bat命令可以根據(jù)WSDL文件自動(dòng)產(chǎn)生調(diào)用WebService的代碼。
?wsdl2java.bat命令可以在<Axis2安裝目錄>/bin目錄中找到。如果你配置了環(huán)境變量則可以在控制臺(tái)中用一下方式
環(huán)境變量\bin\wsdl2java,具體如下。
%AXIS2_HOME%\bin\wsdl2java -uri d:demo.wsdl -p client -s -o stub
如果沒(méi)有則自己鍵入到對(duì)應(yīng)的位置執(zhí)行。wsdl2java -uri d:demo.wsdl -p client -s -o stub
其中,-url是對(duì)應(yīng)WebService的wsdl位置,可以是本地的也可以是網(wǎng)絡(luò)的。-p是指定生成的類(lèi)名。具體參數(shù)列表如下:
- -o <path> : 指定生成代碼的輸出路徑
- -a : 生成異步模式的代碼
- -s : 生成同步模式的代碼
- -p <pkg> : 指定代碼的package名稱(chēng)
- -l <languange> : 使用的語(yǔ)言(Java/C) 默認(rèn)是java
- -t : 為代碼生成測(cè)試用例
- -ss : 生成服務(wù)端代碼 默認(rèn)不生成
- -sd : 生成服務(wù)描述文件 services.xml,僅與-ss一同使用
- -d <databinding> : 指定databingding,例如,adb,xmlbean,jibx,jaxme and jaxbri
- -g : 生成服務(wù)端和客戶端的代碼
- -pn <port_name> : 當(dāng)WSDL中有多個(gè)port時(shí),指定其中一個(gè)port
- -sn <serv_name> : 選擇WSDL中的一個(gè)service
- -u : 展開(kāi)data-binding的類(lèi)
- -r <path> : 為代碼生成指定一個(gè)repository
- -ssi : 為服務(wù)端實(shí)現(xiàn)代碼生成接口類(lèi)
- -S : 為生成的源碼指定存儲(chǔ)路徑
- -R : 為生成的resources指定存儲(chǔ)路徑
–noBuildXML : 輸出中不生成build.xml文件
–noWSDL : 在resources目錄中不生成WSDL文件
–noMessageReceiver : 不生成MessageReceiver類(lèi)
生成完后可以在axis2的bin目錄下找到對(duì)應(yīng)的文件。文件和同類(lèi).java文件要大很多,并且調(diào)用路徑是定死的(標(biāo)紅部分),改起來(lái)麻煩,反正我是不喜歡這種方式。雖然不要自己寫(xiě),但是看著這么多行就不爽,太臃腫了。


調(diào)用方式如下。(方式應(yīng)該有多種,沒(méi)有去深入研究)
package client;
import javax.xml.namespace.QName;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;
public class TestAms {
public static void main(String[] args) throws Exception
{
AmsStub1 stub=new AmsStub1();
AmsStub1.SetAlarmServerCfgMsg setmsg= new AmsStub1.SetAlarmServerCfgMsg();
//ServiceStub.SetAlarmServerCfgMsgResponse re=new ServiceStub.SetAlarmServerCfgMsgResponse();
String str="{\"name\":\"demo\",\"id\":21,\"code\":\"161021021040288690\"}";//對(duì)應(yīng)的參數(shù)
setmsg.setPAlarmCfgMsg(str); //設(shè)置參數(shù)
String re=stub.setAlarmServerCfgMsg(setmsg).getResponse(); //調(diào)用并獲取返回值
System.out.println(re);
}
}
我工作中遇到的全是C語(yǔ)言編寫(xiě)的WebService,而且每個(gè)人編寫(xiě)的都有些區(qū)別,很多人給你的wsdl并不能直接成功生成對(duì)應(yīng)的java類(lèi),而且這種方法還有上述的一些缺點(diǎn),所以我拋棄了這種方法。(上述列的代碼全是以前試驗(yàn)時(shí)用過(guò)的,那時(shí)候是成功的,寫(xiě)這個(gè)博客的時(shí)候我把所有能找到的wsdl全試著生成一遍,結(jié)果全部生成失敗。心塞)。
第二種RPC 方式,強(qiáng)烈推薦。
這種方式不多說(shuō),直接看代碼就懂了 。這是一個(gè)調(diào)用webService查詢?cè)O(shè)備在線數(shù)的。
public String getOnline(String url){
int errCode=0;
JSONObject resultJson=new JSONObject();
String result="";
Service service = new Service();
Call call;
try {
call=(Call) service.createCall();
QName opAddEntry = new QName("urn:demo", "GetOnlineInfo"); //設(shè)置命名空間和需要調(diào)用的方法名
call.setTargetEndpointAddress(url); //設(shè)置請(qǐng)求路徑
call.setOperationName("GetNcgOnlineInfo"); //調(diào)用的方法名
call.setTimeout(Integer.valueOf(2000)); //設(shè)置請(qǐng)求超時(shí)
call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);//設(shè)置返回類(lèi)型
result= (String) call.invoke(opAddEntry,new Object[]{});
} catch (ServiceException e) {
// TODO Auto-generated catch block
System.out.println("查詢?cè)诰€狀態(tài)1:"+e.getMessage());
errCode=1;
} catch (RemoteException e) {
// TODO Auto-generated catch block
System.out.println("查詢?cè)诰€狀態(tài)2:"+e.getMessage());
errCode=2;
}
resultJson.put("errCode", errCode);
resultJson.put("data", result);
return resultJson.toString();
}
里面注釋比較全。還有些別的設(shè)置也比較簡(jiǎn)單,自己琢磨就知道了。例如編碼方式、解析時(shí)間等。
說(shuō)說(shuō)這種方式的問(wèn)題吧。我在使用的時(shí)候遇到的是:和我對(duì)接的人編寫(xiě)了兩個(gè)WebService。但是由于這兩個(gè)中有許多部分是相同的,他就把這兩個(gè)合并了,同時(shí)提供了兩個(gè)命名空間(具體怎么操作的我也不清楚),那么問(wèn)題了,這其中有一個(gè)命名空間的所有方法我都能成功調(diào)用,但是都無(wú)法收到返回值。當(dāng)時(shí)我就方了,開(kāi)始還是好好的,怎么就突然不行了,于是我繼續(xù)執(zhí)行,查看報(bào)錯(cuò)消息,同時(shí)抓包查看報(bào)文內(nèi)容。終于給我發(fā)現(xiàn)了問(wèn)題。
下圖是返回結(jié)果報(bào)的錯(cuò),大體意識(shí)就是說(shuō)我設(shè)置的命名空間和對(duì)方的命名空間不匹配。然后RPC解析就失敗了。

然后我利用Wireshark抓包,得到一下結(jié)果??梢钥纯闯?,我請(qǐng)求的是命名空間是 ns1="urn:ncg"(其余的都是wsdl默認(rèn)自帶的)??墒俏沂盏降姆祷貓?bào)文就變了。變成了這樣的? xmlns:dag="http://tempuri.org/dag.xsd" xmlns:dag="urn:dag" xmlns:ncg="urn:ncg"? 足足有三個(gè)啊。RPC按照默認(rèn)設(shè)置的 ns1="urn:ncg" 去解析,那肯定什么都解析不了的。所以只有自己去解析了。這種情況可以利用第三種或者第四種方式進(jìn)行調(diào)用。

第三種:利用HttpURLConnection拼接和解析報(bào)文進(jìn)行調(diào)用。
還是上面那個(gè)查詢?cè)O(shè)備的方法。只不過(guò)改了下。當(dāng)然,我這是知道報(bào)文后的解決辦法。
public String ncgConnection(String url,String method){
URL wsUrl;
int errCode=0;
JSONObject resultJson=new JSONObject();
String result="";
try {
wsUrl = new URL(url+"/"+method);
HttpURLConnection conn = (HttpURLConnection) wsUrl.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "text/xml;charset=UTF-8");
conn.setConnectTimeout(2000);
conn.setReadTimeout(2000);
OutputStream os = conn.getOutputStream();
//請(qǐng)求體
//<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:DeleteCascadeFromCms soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="urn:ncg"><ncg-code-list xsi:type="xsd:string">["11241525"]</ncg-code-list></ns1:DeleteCascadeFromCms></soapenv:Body></soapenv:Envelope>
String soap = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><soapenv:Body><ns1:"+method+" soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:ns1=\"urn:ncg\"/></soapenv:Body></soapenv:Envelope>";
os.write(soap.getBytes());
InputStream is = conn.getInputStream();
byte[] b = new byte[1024];
int len = 0;
String s = "";
while((len = is.read(b)) != -1){
String ss = new String(b,0,len,"UTF-8");
s += ss;
}
result=s.split("<response xsi:type=\"xsd:string\">")[1].split("</response>")[0];
is.close();
os.close();
conn.disconnect();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
System.out.println("通訊模塊1:"+e.getMessage());
errCode=1;
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("通訊模塊2:"+e.getMessage());
errCode=2;
}
resultJson.put("errCode", errCode);
resultJson.put("data", result);
return resultJson.toString();
}
正常來(lái)說(shuō),利用HttpURLConnection實(shí)現(xiàn)很多的調(diào)用不需要自己拼接請(qǐng)求頭和解析返回結(jié)果的(例如java端提供的一些action或者controller),可是在這兒調(diào)用WebService,確確實(shí)實(shí)的需要自己手寫(xiě)。對(duì)比上面那個(gè)Wireshark抓包的結(jié)果可以發(fā)現(xiàn),在請(qǐng)求體部分按照對(duì)方提供的wsdl進(jìn)行拼接,結(jié)果部分也進(jìn)行相同的解析??梢哉_獲得結(jié)果。

第四種,利用httpclient
簡(jiǎn)單來(lái)說(shuō),httpClient可以算是加強(qiáng)版的HttpURLConnection,httpClient的API比較多,也比較穩(wěn)定,不容易擴(kuò)展。HttpURLConnection比較輕量級(jí),容易根據(jù)自己的需求進(jìn)行擴(kuò)展。但是穩(wěn)定性不如httpClient。
這種方法具體實(shí)現(xiàn)思路和HttpURLConnection一樣。只是有點(diǎn)小區(qū)別。代碼如下:
public void demo(String url){
HttpClient httpClient=new HttpClient();
PostMethod postMethod=new PostMethod();
postMethod.setPath(url+"/ncg.wsdl"); //路徑和wsdl名
String soap = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><soapenv:Body><ns1:GetNcgOnlineInfo soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:ns1=\"urn:ncg\"/></soapenv:Body></soapenv:Envelope>";
try {
byte[] b=soap.getBytes("utf-8");
InputStream is = new ByteArrayInputStream(b, 0, b.length);
RequestEntity re = new InputStreamRequestEntity(is, b.length,
"application/soap+xml; charset=utf-8");
postMethod.setRequestEntity(re);
int statusCode = httpClient.executeMethod(postMethod);
String soapResponseData = postMethod.getResponseBodyAsString();
postMethod.releaseConnection();
//解析
System.out.println(soapResponseData.split("<response xsi:type=\"xsd:string\">")[1].split("</response>")[0]);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
結(jié)果:我這兒沒(méi)有做更多的判斷,直接輸出,這種方式我以前其實(shí)并沒(méi)有用到。如果有需要可以更具返回的狀態(tài)判斷是否成功。如果你去抓包的話,你會(huì)發(fā)現(xiàn)這個(gè)會(huì)和上面HttpURLConnection抓的一樣。
????
總結(jié):調(diào)用webService很多程度上需要依賴(lài)對(duì)方編寫(xiě)WebService是否嚴(yán)謹(jǐn),如果足夠嚴(yán)謹(jǐn),推薦使用RPC方式編寫(xiě),其余的更具實(shí)際情況進(jìn)行選擇
總結(jié)
到此這篇關(guān)于java調(diào)用WebService服務(wù)的四種方法的文章就介紹到這了,更多相關(guān)java調(diào)用WebService服務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC REST風(fēng)格深入詳細(xì)講解
這篇文章主要介紹了SpringMVC REST風(fēng)格,Rest全稱(chēng)為Representational State Transfer,翻譯為表現(xiàn)形式狀態(tài)轉(zhuǎn)換,它是一種軟件架構(gòu)2022-10-10
SpringCloud Feign配置應(yīng)用詳細(xì)介紹
這篇文章主要介紹了SpringCloud Feign配置應(yīng)用,feign是netflix提供的服務(wù)間基于http的rpc調(diào)用框架,在spring cloud得到廣泛應(yīng)用2022-09-09
Spring?AOP手寫(xiě)動(dòng)態(tài)代理代碼實(shí)例
這篇文章主要介紹了Spring?AOP手寫(xiě)動(dòng)態(tài)代理代碼實(shí)例,AOP我們知道,是在不修改源代碼的情況下,為代碼添加一些新功能的技術(shù),通過(guò)動(dòng)態(tài)代理,可以在不修改原始類(lèi)代碼的前提下,對(duì)方法進(jìn)行攔截和增強(qiáng),需要的朋友可以參考下2024-01-01
spring學(xué)習(xí)之參數(shù)傳遞與檢驗(yàn)詳解
這篇文章主要給大家介紹了關(guān)于spring參數(shù)傳遞與檢驗(yàn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-07-07
SpringBoot集成Redis實(shí)現(xiàn)消息隊(duì)列的方法
這篇文章主要介紹了SpringBoot集成Redis實(shí)現(xiàn)消息隊(duì)列的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
Java設(shè)計(jì)模式之橋梁(Bridge)模式
這篇文章主要介紹了Java設(shè)計(jì)模式之橋梁(Bridge)模式,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)Java設(shè)計(jì)模式的小伙伴們有很好的幫助,需要的朋友可以參考下2021-05-05
使用Sentinel滑動(dòng)窗口實(shí)現(xiàn)限流和降級(jí)
Sentinel 是一個(gè)開(kāi)源的高可用性、高擴(kuò)展性的實(shí)時(shí)流量控制框架,它可以用于保護(hù)服務(wù)穩(wěn)定性,防止系統(tǒng)因?yàn)榱髁窟^(guò)大而崩潰,這篇文章我們所介紹的是滑動(dòng)窗口,它是 Sentinel 實(shí)現(xiàn)限流和降級(jí)的重要組件之一,感興趣的同學(xué)跟著小編來(lái)看看吧2023-09-09
SpringBoot 整合 Shiro 密碼登錄與郵件驗(yàn)證碼登錄功能(多 Realm 認(rèn)證)
這篇文章主要介紹了SpringBoot 整合 Shiro 密碼登錄與郵件驗(yàn)證碼登錄(多 Realm 認(rèn)證),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02

