1. 程式人生 > 其它 >java呼叫WebService服務的四種方法總結

java呼叫WebService服務的四種方法總結

 

轉自:https://www.jb51.net/article/230317.htm

WebService是一種跨程式語言、跨作業系統平臺的遠端呼叫技術,已存在很多年了,很多介面也都是通過WebService方式來發布的,下面這篇文章主要給大家介紹了關於java呼叫WebService服務的四種方法,需要的朋友可以參考下

 
目錄

一、前言

本來不想寫這個的,因為網上類似的是在是太多了。但是想想自己前面段時間用過,而且以後可能再也沒機會用了。所以還是記錄一下吧。我這兒是以C語言生成的WebService為例。因為通常來說,兩個java端之間的互相通訊沒必要寫成WebService的方式,太麻煩。除非有一方已經固定了是webService的方式(常見於牛逼的甲方)。而且就算寫成了WebService方式兩個java端直接呼叫也相對比較簡單,因為用java的話很多規範都是自動生成好的,而其他語言就不是這樣了,有時候對方壓根就不是正確的規範,你還不能讓對方改!!!!!我覺得webService這個東西常用於不同語言編寫的伺服器之間進行資料互動。因為是基於WSDL的。我知道的主要的方法有以下幾種(我這兒全部以C語言編寫的WebService做優缺點比較):

二、簡介  

1、通過axis2將WebService提供的wsdl檔案生成對應的java類,這樣就可以相當呼叫本地類一樣呼叫webService提供的介面。

    優點:呼叫簡單,無需自己編寫太多的東西。

    缺點:大部分情況根據對應的webService生成的服務中地址是固定的,不易更改,而且生成的程式碼過於龐大 ,不便於閱讀。同時必須得有webservice對應的的wsdl檔案,不太可控。

2、通過RPC方式呼叫(推薦使用)

    優點:自己編寫部分呼叫程式碼,可靈活更換呼叫的路徑,適合分散式部署的伺服器,只需要知道webservice服務的方法名、名稱空間、以及對應的引數就好。

    缺點:部分特殊情況下可能可以呼叫成功,但是無法獲取返回值。稍後會進行說明。

3、通過HttpURLConnection進行呼叫,可用於補充第二種方法的不足之處。

    優點:補充RPC方式的不足,程式碼編寫量較少。

    缺點:(C語言的WebService服務)大部分時候要自己編寫輸入的報文頭,自己解析返回的報文。需要事先抓包檢視報文。

4、通過httpclient呼叫。

    和HttpURLConnection原理一樣,只是用不同方法實現。優缺點也差不多。

三、具體解析

第一種方式,首先得下載axis2的jar包,Axis2提供了一個wsdl2java.bat命令可以根據WSDL檔案自動產生呼叫WebService的程式碼。

 wsdl2java.bat命令可以在<Axis2安裝目錄>/bin目錄中找到。如果你配置了環境變數則可以在控制檯中用一下方式

環境變數\bin\wsdl2java,具體如下。

%AXIS2_HOME%\bin\wsdl2java -uri d:demo.wsdl -p client -s -o stub

如果沒有則自己鍵入到對應的位置執行。wsdl2java -uri d:demo.wsdl -p client -s -o stub

其中,-url是對應WebService的wsdl位置,可以是本地的也可以是網路的。-p是指定生成的類名。具體引數列表如下:

  • -o <path> : 指定生成程式碼的輸出路徑
  • -a : 生成非同步模式的程式碼
  • -s : 生成同步模式的程式碼
  • -p <pkg> : 指定程式碼的package名稱
  • -l <languange> : 使用的語言(Java/C) 預設是java
  • -t : 為程式碼生成測試用例
  • -ss : 生成服務端程式碼 預設不生成
  • -sd : 生成服務描述檔案 services.xml,僅與-ss一同使用
  • -d <databinding> : 指定databingding,例如,adb,xmlbean,jibx,jaxme and jaxbri
  • -g : 生成服務端和客戶端的程式碼
  • -pn <port_name> : 當WSDL中有多個port時,指定其中一個port
  • -sn <serv_name> : 選擇WSDL中的一個service
  • -u : 展開data-binding的類
  • -r <path> : 為程式碼生成指定一個repository
  • -ssi : 為服務端實現程式碼生成介面類
  • -S : 為生成的原始碼指定儲存路徑
  • -R : 為生成的resources指定儲存路徑

–noBuildXML : 輸出中不生成build.xml檔案

–noWSDL : 在resources目錄中不生成WSDL檔案

–noMessageReceiver : 不生成MessageReceiver類

生成完後可以在axis2的bin目錄下找到對應的檔案。檔案和同類.java檔案要大很多,並且呼叫路徑是定死的(標紅部分),改起來麻煩,反正我是不喜歡這種方式。雖然不要自己寫,但是看著這麼多行就不爽,太臃腫了。

 

呼叫方式如下。(方式應該有多種,沒有去深入研究)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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\"}";//對應的引數             setmsg.setPAlarmCfgMsg(str); //設定引數                String re=stub.setAlarmServerCfgMsg(setmsg).getResponse(); //呼叫並獲取返回值             System.out.println(re);         }     }  

我工作中遇到的全是C語言編寫的WebService,而且每個人編寫的都有些區別,很多人給你的wsdl並不能直接成功生成對應的java類,而且這種方法還有上述的一些缺點,所以我拋棄了這種方法。(上述列的程式碼全是以前試驗時用過的,那時候是成功的,寫這個部落格的時候我把所有能找到的wsdl全試著生成一遍,結果全部生成失敗。心塞)。

第二種RPC 方式,強烈推薦。

這種方式不多說,直接看程式碼就懂了 。這是一個呼叫webService查詢裝置線上數的。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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"); //設定名稱空間和需要呼叫的方法名             call.setTargetEndpointAddress(url); //設定請求路徑             call.setOperationName("GetNcgOnlineInfo"); //呼叫的方法名             call.setTimeout(Integer.valueOf(2000));     //設定請求超時                        call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);//設定返回型別             result= (String) call.invoke(opAddEntry,new Object[]{});                       } catch (ServiceException e) {             // TODO Auto-generated catch block             System.out.println("查詢線上狀態1:"+e.getMessage());             errCode=1;         } catch (RemoteException 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();     }

裡面註釋比較全。還有些別的設定也比較簡單,自己琢磨就知道了。例如編碼方式、解析時間等。

說說這種方式的問題吧。我在使用的時候遇到的是:和我對接的人編寫了兩個WebService。但是由於這兩個中有許多部分是相同的,他就把這兩個合併了,同時提供了兩個名稱空間(具體怎麼操作的我也不清楚),那麼問題了,這其中有一個名稱空間的所有方法我都能成功呼叫,但是都無法收到返回值。當時我就方了,開始還是好好的,怎麼就突然不行了,於是我繼續執行,檢視報錯訊息,同時抓包檢視報文內容。終於給我發現了問題。

下圖是返回結果報的錯,大體意識就是說我設定的名稱空間和對方的名稱空間不匹配。然後RPC解析就失敗了。

然後我利用Wireshark抓包,得到一下結果。可以看看出,我請求的是名稱空間是 ns1="urn:ncg"(其餘的都是wsdl預設自帶的)。可是我收到的返回報文就變了。變成了這樣的  xmlns:dag="http://tempuri.org/dag.xsd" xmlns:dag="urn:dag" xmlns:ncg="urn:ncg"  足足有三個啊。RPC按照預設設定的 ns1="urn:ncg" 去解析,那肯定什麼都解析不了的。所以只有自己去解析了。這種情況可以利用第三種或者第四種方式進行呼叫。

第三種:利用HttpURLConnection拼接和解析報文進行呼叫。

還是上面那個查詢裝置的方法。只不過改了下。當然,我這是知道報文後的解決辦法。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 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();                     //請求體                                           //<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();     }

正常來說,利用HttpURLConnection實現很多的呼叫不需要自己拼接請求頭和解析返回結果的(例如java端提供的一些action或者controller),可是在這兒呼叫WebService,確確實實的需要自己手寫。對比上面那個Wireshark抓包的結果可以發現,在請求體部分按照對方提供的wsdl進行拼接,結果部分也進行相同的解析。可以正確獲得結果。

第四種,利用httpclient

簡單來說,httpClient可以算是加強版的HttpURLConnection,httpClient的API比較多,也比較穩定,不容易擴充套件。HttpURLConnection比較輕量級,容易根據自己的需求進行擴充套件。但是穩定性不如httpClient。

這種方法具體實現思路和HttpURLConnection一樣。只是有點小區別。程式碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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();         }               }

結果:我這兒沒有做更多的判斷,直接輸出,這種方式我以前其實並沒有用到。如果有需要可以更具返回的狀態判斷是否成功。如果你去抓包的話,你會發現這個會和上面HttpURLConnection抓的一樣。

    

總結:呼叫webService很多程度上需要依賴對方編寫WebService是否嚴謹,如果足夠嚴謹,推薦使用RPC方式編寫,其餘的更具實際情況進行選擇

 

轉自:https://www.jb51.net/article/230317.htm