java调用WebService服务的四种方法总结


 

 

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM