Spring Boot无请求对象时如何发送SOAP请求?ATMWebSvr场景
看起来你遇到的核心问题是这个WSDL是RPC/Literal风格,和常见的Document风格WSDL处理逻辑不一样,导致JAXB没有生成你预期的请求对象,同时SoapAction的配置也需要对应调整。我来一步步帮你解决:
为什么没有生成averbaCTeRequest这类请求对象?
你的WSDL里明确标注了soap:binding style="rpc",这意味着这是一个RPC风格的SOAP服务。在RPC风格下,请求参数是直接作为独立元素出现在SOAP体中的,而不是被包裹在一个统一的请求对象里。所以JAXB(或wsimport工具)不会生成类似averbaCTeRequest这样的封装类,而是会生成包含对应方法参数的接口——比如你会得到一个ATMWebSvrPortType接口,里面有averbaCTe(String usuario, String senha, String codatm, String xmlCTe)这样的方法签名。
第一步:正确生成JAXB类
你需要用专门的工具处理RPC风格的WSDL,推荐用wsimport命令或者Maven的jaxws-maven-plugin来生成类,而不是单纯用JAXB的基础工具。
方式1:使用wsimport命令
打开终端,执行以下命令(替换包名为你自己的项目包路径):
wsimport -keep -p com.yourproject.atm.wsdl http://webserver.averba.com.br/20/index.soap?wsdl
-keep:保留生成的.java文件-p:指定生成类的包名- 最后是WSDL的地址(可以直接用在线地址,也可以下载到本地后用本地路径)
方式2:使用Maven插件
在你的pom.xml中添加以下插件配置:
<plugin> <groupId>org.jvnet.jax-ws-commons</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>2.3</version> <executions> <execution> <goals> <goal>wsimport</goal> </goals> <configuration> <wsdlUrl>http://webserver.averba.com.br/20/index.soap?wsdl</wsdlUrl> <packageName>com.yourproject.atm.wsdl</packageName> <keep>true</keep> </configuration> </execution> </executions> </plugin>
执行mvn clean install后,插件会自动生成对应的JAXB类到指定包下。
第二步:调整Spring Boot客户端代码
方法1:直接使用WebServiceTemplate传递参数
不需要封装请求对象,直接把参数以数组形式传递,同时修正SoapAction的值(必须和WSDL中定义的一致):
public class AtmIntegracaoClient extends WebServiceGatewaySupport { private static final Logger log = LoggerFactory.getLogger(AtmIntegracaoClient.class); @Value("${atm.usuario}") private String usuario; @Value("${atm.senha}") private String senha; @Value("${atm.codatm}") private String codatm; @Value("${atm.url}") private String url; public Retorno averbaCte(String cteXmlStr) { log.info("Enviando requisição de averbação CTe para AT&M"); // RPC风格直接传递参数数组,顺序要和WSDL中定义的一致 Object[] requestParams = new Object[]{usuario, senha, codatm, cteXmlStr}; // 设置正确的SoapAction:和WSDL里的soap:operation soapAction值完全一致 Retorno response = (Retorno) getWebServiceTemplate().marshalSendAndReceive( url, requestParams, new SoapActionCallback("urn:ATMWebSvr#averbaCTe") ); log.info("Retorno recebido: {}", response); return response; } }
同时调整Marshaller的配置,确保支持RPC风格的序列化:
@Configuration public class AtmIntegracaoConfiguration { @Value("${atm.url}") private String url; @Bean public Jaxb2Marshaller marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); // 这里的包名要和你生成JAXB类的包完全一致 marshaller.setContextPath("com.yourproject.atm.wsdl"); // 添加RPC风格需要的属性 marshaller.setMarshallerProperties(Collections.singletonMap( javax.xml.bind.Marshaller.JAXB_FRAGMENT, true )); return marshaller; } @Bean public AtmIntegracaoClient atmIntegracaoClient(Jaxb2Marshaller marshaller) { AtmIntegracaoClient client = new AtmIntegracaoClient(); client.setDefaultUri(url); client.setMarshaller(marshaller); client.setUnmarshaller(marshaller); return client; } }
方法2:使用接口代理(更简洁)
利用生成的ATMWebSvrPortType接口,创建代理类直接调用方法,Spring WS会自动处理参数序列化和SoapAction:
首先在配置类中添加代理Bean:
@Bean public ATMWebSvrPortType atmWebSvrPortType(Jaxb2Marshaller marshaller) { WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller); webServiceTemplate.setDefaultUri(url); // 创建接口代理 return (ATMWebSvrPortType) Proxy.newProxyInstance( ATMWebSvrPortType.class.getClassLoader(), new Class[]{ATMWebSvrPortType.class}, new SoapServiceInvocationHandler(webServiceTemplate) ); }
然后在客户端中注入这个接口,直接调用方法:
@Component public class AtmIntegracaoClient { private static final Logger log = LoggerFactory.getLogger(AtmIntegracaoClient.class); @Value("${atm.usuario}") private String usuario; @Value("${atm.senha}") private String senha; @Value("${atm.codatm}") private String codatm; @Autowired private ATMWebSvrPortType atmWebSvrPortType; public Retorno averbaCte(String cteXmlStr) { log.info("Enviando requisição de averbação CTe"); // 直接调用接口方法,参数顺序和WSDL一致 return atmWebSvrPortType.averbaCTe(usuario, senha, codatm, cteXmlStr); } }
这种方式更符合面向接口的编程习惯,也省去了手动处理SoapAction的麻烦。
关键注意点
- SoapAction必须完全匹配:你之前代码里写的
http://localhost:8080/ATMWebSvr/averbaCTe是错误的,必须和WSDL中soap:operation soapAction="urn:ATMWebSvr#averbaCTe"完全一致。 - 参数顺序不能错:RPC风格对参数的顺序要求严格,必须和WSDL中
message定义的参数顺序一致。 - 确认Marshaller的上下文路径:必须指向生成JAXB类的包,否则会出现序列化/反序列化错误。
内容的提问来源于stack exchange,提问作者Eduardo Mayer

