运维开发网

对调用java服务的几种方法的总结

运维开发网 https://www.qedev.com 2022-05-13 17:50 出处:网络
WebService是一种跨编程语言、跨操作系统平台的远程调用技术,已存在很多年了,很多接口也都是通过WebService方式来发布的,下面这篇文章主要给大家介绍了关于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命令可以在lt;2 Axis2安装目录gt;/bin目录。如果配置了环境变量,可以在控制台中使用它们。

环境变量\bin\wsdl2java如下。

% axis 2 _ HOME % \ bin \ wsdl 2 Java-uri d:demo . wsdl-p client-s-o存根

如果没有,请将其键入相应的位置来执行。wsdl 2 Java-uri d:demo . wsdl-p client-s-o存根

其中-url是相应WebService的wsdl位置,可以是本地的,也可以是网络的。-p是指定层代的类名。具体参数列表如下:

-o lt;pathgt; : 指定生成代码的输出路径 -a : 生成异步模式的代码 -s : 生成同步模式的代码 -p lt;pkggt; : 指定代码的package名称 -l lt;languangegt; : 使用的语言(Java/C) 默认是java -t : 为代码生成测试用例 -ss : 生成服务端代码 默认不生成 -sd : 生成服务描述文件 services.xml,仅与-ss一同使用 -d lt;databindinggt; : 指定databingding,例如,adb,xmlbean,jibx,jaxme and jaxbri -g : 生成服务端和客户端的代码 -pn lt;port_namegt; : 当WSDL中有多个port时,指定其中一个port -sn lt;serv_namegt; : 选择WSDL中的一个service -u : 展开data-binding的类 -r lt;pathgt; : 为代码生成指定一个repository -ssi : 为服务端实现代码生成接口类 -S : 为生成的源码指定存储路径 -R : 为生成的resources指定存储路径

–-noBuildXML:输出中不生成任何build.xml文件

–-noWSDL:不要在resources目录中生成WSDL文件。

–-noMessageReceiver:不生成MessageReceiver类。

生成后,可以在axis2的bin目录下找到相应的文件。相同种类的文件。java文件大很多,调用路径是固定的(用红色标注),改起来很麻烦。反正我不喜欢这种方式。不要自己写,但是看这么多行太臃肿了。



呼叫模式如下。(应该有多种方式,不做进一步研究)

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); }}

我在工作中遇到的WebService都是用C语言写的,每个人写的都不一样。很多人给你的wsdl并不能直接成功的生成对应的java类,而且这种方法也有上述的一些缺点,所以我放弃了这种方法。(上面列出的代码都是以前实验用的,当时都成功了。写这篇博客的时候,我试图生成所有我能找到的wsdl,但是所有生成的结果都失败了。心塞)。


第二种RPC 方式,强烈推荐。

这个办法不多说,看看代码就知道了。这是一个调用webService查询设备在线数量的方法。

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 blockSystem.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抓取包并得到结果。如您所见,我所要求的是name 空应该是ns1="urn:ncg "(其余部分默认由wsdl提供)。但是我收到的回复信息变了。变成这样?xmlns:Dag = " http://tempuri . org/Dag . xsd " xmlns:Dag = " urn:Dag " xmlns:ncg = " urn:ncg "?他们有三个人。按照RPC的ns1="urn:ncg "默认设置,肯定不会解决任何事情。所以我得自己分析。这种情况可以用第三种或第四种方式来称呼。



第三种:利用HttpURLConnection拼接和解析报文进行调用。

还是上面那个查询装备的方法。只是变了而已。当然,这是我知道消息后的解决办法。

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(); //请求体 //lt;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"gt;lt;soapenv:Bodygt;lt;ns1:DeleteCascadeFromCms soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="urn:ncg"gt;lt;ncg-code-list xsi:type="xsd:string"gt;["11241525"]lt;/ncg-code-listgt;lt;/ns1:DeleteCascadeFromCmsgt;lt;/soapenv:Bodygt;lt;/soapenv:Envelopegt; String soap = "lt;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\"gt;lt;soapenv:Bodygt;lt;ns1:"+method+" soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:ns1=\"urn:ncg\"/gt;lt;/soapenv:Bodygt;lt;/soapenv:Envelopegt;"; 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("lt;response xsi:type=\"xsd:string\"gt;")[1].split("lt;/responsegt;")[0]; is.close(); os.close(); conn.disconnect();} catch (MalformedURLException e) {// TODO Auto-generated catch blockSystem.out.println("通讯模块1:"+e.getMessage());errCode=1;} catch (IOException e) {// TODO Auto-generated catch blockSystem.out.println("通讯模块2:"+e.getMessage());errCode=2;}resultJson.put("errCode", errCode);resultJson.put("data", result); return resultJson.toString();}

通常情况下,使用HttpURLConnection进行多次调用并不需要拼接请求头和解析返回的结果(例如,java提供的一些动作或控制器),但在这里调用WebService确实需要自己动手写。对比上面Wireshark抓包的结果,可以发现请求体是根据对方提供的wsdl拼接的,结果部分也是用同样的方式解析的。可以正确地获得结果。



第四种,利用httpclient

简单来说,httpClient可以看作是HttpURLConnection的加强版。httpClient有很多API,比较稳定,不容易扩展。HttpURLConnection是轻量级的,很容易根据自己的需要进行扩展。但是稳定性不如httpClient。

该方法的具体实现思路与HttpURLConnection相同。只是有一点点不同。代码如下:

public void demo(String url){HttpClient httpClient=new HttpClient();PostMethod postMethod=new PostMethod();postMethod.setPath(url+"/ncg.wsdl"); //路径和wsdl名String soap = "lt;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\"gt;lt;soapenv:Bodygt;lt;ns1:GetNcgOnlineInfo soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:ns1=\"urn:ncg\"/gt;lt;/soapenv:Bodygt;lt;/soapenv:Envelopegt;"; 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("lt;response xsi:type=\"xsd:string\"gt;")[1].split("lt;/responsegt;")[0]);} catch (UnsupportedEncodingException e1) {// TODO Auto-generated catch blocke1.printStackTrace();} catch (HttpException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

结果:我在这里没有做更多的判断,直接输出,我之前其实没有用过。如果需要,可以用更多的返回状态来判断是否成功。如果你抓包,你会发现这个和上面HttpURLConnection抓到的是一样的。

????

总结:调用web服务很大程度上取决于对方编写web服务是否严谨。如果足够严谨的话,建议用RPC写web服务,剩下的比较实用。


总结

关于java调用WebService的四种方法的这篇文章到此为止。关于java调用WebService的更多信息

0

精彩评论

暂无评论...
验证码 换一张
取 消