就在昨天临下班,项目经理提了个需求!!反馈档案系统的对接,这个系统 .net开发而不是用Java写的WebService接口 虽说都一样,但还是有坑的,与往常的域名端口号相比,发布一般都是用WSDL文件样式发布,在文件里包含这个WebService对外暴露可使用的接口信息,使用.asmx站点路径和namespace访问。看完客户发来的文档看能看懂,技术完全没接触过一头雾水。。。。。花了点时间问技术经理思路加上自己搜索资料,接下来我们来看看简单的使用吧~ ~~ 开锤!!!
这里我使用的是 Apache AXIS (技术点)
客户那边是用 .net开发 的WebSerivce服务端http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx这是一个很好的WebService服务地址,后面加(?wsdl)可以查看WSDL文件样式 wsimport -s ./ url (这是个命令,可以将wsdl文件生成本地文件 ./ 是生成地的目录)
wsimport命令是jdk提供的,作用是根据使用说明书生成客户端代码,wsimport只支持SOAP1.1客户端的生成
这里能使用的技术有很多种,根据自己的需要选择
- 使用Apache的AXIS 1.x … AXIS 2.x
- 使用Document方式生成参数列表调用,使用也是AXIS(传递字符类型很好用,对于数组什么类型比较麻烦)不用生成客户端代码
- 使用OkHttp自己组装soap协议(应该就是拼接成XML的形式)然后调用
- 使用wsdl2java工具,生成客户端代码调用WebService(一般内网系统没有WSDL文件没法生成)
- 其他方法。。。
1. 听说了下面就是整合使用,先引入.jar包
这里我使用的是1.x版本
<dependency>
<groupId>org.apache.axis</groupId>
<artifactId>axis</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.axis</groupId>
<artifactId>axis-jaxrpc</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>axis</groupId>
<artifactId>axis-wsdl4j</artifactId>
<version>1.5.1</version>
</dependency>
2. 编写工具类调用服务
import cn.hutool.core.lang.Assert;
import com.google.common.collect.Lists;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.encoding.ser.Base64DeserializerFactory;
import org.apache.axis.encoding.ser.Base64SerializerFactory;
import org.apache.axis.message.SOAPHeaderElement;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Component
public final class WebServiceUtils {
// 接口的调用地址
private static String ENDPOINT;
// 命名空间
private static String NAMESPACE;
@Value("${websevice.endpoint}")
public void setENDPOINT(String ENDPOINT) {
Assert.notEmpty(ENDPOINT, "服务地址未配置");
WebServiceUtils.ENDPOINT = ENDPOINT;
}
@Value("${websevice.namespace}")
public void setNAMESPACE(String NAMESPACE) {
Assert.notEmpty(NAMESPACE, "命名空间值未设置");
WebServiceUtils.NAMESPACE = NAMESPACE;
}
/**
* methodName:调用的方法名
* params:调用方法需要的参数
* return:String类型的返回值
*/
@SuppressWarnings("rawtypes")
public static String call(String methodName, LinkedHashMap<String, LinkedHashMap<Object, Class>> params) {
Service service = new Service();
Call call = (Call) service.createCall();
SOAPHeaderElement soapHeaderElement = new SOAPHeaderElement(NAMESPACE, methodName);
soapHeaderElement.setNamespaceURI(NAMESPACE);
call.setTargetEndpointAddress(new URL(ENDPOINT));
QName qName = new QName(NAMESPACE, methodName);
call.setOperationName(qName);
call.setSOAPActionURI(NAMESPACE + methodName);
call.setUseSOAPAction(true);
call.setReturnType(qName, String.class);
call.addHeader(soapHeaderElement);
// 如果参数中包含字节数组 注册Base64编码解码器,主要是用传送文件
if (params.values().stream().map(LinkedHashMap::values).anyMatch(v -> v.stream().anyMatch(t -> t == byte[].class))) {
call.registerTypeMapping(byte[].class, qName, new Base64SerializerFactory(byte[].class, qName), new Base64DeserializerFactory(byte[].class, qName));
}
List<Object> parameterList = Lists.newArrayList();
for (Map.Entry<String, LinkedHashMap<Object, Class>> keyEntry : params.entrySet()) {
for (Map.Entry<Object, Class> valueEntry : keyEntry.getValue().entrySet()) {
call.addParameter(new QName(NAMESPACE, keyEntry.getKey()), getXmlType(valueEntry), ParameterMode.IN);
parameterList.add(valueEntry.getKey());
}
}
String result = String.valueOf(call.invoke(parameterList.toArray()));
return result;
}
/**
* valueEntry:参数值对应的Class对象
* return:QName对象,表示参数的类型
*/
private static QName getXmlType(Map.Entry<Object, Class> valueEntry) {
Class clazz = valueEntry.getValue();
QName qName = null;
if (clazz == byte[].class) {
qName = XMLType.XSD_BASE64;
} else {
qName = XMLType.XSD_STRING;
}
return qName;
}
/**
* methodName:调用的方法名
* params:调用方法需要的参数
* return:返回对象类型
*/
public static <T> T call(String methodName, LinkedHashMap<String, LinkedHashMap<Object, Class>> params, Class<T> clazz) {
Service service = new Service();
Call call = (Call) service.createCall();
SOAPHeaderElement soapHeaderElement = new SOAPHeaderElement(NAMESPACE, methodName);
soapHeaderElement.setNamespaceURI(NAMESPACE);
call.setTargetEndpointAddress(new URL(ENDPOINT));
QName qName = new QName(NAMESPACE, methodName);
call.setOperationName(qName);
call.setSOAPActionURI(NAMESPACE + methodName);
call.setUseSOAPAction(true);
call.addHeader(soapHeaderElement);
// 需要注册,进行序列化 实体类也要序列化 implements Serializable
call.registerTypeMapping(clazz, qName, new BeanSerializerFactory(clazz, qName), new BeanDeserializerFactory(clazz, qName));
// 设置输出的类
call.setReturnClass(clazz);
List<Object> parameterList = Lists.newArrayList();
for (Map.Entry<String, LinkedHashMap<Object, Class>> keyEntry : params.entrySet()) {
for (Map.Entry<Object, Class> valueEntry : keyEntry.getValue().entrySet()) {
call.addParameter(new QName(NAMESPACE, keyEntry.getKey()), getXmlType(valueEntry), ParameterMode.IN);
parameterList.add(valueEntry.getKey());
}
}
T result = (T) call.invoke(parameterList.toArray());
return result;
}
}
到这里已经成功的完成客户端掉取服务的工具,可以根据自己项目的需求进行修改。其他未涉及的技术点可自行查阅,如有报错分享或补充技术点可以评论
e.g. 其他方式拓展 AXIS 2.x
1. 引入.jar包
如果引用过EasyExcel或者POI之类的依赖,小心poi-ooxml-schemas —> xmlbeans冲突
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-spring</artifactId>
<version>1.7.8</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>1.7.8</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-local</artifactId>
<version>1.7.8</version>
</dependency>
<!-- 版本与EasyExcel中的xmlbeans版本冲突,因此排除 -->
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-xmlbeans</artifactId>
<version>1.7.8</version>
<exclusions>
<exclusion>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
</exclusion>
</exclusions>
</dependency>
2. 使用RPCServiceClient远程调用
public static String call(String methodName, LinkedHashMap<String, Object> params) {
RPCServiceClient serviceClient = new RPCServiceClient();
Options options = serviceClient.getOptions();
EndpointReference endpointReference = new EndpointReference(ENDPOINT);
options.setTo(endpointReference);
options.setAction(NAMESPACE + methodName);
options.setTransportInProtocol(Constants.TRANSPORT_HTTP);
options.setProperty(HTTPConstants.CHUNKED, "false");
options.setProperty(Constants.Configuration.HTTP_METHOD, HTTPConstants.HTTP_METHOD_POST);
QName qName = new QName(NAMESPACE, methodName);
List<Object> parameterList = Lists.newArrayList();
for (String key : params.keySet()) {
parameterList.add(params.get(key));
}
Class[] returnClass = {String.class};
result = String.valueOf(serviceClient.invokeBlocking(qName, parameterList.toArray(), returnClass)[0]);
}
3. 使用ServiceClient调用
使用OMElement可以完成文件的字节传输吧,我也没有过多的研究 包装类也没有研究,不如直接注册个序列化器方便。。根据自己的业务需求和代码风格来衡量
public static void call(String value) {
ServiceClient client = new ServiceClient();
Options options = client.getOptions();
EndpointReference endpointReference = new EndpointReference(ENDPOINT);
options.setTo(endpointReference);
options.setAction(NAMESPACE + methodName);
OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace namespace = factory.createOMNamespace(NAMESPACE, "");
OMElement method = factory.createOMElement(methodName, namespace);
// 这里有多少参数,就要创建多少个Element
OMElement param = factory.createOMElement("parameter", namespace);
param.addChild(factory.createOMText(param, value));
method.addChild(param);
// 增加附件 这个就可以完成文件上传等功能
OMElement fileData = factory.createOMElement("imageByte", namespace);
java.io.File file = new java.io.File("c:\\test.jpg");
FileDataSource fs = new FileDataSource(file);
DataHandler fileHandle = new DataHandler(fs);
OMText textData = factory.createOMText(fileHandle, true);
fileData.addChild(textData);
method.addChild(fileData);
OMElement metaData = factory.createOMElement("length", namespace);
metaData.setText(file.length() + "");
method.addChild(metaData);
method.build();
OMElement result = client.sendReceive(method);
}