Spring WS端点如何为SOAP Body添加多个子元素
解决Spring WS中SOAP Body包含多个直接子元素的问题
我明白你的痛点——Spring WS默认会把返回对象作为SOAP Body的唯一子元素,导致你不得不额外加一层包装。要实现你想要的Body里同时存在<Response>和<Result>的结构,我们可以通过自定义消息处理逻辑来绕开这个限制,下面给你两种实用的方案:
方案一:自定义PayloadEndpoint直接操作SOAP消息
这种方式能最直接地控制SOAP Body的内容结构,适合需要高度自定义的场景:
- 先定义对应XML结构的POJO类(匹配你给出的响应示例):
// ResponseResult.java public class ResponseResult { @XmlAttribute(name = "href") private String href; // 构造方法、getter、setter省略 } // Response.java @XmlRootElement(name = "Response") public class Response { @XmlElement(name = "ResponseResult") private ResponseResult responseResult; // 构造方法、getter、setter省略 } // Result.java @XmlRootElement(name = "Result") public class Result { @XmlAttribute(name = "id") private String id; // 其他业务字段和getter、setter省略 }
- 创建自定义Endpoint,直接操作SOAP Body的DOM结构:
import org.springframework.ws.server.endpoint.PayloadEndpoint; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessage; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import org.w3c.dom.Document; import org.w3c.dom.Element; public class MultiElementResponseEndpoint implements PayloadEndpoint { @Override public Source invoke(Source request) throws Exception { // 1. 构建业务数据对象 Response response = new Response(); response.setResponseResult(new ResponseResult("#id1")); Result result = new Result(); result.setId("id1"); // 填充Result的其他业务字段 // 2. 获取SOAP Body的DOM文档 SoapMessage soapMessage = (SoapMessage) org.springframework.ws.context.MessageContextHolder.getRequest().getResponse(); Document document = ((SaajSoapMessage) soapMessage).getSaajMessage().getSOAPBody().getOwnerDocument(); // 3. 将Response序列化为XML元素并添加到Body JAXBContext jaxbContext = JAXBContext.newInstance(Response.class); Marshaller marshaller = jaxbContext.createMarshaller(); Element responseElement = document.createElementNS("你的业务命名空间", "Response"); marshaller.marshal(response, responseElement); soapMessage.getSoapBody().addDocumentContent(responseElement); // 4. 将Result序列化为XML元素并添加到Body jaxbContext = JAXBContext.newInstance(Result.class); marshaller = jaxbContext.createMarshaller(); Element resultElement = document.createElementNS("你的业务命名空间", "Result"); marshaller.marshal(result, resultElement); soapMessage.getSoapBody().addDocumentContent(resultElement); return new DOMSource(document); } }
- 在Spring配置中注册这个Endpoint:
<bean id="multiElementEndpoint" class="com.yourpackage.MultiElementResponseEndpoint"/> <sws:endpoint id="multiElementResponseMapping" endpoint-ref="multiElementEndpoint" uri="/your-service-path"/>
方案二:自定义MessageConverter适配注解式Endpoint
如果你习惯用@Endpoint注解开发,可以通过自定义消息转换器来支持多元素响应:
- 创建自定义消息转换器,处理多对象的序列化:
import org.springframework.ws.WebServiceMessage; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.endpoint.adapter.method.MessageConverter; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessage; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import org.w3c.dom.Document; import org.w3c.dom.Element; public class MultiElementMessageConverter implements MessageConverter { @Override public Object fromMessage(WebServiceMessage message, Class<?> targetClass) { // 按需实现请求消息的解析逻辑 return null; } @Override public void toMessage(Object payload, WebServiceMessage message, MessageContext messageContext) throws Exception { if (payload instanceof MultiResponseWrapper) { MultiResponseWrapper wrapper = (MultiResponseWrapper) payload; SoapMessage soapMessage = (SoapMessage) message; Document document = ((SaajSoapMessage) soapMessage).getSaajMessage().getSOAPBody().getOwnerDocument(); // 序列化Response并添加到Body JAXBContext jaxbContext = JAXBContext.newInstance(Response.class); Marshaller marshaller = jaxbContext.createMarshaller(); Element responseElement = document.createElementNS("你的业务命名空间", "Response"); marshaller.marshal(wrapper.getResponse(), responseElement); soapMessage.getSoapBody().addDocumentContent(responseElement); // 序列化Result并添加到Body jaxbContext = JAXBContext.newInstance(Result.class); marshaller = jaxbContext.createMarshaller(); Element resultElement = document.createElementNS("你的业务命名空间", "Result"); marshaller.marshal(wrapper.getResult(), resultElement); soapMessage.getSoapBody().addDocumentContent(resultElement); } } // 包装类,用于传递Response和Result public static class MultiResponseWrapper { private Response response; private Result result; // 构造方法、getter、setter省略 } }
- 在配置类中注册这个转换器:
@Configuration @EnableWs public class WebServiceConfig extends WsConfigurerAdapter { @Override public void configureMessageConverters(List<MessageConverter> messageConverters) { messageConverters.add(new MultiElementMessageConverter()); super.configureMessageConverters(messageConverters); } // 其他WSDL定义、Endpoint映射等配置省略 }
- 在注解式Endpoint中返回包装对象:
@Endpoint public class YourServiceEndpoint { @PayloadRoot(namespace = "你的业务命名空间", localPart = "YourRequest") @ResponsePayload public MultiElementMessageConverter.MultiResponseWrapper handleRequest(@RequestPayload YourRequest request) { Response response = new Response(); response.setResponseResult(new ResponseResult("#id1")); Result result = new Result(); result.setId("id1"); return new MultiElementMessageConverter.MultiResponseWrapper(response, result); } }
关键注意事项
- 替换代码中的
你的业务命名空间为实际的XML命名空间,确保和WSDL定义一致 - 测试时推荐使用SOAP UI验证返回的消息结构,确认
<Response>和<Result>直接作为soap:Body的子元素 - 如果使用JAXB序列化,确保POJO的注解和XML结构完全匹配,避免出现元素名称、属性不对应的问题
内容的提问来源于stack exchange,提问作者magoeke
相关产品推荐
相关产品推荐

