如何让wsdl2java生成包装式响应类替代Holder封装的输出参数
问题场景
使用wsdl2java生成WebService客户端类时,遇到如下问题:接口方法(如Auth#login)的返回值始终为void,所有响应参数都被封装在Holder中,无法生成对应的LoginResponse类作为返回实例。
自动生成的Auth.java客户端接口示例:
@WebService(targetNamespace = "http://xml.kamsoft.pl/ws/auth", name = "Auth") @XmlSeeAlso({pl.kamsoft.xml.ws.common.ObjectFactory.class, pl.kamsoft.xml.ws.kaas.login_types.ObjectFactory.class}) @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) public interface Auth { // [...] - `logout`, `changePassword`, `changePasswordLog` 方法 @WebMethod(action = "login") public void login( @WebParam(partName = "request", name = "login", targetNamespace = "http://xml.kamsoft.pl/ws/kaas/login_types") pl.kamsoft.xml.ws.kaas.login_types.LoginRequest request, @WebParam(partName = "response", mode = WebParam.Mode.OUT, name = "loginReturn", targetNamespace = "http://xml.kamsoft.pl/ws/kaas/login_types") jakarta.xml.ws.Holder<java.lang.String> response, @WebParam(partName = "session", mode = WebParam.Mode.OUT, name = "session", targetNamespace = "http://xml.kamsoft.pl/ws/common", header = true) jakarta.xml.ws.Holder<pl.kamsoft.xml.ws.common.Session> session, @WebParam(partName = "token", mode = WebParam.Mode.OUT, name = "authToken", targetNamespace = "http://xml.kamsoft.pl/ws/common", header = true) jakarta.xml.ws.Holder<pl.kamsoft.xml.ws.common.AuthToken> token ) throws pl.kamsoft.wsdl.common.AuthenticationExceptionMsg, pl.kamsoft.wsdl.common.ServerExceptionMsg, PassExpiredExceptionMsg, pl.kamsoft.wsdl.common.AuthTokenExceptionMsg, pl.kamsoft.wsdl.common.InputExceptionMsg, pl.kamsoft.wsdl.common.AuthorizationExceptionMsg; }
对应的Gradle配置(build.gradle.kts):
plugins { java id("com.yupzip.wsdl2java") version "3.0.0" } java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } } repositories { mavenCentral() } dependencies { implementation("org.apache.cxf:cxf-rt-frontend-jaxws:4.0.0") implementation("org.apache.cxf:cxf-rt-transports-http-hc5:4.0.0") wsdl2java("com.sun.xml.bind:jaxb-impl:4.0.1") wsdl2java("org.apache.cxf.xjc-utils:cxf-xjc-runtime:4.0.0") wsdl2java("jakarta.xml.ws:jakarta.xml.ws-api:4.0.0") wsdl2java("com.sun.xml.ws:rt:4.0.0") wsdl2java("org.jvnet.jaxb2_commons:jaxb2-namespace-prefix:2.0") wsdl2java("codes.rafael.jaxb2_commons:jaxb2-basics-runtime:3.0.0") wsdl2java("codes.rafael.jaxb2_commons:jaxb2-basics:3.0.0") } wsdl2java { wsdlDir = file("$projectDir/src/main/resources/") includeJava8XmlDependencies = false cxfVersion = "4.0.0" cxfPluginVersion = "4.0.0" wsdlsToGenerate = listOf( listOf( "-wsdlLocation", "https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl", "-autoNameResolution", "https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl", ) ) }
核心疑问
如何强制wsdl2java生成可作为Auth#login方法返回实例的LoginResponse类?
已尝试使用绑定文件bindings.xml,但无论将enableWrapperStyle设为true或false都没有效果:
<bindings wsdlLocation="https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://java.sun.com/xml/ns/jaxws"> <enableWrapperStyle>false</enableWrapperStyle> </bindings>
对应的Gradle配置修改后:
wsdl2java { wsdlDir = file("$projectDir/src/main/resources/") includeJava8XmlDependencies = false cxfVersion = "4.0.0" cxfPluginVersion = "4.0.0" wsdlsToGenerate = listOf( listOf( "-b", "$projectDir/src/main/resources/wsdl/bindings.xml", "-wsdlLocation", "https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl", "-autoNameResolution", "https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl", ) ) }
另外,session和token被声明为wsdlsoap:header是否会影响生成的Java代码?
解决方案
1. 根本原因:BARE参数样式
当前生成的代码是因为WSDL采用了BARE参数样式(对应代码中的@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)),这种样式下JAX-WS不会自动将请求/响应包装为单个对象,而是拆分参数到Holder中。要生成带返回类型的方法,需要切换到WRAPPED样式。
2. 修正绑定文件配置
全局设置enableWrapperStyle无效,是因为WSDL本身的参数样式优先级更高,需要精准定位到portType或具体operation节点配置:
<bindings wsdlLocation="https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl" xmlns="http://java.sun.com/xml/ns/jaxws" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <!-- 针对整个Auth portType启用包装样式 --> <bindings node="wsdl:definitions/wsdl:portType[@name='Auth']"> <enableWrapperStyle>true</enableWrapperStyle> </bindings> <!-- 如需单独针对login方法配置,可使用下面这段 --> <!-- <bindings node="wsdl:definitions/wsdl:portType[@name='Auth']/wsdl:operation[@name='login']"> <enableWrapperStyle>true</enableWrapperStyle> </bindings> --> </bindings>
3. 添加CXF强制包装类参数
在wsdl2java的参数中加入-wrapperbean,强制生成响应包装类:
wsdl2java { wsdlDir = file("$projectDir/src/main/resources/") includeJava8XmlDependencies = false cxfVersion = "4.0.0" cxfPluginVersion = "4.0.0" wsdlsToGenerate = listOf( listOf( "-b", "$projectDir/src/main/resources/wsdl/bindings.xml", "-wsdlLocation", "https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl", "-autoNameResolution", "-wrapperbean", // 新增参数:强制生成包装类 "https://ewus.nfz.gov.pl/ws-broker-server-ewus-auth-test/services/Auth?wsdl", ) ) }
关于Header参数的影响
session和token作为wsdlsoap:header确实会影响代码生成:它们属于SOAP Header部分,不属于Body内容,因此即使启用包装样式,也会保持作为方法的Holder参数存在,不会被包含在LoginResponse类中。但Body部分的响应会被正常包装到LoginResponse作为方法返回值。
内容的提问来源于stack exchange,提问作者adriangl1997

