需求
最近,接触到了一个java对接C#的项目,使用WebService技术开发。项目已经快告一段落了,经过这几个月接触和使用。我有了一个清晰的认识,之前也调研了互联网上大部分实现的通讯,他们的优缺点,我都有一定的了解,我认为的最好解决方案是 wsimport生成的代码,让我们看看这几种方式的优缺点吧。
WebService是什么
WebService是一种跨编程语言和跨操作系统平台的远程调用技术。
WebService使用场景
WebService可用于调用第三方系统API。
WebService创建方式
引入依赖
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.5.3</version>
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>org.apache.cxf</groupId>-->
<!-- <artifactId>cxf-rt-frontend-jaxws</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.5.3</version>
</dependency>
</dependencies>
创建bean
package com.hikktn;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public interface IUserService {
@WebMethod
//返回一个字符串
String getStr(String str);
}
实现接口
@WebService
public class UserServiceImpl implements IUserService{
@WebMethod
@Override
public String getStr(String str) {
String str2 = null;
try {
str2 = new String("处理后的字符串:".getBytes(),"UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return str2 + str;
}
}
第一种服务端
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.message.Message;
import javax.xml.ws.Endpoint;
import java.util.List;
public class TestServer {
private static String PATH = "http://127.0.0.1:7777/WebService_Server/jdk";
//浏览器输入以上地址访问不到,但是http://127.0.0.1:7777/WebService_Server/jdk?wsdl这个地址可以查看到xml文件
//相对路径随便写
public static void main(String[] args) {
//获取一个EndPoint实例
EndpointImpl endpoint = (EndpointImpl) Endpoint.publish(PATH, new UserServiceImpl());
//服务器端获取输入拦截器
List<Interceptor<? extends Message>> inInterceptors = endpoint.getInInterceptors();
//添加接受信息日志拦截器
inInterceptors.add(new LoggingInInterceptor());
System.out.println("成功发布!");
}
}
第二种服务端
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
/**
* @author killian
* @since 2022-11-04 14:54
*/
public class TestServer2 {
public static void main(String[] args) {
//以下是编译wsdl文件的另一种方法,这种方法使用CXF的JaxWsServerFactoryBean类中的方法创建
System.out.println("启动服务器...");
//创建地址
String address = "http://127.0.0.1:7777/WebService_Server/cxf";
//创建一个服务器工厂实例
JaxWsServerFactoryBean jaxWsServerFactoryBean = new JaxWsServerFactoryBean();
//设置暴露地址
jaxWsServerFactoryBean.setAddress(address);
//设置接口类
jaxWsServerFactoryBean.setServiceClass(IUserService.class);
//设置实现类
jaxWsServerFactoryBean.setServiceBean(uSerService);
//添加日志拦截器
jaxWsServerFactoryBean.getInInterceptors().add(new LoggingInInterceptor());
//创建服务器
jaxWsServerFactoryBean.create();
System.out.println("服务器成功启动!");
}
}
启动服务后,下载Soap软件测试
放入url
http://127.0.0.1:7777/WebService_Server/jdk?wsdl
就可以测试
客户端调用(不建议)
public class TestXml {
public static void main(String[] args) {
try {
StringWriter writer = new StringWriter();
JAXBContext jc = JAXBContext.newInstance(Student.class);
Marshaller ms = jc.createMarshaller();
Student st = new Student("zhang", "w", "h", 11);
ms.marshal(st, writer);
String xmlStr = writer.toString();
System.out.println(xmlStr);
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这种在网络上都是入门级别,玩玩demo可以。但是你要拿到项目中,就恼火了。
首先,这样的调用方式是完全不管命名空间的调用,而国外的超多API都是有很多嵌套的命名空间xml。
可以说这个是非常致命的缺点,无法生成有效的XML。
hutool调用WebService(简单场景)
具体大家看这篇博客,它有非常详细的教程。
Java使用Hutool调用WebService接口详解
优点:不用写HttpURLConnection通讯,简单的接口可以使用这个来开发。
缺点:代码繁多,没有更加优化的工具类,要去处理返回的XML,处理方式只有转换为Map,非常不利于后续维护和代码编写。
HttpURLConnection原生请求WebService(简单场景)
public class SoapSendUtil {
/**
* 默认连接超时
*/
private static final int connectionTimeout = 60 * 1000;
/**
* 默认读取超时
*/
private static final int readTimeout = 60 * 1000;
/**
* 发送请求
*
* @param SapPiUrl soap协议url
* @param xmlStr soap协议的xml
* @param action 请求接口url
* @return 响应报文
* @throws IOException IO异常
*/
public static String sendRequest(String SapPiUrl, String xmlStr, String action) throws IOException {
//创建一个支持HTTP特定功能的URLConnection
HttpURLConnection httpConn = null;
//创建一个输出流对象
OutputStream out = null;
String returnXml = "";
//打开链接
httpConn = (HttpURLConnection) new URL(SapPiUrl).openConnection();
//设置内容和字符格式
httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
//设置请求和xml
httpConn.setRequestProperty("SOAPAction", action);
//设置请求为post亲求
httpConn.setRequestMethod("POST");
httpConn.setDoOutput(true);
httpConn.setDoInput(true);
httpConn.setConnectTimeout(connectionTimeout);
httpConn.setReadTimeout(readTimeout);
httpConn.connect();
out = httpConn.getOutputStream(); // 获取输出流对象
httpConn.getOutputStream().write(xmlStr.getBytes()); // 将要提交服务器的SOAP请求字符流写入输出流
out.flush();
out.close();
int code = httpConn.getResponseCode(); // 用来获取服务器响应状态
System.out.println(code);
String tempString = null;
StringBuffer sb1 = new StringBuffer();
//如果响应状态为ok,就读取http内容
if (code == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), StandardCharsets.UTF_8));
while ((tempString = reader.readLine()) != null) {
sb1.append(tempString);
}
if (null != reader) {
reader.close();
}
} else {
BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getErrorStream(), StandardCharsets.UTF_8));
// 一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null) {
sb1.append(tempString);
}
if (null != reader) {
reader.close();
}
}
// 响应报文
returnXml = sb1.toString();
return returnXml;
}
}
import org.springframework.util.StringUtils;
import javax.xml.bind.*;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.namespace.QName;
import java.io.File;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
/**
* 使用Jaxb2.0实现XMLJava Object的Binder.
* <p>
* 特别支持Root对象是List的情形.
*/
public class JaxbBinderUtil {
/**
* 多线程安全的Context.
*/
private JAXBContext jaxbContext;
/**
* @param types 所有需要序列化的Root对象的类型.
*/
public JaxbBinderUtil(Class... types) {
try {
jaxbContext = JAXBContext.newInstance(types);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
/**
* Java Object->Xml.
*/
public String toXml(Object root, String encoding) {
try {
StringWriter writer = new StringWriter();
createMarshaller(encoding).marshal(root, writer);
return writer.toString();
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
public String toXml(Object root) {
try {
StringWriter writer = new StringWriter();
createMarshaller(StandardCharsets.UTF_8.toString()).marshal(root, writer);
return writer.toString();
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
/**
* Java Object->Xml, 特别支持对Root Element是Collection的情形.
*/
public String toXml(Collection root, String rootName, String encoding) {
try {
CollectionWrapper wrapper = new CollectionWrapper();
wrapper.collection = root;
JAXBElement wrapperElement = new JAXBElement(new QName(rootName),
CollectionWrapper.class, wrapper);
StringWriter writer = new StringWriter();
createMarshaller(encoding).marshal(wrapperElement, writer);
return writer.toString();
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
/**
* Xml->Java Object.
*/
public Object fromXml(String xml) {
try {
StringReader reader = new StringReader(xml);
return createUnmarshaller().unmarshal(reader);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
/**
* 创建Marshaller, 设定encoding(可为Null).
*/
public Marshaller createMarshaller(String encoding) {
try {
Marshaller marshaller = jaxbContext.createMarshaller();
// 去除XML头
marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
// 指定是否使用换行和缩排对已编组 XML 数据进行格式化的属性名称
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
if (StringUtils.isEmpty(encoding)) {
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
}
return marshaller;
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
/**
* 创建UnMarshaller.
*/
public Unmarshaller createUnmarshaller() {
try {
return jaxbContext.createUnmarshaller();
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
/**
* 封装Root Element 是 Collection的情况.
*/
public static class CollectionWrapper {
@XmlAnyElement
protected Collection collection;
}
public Object fromXML(String fileName) {
return fromXML(new File(fileName));
}
public Object fromXML(File file) {
try {
Unmarshaller unmarshaller = createUnmarshaller();
return unmarshaller.unmarshal(file);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
public Object fromXML(InputStream stream) {
try {
Unmarshaller unmarshaller = createUnmarshaller();
return unmarshaller.unmarshal(stream);
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
}
这两个工具类也是由大佬提供,他们需要结合起来编写。
具体使用
对象转 soap_使用jaxb 实现对象与xml之间的转换
优点:解决了上面的返回对象只能转Map的问题
缺点:需要为每个接口的入参和结果集,封装一层SoapBody和SoapHeader,并且要在package-info.java编写命名空间。java版本低一点就没有package-info.java了。
什么事命名空间?
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://server.hikktn.com/">
<soapenv:Header/>
<soapenv:Body>
<ser:getArticle>
<!--Optional:-->
<arg0>
<!--Optional:-->
<guid>123</guid>
<!--Optional:-->
<name>true</name>
</arg0>
</ser:getArticle>
</soapenv:Body>
</soapenv:Envelope>
xmlns后面跟着就是命令空间,它会为下面的xml标签加上一个小前缀。
package-info.java
@XmlSchema(xmlns = {
@XmlNs(namespaceURI = "http://server.hikktn.com/", prefix = "ser"),
@XmlNs(namespaceURI = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soapenv")
})
package com.eci.tbms.memoq.domain.vo;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
cxf方式
首先,我们需要去下载Apache CXF
CXF官方下载地址
然后解压,cmd打开对应路径。
该方案适用于json格式发送请求。
可以生成去除该字段类型的 客户端代码 ; 比如 JAXBElement 可以变成 String 类型
XML
复制代码
<jaxb:bindings version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">
<jaxb:bindings>
<jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>
</jaxb:bindings>
第二步 : 生成客户端代码
LaTeX
复制代码
wsimport -encoding utf-8 -b remove.xml -Xnocompile http://xxxxxxxx?WSDL
-encoding utf-8 表示 编码
-Xnocompile 表示生成java代码, 不加的话, 将会 只生成class 文件
-b 表示绑定指定文件
此时生成的客户端代码中 , 原先的JAXBElement 字段类型 , 就会变成 String 类型;
controller
在controller层使用,会因为各种webservice生成的代码问题,造成一部分问题。
优点:完美的解决了各种xml转换问题。用JSON格式传输。
缺点:就是API生成了所有的API,代码厚重。并不能直接调用,还是需要编写controller。
这个方案我用stackoverflow的例子:
CheckTinService.java:
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.4-b01
* Generated source version: 2.2
*
*/
@WebServiceClient(name = "checkTinService", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin", wsdlLocation = "https://ec.europa.eu/taxation_customs/tin/checkTinService.wsdl")
public class CheckTinService
extends Service
{
private final static URL CHECKTINSERVICE_WSDL_LOCATION;
private final static WebServiceException CHECKTINSERVICE_EXCEPTION;
private final static QName CHECKTINSERVICE_QNAME = new QName("urn:ec.europa.eu:taxud:tin:services:checkTin", "checkTinService");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("https://ec.europa.eu/taxation_customs/tin/checkTinService.wsdl");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
CHECKTINSERVICE_WSDL_LOCATION = url;
CHECKTINSERVICE_EXCEPTION = e;
}
public CheckTinService() {
super(__getWsdlLocation(), CHECKTINSERVICE_QNAME);
}
public CheckTinService(WebServiceFeature... features) {
super(__getWsdlLocation(), CHECKTINSERVICE_QNAME);
}
public CheckTinService(URL wsdlLocation) {
super(wsdlLocation, CHECKTINSERVICE_QNAME);
}
public CheckTinService(URL wsdlLocation, WebServiceFeature... features) {
super(wsdlLocation, CHECKTINSERVICE_QNAME);
}
public CheckTinService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public CheckTinService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
super(wsdlLocation, serviceName);
}
/**
*
* @return
* returns CheckTinPortType
*/
@WebEndpoint(name = "checkTinPort")
public CheckTinPortType getCheckTinPort() {
return super.getPort(new QName("urn:ec.europa.eu:taxud:tin:services:checkTin", "checkTinPort"), CheckTinPortType.class);
}
/**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* @return
* returns CheckTinPortType
*/
@WebEndpoint(name = "checkTinPort")
public CheckTinPortType getCheckTinPort(WebServiceFeature... features) {
return super.getPort(new QName("urn:ec.europa.eu:taxud:tin:services:checkTin", "checkTinPort"), CheckTinPortType.class, features);
}
private static URL __getWsdlLocation() {
if (CHECKTINSERVICE_EXCEPTION!= null) {
throw CHECKTINSERVICE_EXCEPTION;
}
return CHECKTINSERVICE_WSDL_LOCATION;
}
}
CheckTinPortType.java
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.Holder;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
import eu.europa.ec.taxud.tin.services.checktin.types.ObjectFactory;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.4-b01
* Generated source version: 2.2
*
*/
@WebService(name = "checkTinPortType", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin")
@XmlSeeAlso({
ObjectFactory.class
})
public interface CheckTinPortType {
/**
*
* @param tinNumber
* @param countryCode
* @param validStructure
* @param requestDate
* @param validSyntax
*/
@WebMethod
@RequestWrapper(localName = "checkTin", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin:types", className = "eu.europa.ec.taxud.tin.services.checktin.types.CheckTin")
@ResponseWrapper(localName = "checkTinResponse", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin:types", className = "eu.europa.ec.taxud.tin.services.checktin.types.CheckTinResponse")
public void checkTin(
@WebParam(name = "countryCode", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin:types", mode = WebParam.Mode.INOUT)
Holder<String> countryCode,
@WebParam(name = "tinNumber", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin:types", mode = WebParam.Mode.INOUT)
Holder<String> tinNumber,
@WebParam(name = "requestDate", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin:types", mode = WebParam.Mode.OUT)
Holder<XMLGregorianCalendar> requestDate,
@WebParam(name = "validStructure", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin:types", mode = WebParam.Mode.OUT)
Holder<Boolean> validStructure,
@WebParam(name = "validSyntax", targetNamespace = "urn:ec.europa.eu:taxud:tin:services:checkTin:types", mode = WebParam.Mode.OUT)
Holder<Boolean> validSyntax);
}
ObjectFactory.java:
import com.exiqon.core.eu.europa.ec.taxud.tin.services.checktin.types.CheckTin;
import com.exiqon.core.eu.europa.ec.taxud.tin.services.checktin.types.CheckTinResponse;
import javax.xml.bind.annotation.XmlRegistry;
/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the eu.europa.ec.taxud.tin.services.checktin.types package.
* <p>An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {
/**
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: eu.europa.ec.taxud.tin.services.checktin.types
*
*/
public ObjectFactory() {
}
/**
* Create an instance of {@link eu.europa.ec.taxud.tin.services.checktin.types.CheckTin }
*
*/
public CheckTin createCheckTin() {
return new CheckTin();
}
/**
* Create an instance of {@link CheckTinResponse }
*
*/
public CheckTinResponse createCheckTinResponse() {
return new CheckTinResponse();
}
}
CheckTinResponse.java:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.datatype.XMLGregorianCalendar;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="countryCode" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="tinNumber" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="requestDate" type="{http://www.w3.org/2001/XMLSchema}date"/>
* <element name="validStructure" type="{http://www.w3.org/2001/XMLSchema}boolean"/>
* <element name="validSyntax" type="{http://www.w3.org/2001/XMLSchema}boolean" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"countryCode",
"tinNumber",
"requestDate",
"validStructure",
"validSyntax"
})
@XmlRootElement(name = "checkTinResponse")
public class CheckTinResponse {
@XmlElement(required = true)
protected String countryCode;
@XmlElement(required = true)
protected String tinNumber;
@XmlElement(required = true)
@XmlSchemaType(name = "date")
protected XMLGregorianCalendar requestDate;
protected boolean validStructure;
protected Boolean validSyntax;
/**
* Gets the value of the countryCode property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getCountryCode() {
return countryCode;
}
/**
* Sets the value of the countryCode property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setCountryCode(String value) {
this.countryCode = value;
}
/**
* Gets the value of the tinNumber property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getTinNumber() {
return tinNumber;
}
/**
* Sets the value of the tinNumber property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setTinNumber(String value) {
this.tinNumber = value;
}
/**
* Gets the value of the requestDate property.
*
* @return
* possible object is
* {@link XMLGregorianCalendar }
*
*/
public XMLGregorianCalendar getRequestDate() {
return requestDate;
}
/**
* Sets the value of the requestDate property.
*
* @param value
* allowed object is
* {@link XMLGregorianCalendar }
*
*/
public void setRequestDate(XMLGregorianCalendar value) {
this.requestDate = value;
}
/**
* Gets the value of the validStructure property.
*
*/
public boolean isValidStructure() {
return validStructure;
}
/**
* Sets the value of the validStructure property.
*
*/
public void setValidStructure(boolean value) {
this.validStructure = value;
}
/**
* Gets the value of the validSyntax property.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isValidSyntax() {
return validSyntax;
}
/**
* Sets the value of the validSyntax property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setValidSyntax(Boolean value) {
this.validSyntax = value;
}
}
CheckTin.java:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="countryCode" type="{http://www.w3.org/2001/XMLSchema}string"/>
* <element name="tinNumber" type="{http://www.w3.org/2001/XMLSchema}string"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"countryCode",
"tinNumber"
})
@XmlRootElement(name = "checkTin")
public class CheckTin {
@XmlElement(required = true)
protected String countryCode;
@XmlElement(required = true)
protected String tinNumber;
/**
* Gets the value of the countryCode property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getCountryCode() {
return countryCode;
}
/**
* Sets the value of the countryCode property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setCountryCode(String value) {
this.countryCode = value;
}
/**
* Gets the value of the tinNumber property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getTinNumber() {
return tinNumber;
}
/**
* Sets the value of the tinNumber property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setTinNumber(String value) {
this.tinNumber = value;
}
}
例子:
Im not sure if i got the question right, but i think i had your same problem a couple of months ago, so here is my solution:
First you need a HeaderHandler class , wich creates the soap header element, it should look like this:
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
String AUTH_TK = "http://www.myurl.com:port/subdir/etc/";
String NOPREFIX="";//no prefix
String PREFIX_XMLNS="xmlns";
String value = "123456";
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
//<AuthorizationToken xmlns="http://www.myurl.com:port/subdir/etc/">
SOAPElement authorizationToken = header.addChildElement("AuthorizationToken", PREFIX_XMLNS, AUTH_TK);
//<Token>value</Token>
SOAPElement usernameToken =
authorizationToken.addChildElement("Token", NOPREFIX);
usernameToken.addTextNode(value);
//<Token>value</Token>
SOAPElement usernameToken =
authorizationToken.addChildElement("Token", PREFIX);
usernameToken.addTextNode(value);
} catch (Exception e) {
e.printStackTrace();
}
}
return outboundProperty;
}
public Set<QName> getHeaders() {
return null;
}
public void close(MessageContext arg0) {
}
public boolean handleFault(SOAPMessageContext arg0) {
return false;
}
}
After that you create a HeaderHandlerResolver to handle the header creation and insert it in a handler chain:
import java.util.ArrayList;
import java.util.List;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
public class HeaderHandlerResolver implements HandlerResolver {
@SuppressWarnings("unchecked")
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerChain = new ArrayList<Handler>();
HeaderHandler hh = new HeaderHandler();
handlerChain.add(hh);
return handlerChain;
}
}
After that, you add in the Client:
public class WsTest {
public static void main(String[] args) {
try {
CheckTinService checkTinService = new CheckTinService();
checkTinService.setHandlerResolver(new HeaderHandlerResolver());
CheckTinPortType portType = checkTinService.getPort(CheckTinPortType.class);
Holder<String> code = new Holder<String>("DE");
Holder<String> tin = new Holder<String>("12346789");
Holder<XMLGregorianCalendar> requestDate = new Holder<>();
Holder<Boolean> validStructure = new Holder<>();
Holder<Boolean> validSyntax = new Holder<>();
portType.checkTin(code, tin, requestDate, validStructure, validSyntax);
System.out.println("requestDate : " + requestDate.value);
System.out.println("validStructure : " + validStructure.value);
System.out.println("validSyntax : " + validSyntax.value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
当然,如果只是简单的单体项目,你甚至可以直接注入Bean,那样非常方便使用。
题外话
关于WebService的超时问题
请参照这篇博客:Future 接口调用超时时间
参考链接
https://stackoverflow.com/questions/12459859/java-web-service-soap-how-do-i-add-a-soap-handler-on-the-client-side-and-e
https://stackoverflow.com/questions/29897160/how-to-use-code-generated-soap-client