企业做网站的流程,用表格做的网站,腾讯广告推广平台,个人微信公众号收费吗简介 在多系统交互中#xff0c;有时候需要以Java作为客户端来调用SOAP方式的WebService服务#xff0c;本文通过分析不同的调用方式#xff0c;以Demo的形式#xff0c;帮助读者在生产实践中选择合适的调用方式。 本文JDK环境为JDK17。 结论 推荐使用Axis2或者Jaxws#…简介 在多系统交互中有时候需要以Java作为客户端来调用SOAP方式的WebService服务本文通过分析不同的调用方式以Demo的形式帮助读者在生产实践中选择合适的调用方式。 本文JDK环境为JDK17。 结论 推荐使用Axis2或者Jaxws以无客户端的形式来调用WebService。 有客户端推荐Maven插件。 有客户端调用
主要时利用wsdl文档自动生成对应的Java代码来实现
建议在pom文件中配置对应的Maven插件来实现WebService客户端代码的自动生成。
JDK wsimport命令生成不推荐
简介
主要是利用jdk的自带工具wsimport工具实现执行命令如下 wsimport -s C:\tmp\com -p com.example.demo5.wsdl -encoding utf-8 http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?wsdl 优点
通常装有JDK的电脑或者服务器都可以直接运行方便生成。
缺点
在实际的运用中wsimport命令会有很多问题首先只有JDK1.8才支持这个命令即使能使用仍然存在一些问题。其次在JDK17以上没有自带这个工具可能要安装插件才能使用但是笔者安装了一些插件仍然无法使用。
ApacheCXF自动生成不推荐
简介
ApacheCXF通过安装也可以自动生成对应的WebService客户端代码。具体操作可见链接。
缺点
需要额外安装ApacheCXF插件。
Maven插件自动生成推荐
简介
通过spring.io网址的Demo示例可以配置pom的maven插件自动生成代码。
demo获取链接如下
Getting Started | Consuming a SOAP web service (spring.io)
pom配置示例如下
buildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin!-- tag::wsdl[] --plugingroupIdcom.sun.xml.ws/groupIdartifactIdjaxws-maven-plugin/artifactIdversion3.0.0/versionexecutionsexecutiongoalsgoalwsimport/goal/goals/execution/executionsconfigurationpackageNamecom.example.consumingwebservice.wsdl/packageNamewsdlUrls
!-- wsdlUrlhttp://localhost:8080/ws/countries.wsdl/wsdlUrl--wsdlUrlhttp://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?wsdl/wsdlUrl/wsdlUrlssourceDestDir${sourcesDir}/sourceDestDirdestDir${classesDir}/destDirextensiontrue/extension/configuration/plugin!-- end::wsdl[] --/plugins/build
生成代码如下 调用方式
RequestMapping(value /{ip}, method RequestMethod.GET)public ArrayOfString searchIp(PathVariable(ip) String ip) {IpAddressSearchWebServiceSoap ipAddressSearchWebServiceSoap new IpAddressSearchWebService().getIpAddressSearchWebServiceSoap();ArrayOfString response ipAddressSearchWebServiceSoap.getCountryCityByIp(ip);return response;} 优点
操作简单改动小。
缺点
唯一的缺点也是有客户端调用普遍存在的自动生成代码后需要重新部署一次。
Springboot集成Git插件实现
通过Springboot集成git插件可以通过接口的形式来修改maven的wsdlUrls配置然后推送到git服务最后触发Jenkins自动部署。
以Git推送代码的形式来实现代码的自动生成其缺点是每次根据一份wsdl文件生成完代码需要重启一次服务但是笔者通过自动配置的形式可以做到一键部署。
其中触发Jenkins自动部署可以通过git的配置实现通过访问特定的url实现。配置好的git部署链接如下
http://192.168.22.22:8080/job/demo_test/build?token1987654567890hjkoijghfvgjjnmkjkmk
其中token的值可以自动定义这样在借助代码的形式就可以做到一键部署。 其实现代码如下
package com.example.consumingwebservice;import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.HttpConfig;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;public class GitUtil {//private static Log log LogFactory.getLog(GitUtil.class);private GitUtil() {}public static Git getGit(String uri, CredentialsProvider credentialsProvider, String localDir) throws Exception {Git git null;if (new File(localDir).exists() ) {git Git.open(new File(localDir));} else {git Git.cloneRepository().setCredentialsProvider(credentialsProvider).setURI(uri).setDirectory(new File(localDir)).call();}//设置一下post内存否则可能会报错Error writing request body to servergit.getRepository().getConfig().setInt(HttpConfig.HTTP, null, HttpConfig.POST_BUFFER_KEY, 512*1024*1024);return git;}public static CredentialsProvider getCredentialsProvider(String username, String password) {return new UsernamePasswordCredentialsProvider(username, password);}public static Repository getRepository(Git git) {return git.getRepository();}public static void pull(Git git, CredentialsProvider credentialsProvider) throws Exception {git.pull().setRemote(origin).setCredentialsProvider(credentialsProvider).call();}public static void push(Git git, CredentialsProvider credentialsProvider, String filepattern, String message)throws Exception {git.add().addFilepattern(filepattern).call();git.add().setUpdate(true);git.commit().setMessage(message).call();git.push().setCredentialsProvider(credentialsProvider).call();}public static void main(String[] args) throws Exception {String uri http://192.168.9.11/test/webservice.git;String username 343535qq.com;String password xdfetrfrr;CredentialsProvider credentialsProvider getCredentialsProvider(username, password);String localDir C:/tmp/git_test;Git git getGit(uri, credentialsProvider, localDir);pull(git, credentialsProvider);changeFile(localDir /pom.xml);// push(git, credentialsProvider, ., 提交文件);push(git, credentialsProvider, pom.xml, 修改pom文件 new Date());}private static final String newText wsdlUrlhttp://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?wsdl/wsdlUrl\r\n /wsdlUrls;protected static void changeFile(String filePath) {try {// 读取文本文件的内容Path path Paths.get(filePath);String content Files.readString(path);System.out.println(content);// 替换内容String modifiedContent content.replace(/wsdlUrls, newText);// 将修改后的内容写回文本文件Files.write(path, modifiedContent.getBytes(), StandardOpenOption.WRITE);System.out.println(文本文件内容已成功修改);} catch (IOException e) {System.out.println(修改文本文件内容时出现错误 e.getMessage());}}} 如要实现流程图的规划可以后台通过http的get请求上文的git部署链接实现接口的自动部署。
无客户端调用
也就是不需要按wsdl的格式来生成对应的Java代码原理时通过构建xml的形式来访问WebService。
这里推荐使用Axis2或者Jaxws的方式来调用二者各有优劣。
Axis调用不推荐
简介
通过pom引入axis依赖实现无客户端访问所需依赖如下 dependency groupIdaxis/groupId artifactIdaxis/artifactId version1.4/version /dependency 代码实现如下
public static void main(String[] args){try {String nameSpac http://WebXml.com.cn/;URL url new URL(http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl);QName sname new QName(nameSpac, MobileCodeWS);QName pname new QName(nameSpac, MobileCodeWSSoap);Service service new Service( url, sname);Call call (Call)service.createCall(pname);call.setSOAPActionURI(nameSpac getMobileCodeInfo);call.setOperationName(new QName(nameSpac, getMobileCodeInfo)); // 需要请求的方法call.addParameter(new QName(nameSpac, mobileCode), XMLType.XSD_STRING, ParameterMode.IN); // 入参call.addParameter(new QName(nameSpac, userID), XMLType.XSD_STRING, ParameterMode.IN); // 入参
// call.addParameter(param3, XMLType.SOAP_STRING, ParameterMode.IN); // 入参String param1 15932582632; // 参数String param2 null; // 参数call.setReturnClass(String.class); // 设置返回值call.setUseSOAPAction(true);Object invoke call.invoke(new Object[]{param1, param2});// 调用获取返回值
// Object invoke call.invoke(new Object[]{});// 调用获取返回值System.out.println(invoke);}catch (Exception e){e.printStackTrace();}}
优点
较少的代码量依赖需要少实现简单
缺点
通过笔者的实验发现Axis的调用并不稳定对于不同的接口有的接口无参数调用可以调通有参数调用会报错有的接口有参数调用可以调通(如例)无参数调用会报错。
实际上这个依赖在2006年便没有维护了它的功能转移到了Axis2。
Axis2调用推荐
简介
通过pom引入axis2依赖实现无客户端访问所需依赖如下
dependency
groupIdorg.apache.axis2/groupId
artifactIdaxis2-jaxws/artifactId
version1.7.0/version
/dependencydependency
groupIdorg.apache.axis2/groupId
artifactIdaxis2-adb-codegen/artifactId
version1.7.0/version
/dependencydependency
groupIdorg.apache.axis2/groupId
artifactIdaxis2-transport-local/artifactId
version1.7.0/version
/dependencydependency
groupIdorg.apache.axiom/groupId
artifactIdcom.springsource.org.apache.axiom/artifactId
version1.2.5/version
/dependency
代码实现如下
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.impl.httpclient3.HttpTransportPropertiesImpl;/**** ClassName: MobileClientDoc* Description: TODO* 方法二 应用document方式调用 用ducument方式应用现对繁琐而灵活。现在用的比较多。因为真正摆脱了我们不想要的耦合* 即使用org.apache.axis2.client.ServiceClient类进行远程调用web服务不生成客户端** date 2017年11月9日 下午1:27:17**/
public class SoapAxis2Client {private static String requestName getCountryCityByIp;public static void ipWS() {try {ServiceClient serviceClient new ServiceClient();//创建服务地址WebService的URL,注意不是WSDL的URLString url http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx;EndpointReference targetEPR new EndpointReference(url);Options options serviceClient.getOptions();options.setTo(targetEPR);//确定调用方法wsdl 命名空间地址 (wsdl文档中的targetNamespace) 和 方法名称 的组合options.setAction(http://WebXml.com.cn/ requestName);//设置密码HttpTransportPropertiesImpl.Authenticator auth new HttpTransportPropertiesImpl.Authenticator();
// auth.setUsername(username); //服务器访问用户名
// auth.setPassword(password); //服务器访问密码
// options.setProperty(HTTPConstants.AUTHENTICATE, auth);OMFactory fac OMAbstractFactory.getOMFactory();/** 指定命名空间参数* uri--即为wsdl文档的targetNamespace命名空间* perfix--可不填*/OMNamespace omNs fac.createOMNamespace(http://WebXml.com.cn/, );// 指定方法OMElement method fac.createOMElement(requestName, omNs);// 指定方法的参数OMElement theIpAddress fac.createOMElement(theIpAddress, omNs);theIpAddress.setText(111.249.198.56);
// OMElement userID fac.createOMElement(userID, omNs);
// userID.setText();method.addChild(theIpAddress);
// method.addChild(userID);method.build();//远程调用web服务OMElement result serviceClient.sendReceive(method);//值得注意的是返回结果就是一段由OMElement对象封装的xml字符串。String xml result.cloneOMElement().toString();System.out.println(xml);} catch (AxisFault axisFault) {axisFault.printStackTrace();}}public static void main(String[] args) throws AxisFault {ipWS();}}优点
代码量较少通过配置xml节点实现系统调用可以设置灵活的调用方式。经过实验对各种WebService接口的有参无参调用都能取得正确的返回结果。
测试结果如下 getCountryCityByIpResponse xmlnshttp://WebXml.com.cn/getCountryCityByIpResultstring111.249.198.56/stringstring台湾省 /string/getCountryCityByIpResult/getCountryCityByIpResponse 缺点
所需的pom配置文件较多且引用不正确较难排查问题且各个pom之间的版本冲突也需要解决。
Jaxws调用推荐
简介
引入对于的pom配置文件
dependencygroupIdorg.apache.axis2/groupIdartifactIdaxis2-jaxws/artifactIdversion1.7.0/version
/dependency
这里提前说下下面代码大部分来自于csdn作者——LengYouNuan的文章但是实在找不到对于作者了提前声明。
还有它的原始代码并不能正常运行会有服务器未能识别 HTTP 头 SOAPAction 的值的报错笔者通过实验和研究添加了如下配置才能正常运行 //这句话很重要否则报错服务器未能识别 HTTP 头 SOAPAction 的值 dispatch.getRequestContext().put(SOAPACTION_URI_PROPERTY, nameSpace elementName); dispatch.getRequestContext().put(SOAPACTION_USE_PROPERTY, true); 由于使用的JDK17,对应的配置和以前不一样了
public interface BindingProvider {String USERNAME_PROPERTY jakarta.xml.ws.security.auth.username;String PASSWORD_PROPERTY jakarta.xml.ws.security.auth.password;String ENDPOINT_ADDRESS_PROPERTY jakarta.xml.ws.service.endpoint.address;String SESSION_MAINTAIN_PROPERTY jakarta.xml.ws.session.maintain;String SOAPACTION_USE_PROPERTY jakarta.xml.ws.soap.http.soapaction.use;String SOAPACTION_URI_PROPERTY jakarta.xml.ws.soap.http.soapaction.uri;
......
应该主要是javax和jakarta的区别。
完整可运行代码如下
package com.example.consumingwebservice;import com.sun.xml.ws.client.BindingProviderProperties;
import com.sun.xml.ws.developer.JAXWSProperties;
import jakarta.xml.soap.*;
import jakarta.xml.ws.Dispatch;
import jakarta.xml.ws.Service;
import org.w3c.dom.Document;import javax.xml.namespace.QName;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;import static jakarta.xml.ws.BindingProvider.SOAPACTION_URI_PROPERTY;
import static jakarta.xml.ws.BindingProvider.SOAPACTION_USE_PROPERTY;/*** soap方式调用webservice方式客户端** author LengYouNuan* create 2021-05-31 下午2:35*/
public class SoapJaxwsClient {String nameSpace ; //wsdl的命名空间String wsdlUrl ; //wsdl文档地址String serviceName ; //服务的名字String portName ;String responseName ; //WebResult注解上的name值String elementName ; //默认是要访问的方法名 如果WebMethod属性name有值 则是该值实际还是以wsdl文档为主int timeout 20000;/*** param nameSpace* param wsdlUrl* param serviceName* param portName* param element* param responseName*/public SoapJaxwsClient(String nameSpace, String wsdlUrl,String serviceName, String portName, String element,String responseName) {this.nameSpace nameSpace;this.wsdlUrl wsdlUrl;this.serviceName serviceName;this.portName portName;this.elementName element;this.responseName responseName;}/*** param nameSpace* param wsdlUrl* param serviceName* param portName* param element* param responseName* param timeOut 毫秒*/public SoapJaxwsClient(String nameSpace, String wsdlUrl,String serviceName, String portName, String element,String responseName, int timeOut) {this.nameSpace nameSpace;this.wsdlUrl wsdlUrl;this.serviceName serviceName;this.portName portName;this.elementName element;this.responseName responseName;this.timeout timeOut;}public String sendMessage(HashMapString, String inMsg) throws Exception {// 创建URL对象URL url null;try {url new URL(wsdlUrl);} catch (Exception e) {e.printStackTrace();return 创建URL对象异常;}// 创建服务(Service)QName sname new QName(nameSpace, serviceName);Service service Service.create(url, sname);// 创建Dispatch对象DispatchSOAPMessage dispatch null;try {dispatch service.createDispatch(new QName(nameSpace, portName), SOAPMessage.class, Service.Mode.MESSAGE);} catch (Exception e) {e.printStackTrace();return 创建Dispatch对象异常;}// 创建SOAPMessagetry {//这句话很重要否则报错服务器未能识别 HTTP 头 SOAPAction 的值dispatch.getRequestContext().put(SOAPACTION_URI_PROPERTY, nameSpace elementName);dispatch.getRequestContext().put(SOAPACTION_USE_PROPERTY, true);SOAPMessage msg MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL).createMessage();msg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, UTF-8);SOAPEnvelope envelope msg.getSOAPPart().getEnvelope();// 创建SOAPHeader(不是必需)// SOAPHeader header envelope.getHeader();// if (header null)// header envelope.addHeader();// QName hname new QName(nameSpace, username, nn);// header.addHeaderElement(hname).setValue(huoyangege);// 创建SOAPBodySOAPBody body envelope.getBody();QName ename new QName(nameSpace, elementName, );SOAPBodyElement ele body.addBodyElement(ename);// 增加Body元素和值for (Map.EntryString, String entry : inMsg.entrySet()) {ele.addChildElement(new QName(nameSpace, entry.getKey())).setValue(entry.getValue());}// 超时设置dispatch.getRequestContext().put(BindingProviderProperties.CONNECT_TIMEOUT, timeout);dispatch.getRequestContext().put(JAXWSProperties.REQUEST_TIMEOUT, timeout);// 通过Dispatch传递消息,会返回响应消息SOAPMessage response dispatch.invoke(msg);// 响应消息处理,将响应的消息转换为doc对象Document doc response.getSOAPPart().getEnvelope().getBody().extractContentAsDocument();String ret doc.getElementsByTagName(responseName).item(0).getTextContent();return ret;} catch (Exception e) {e.printStackTrace();throw e;}}public static void main(String[] args) throws Exception {
// SoapClient soapClientnew SoapClient(http://spring.io/guides/gs-producing-web-service,http://localhost:8080/ws/countries.wsdl,
// CountriesPortService,CountriesPortSoap11,getCountry,
// getCountryResponse,
// 2000);SoapJaxwsClient soapClient new SoapJaxwsClient(http://WebXml.com.cn/, http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl,MobileCodeWS, MobileCodeWSSoap, getDatabaseInfo,getDatabaseInfoResponse,2000);
// SoapClient soapClientnew SoapClient(http://WebXml.com.cn/,http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.wsdl,
// IpAddressSearchWebService,IpAddressSearchWebServiceSoap,getCountryCityByIp,
// getCountryCityByIpResult,
// 2000);//封装请求参数HashMapString, String msg new HashMap();
// msg.put(theIpAddress,111.249.198.56);
// msg.put(mobileCode,18702750020);
// msg.put(userID,);String s soapClient.sendMessage(msg);System.out.println(s);}
}测试结果 优点
pom配置简单无须解决各种版本依赖的问题。
缺点
可以看到Jaxws的调用和Axis2一样都具有较高的灵活性都可以自定义xml的节点数据。
所不同的是它的调用代码稍显繁琐但如果在生产中有良好的封装这应该不是问题。
小结
对于SOAP方式WebService的调用有客户端的调用推荐maven插件自动生成代码的形式唯一的缺点是需要重新部署一次。
对于无客户端的调用推荐Axis2或者Jaxws的形式考虑到二者实现其实各有优劣有需要的读者可以自行甄别选用。