如何在Spring Boot中不使用JAXB编解码调用SOAP服务?
不依赖JAXB的SOAP服务调用等效实现方案
核心思路
放弃JAXB自动编解码机制,改用手动构建SOAP请求XML并解析SOAP响应XML,结合Spring WebServiceTemplate完成服务调用。通过自定义Marshaller和Unmarshaller接口实现,替代原Jaxb2Marshaller的编解码能力。
1. 自定义编解码器(替代Jaxb2Marshaller)
自定义Marshaller(构建请求XML)
import org.springframework.oxm.Marshaller; import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Result; import javax.xml.transform.dom.DOMResult; public class PersonActivityMarshaller implements Marshaller { @Override public boolean supports(Class<?> clazz) { return PersonActivityList.class.isAssignableFrom(clazz); } @Override public void marshal(Object source, Result result) { PersonActivityList request = (PersonActivityList) source; try { // 开启命名空间支持,匹配SOAP服务的XML结构 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); Document doc = factory.newDocumentBuilder().newDocument(); // 构建请求根元素(需匹配WSDL定义的命名空间和元素名) org.w3c.dom.Element root = doc.createElementNS("http://test.com/cxfws", "personActivityList"); doc.appendChild(root); // 构建request子元素,填充业务数据 if (request.getRequest() != null) { org.w3c.dom.Element requestElem = doc.createElementNS("http://test.com/cxfws", "request"); // 示例:填充PersonActionRequest的字段(根据实际业务字段调整) org.w3c.dom.Element idElem = doc.createElement("id"); idElem.setTextContent(request.getRequest().getId()); requestElem.appendChild(idElem); org.w3c.dom.Element nameElem = doc.createElement("name"); nameElem.setTextContent(request.getRequest().getName()); requestElem.appendChild(nameElem); root.appendChild(requestElem); } // 将构建好的Document写入Result DOMResult domResult = (DOMResult) result; domResult.setNode(doc); } catch (Exception e) { throw new RuntimeException("构建SOAP请求XML失败", e); } } }
自定义Unmarshaller(解析响应XML)
import org.springframework.oxm.Unmarshaller; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; public class PersonActivityUnmarshaller implements Unmarshaller { @Override public boolean supports(Class<?> clazz) { return PersonActivityListResponse.class.isAssignableFrom(clazz); } @Override public Object unmarshal(Source source) { DOMSource domSource = (DOMSource) source; Document doc = (Document) domSource.getNode(); PersonActivityListResponse response = new PersonActivityListResponse(); try { // 定位响应根元素(需匹配WSDL定义的命名空间和元素名) NodeList resultNodeList = doc.getElementsByTagNameNS("http://test.com/cxfws", "personActivityListResult"); if (resultNodeList.getLength() > 0) { Element resultElem = (Element) resultNodeList.item(0); // 示例:解析响应字段(根据实际PersonActivityListResponse结构调整) NodeList statusNode = resultElem.getElementsByTagName("status"); if (statusNode.getLength() > 0) { response.setStatus(statusNode.item(0).getTextContent()); } // 解析列表类型字段(示例) NodeList activityNodes = resultElem.getElementsByTagName("activity"); // List<PersonActivity> activityList = new ArrayList<>(); // for (int i = 0; i < activityNodes.getLength(); i++) { // Element activityElem = (Element) activityNodes.item(i); // PersonActivity activity = new PersonActivity(); // activity.setId(activityElem.getElementsByTagName("id").item(0).getTextContent()); // activity.setType(activityElem.getElementsByTagName("type").item(0).getTextContent()); // activityList.add(activity); // } // response.setActivityList(activityList); } } catch (Exception e) { throw new RuntimeException("解析SOAP响应XML失败", e); } return response; } }
2. 修改配置类(替换Jaxb2Marshaller)
更新PersonConfig,用自定义编解码器替代原Jaxb2Marshaller:
@Configuration public class PersonConfig { @Value("${personactivity.uri}") String defaultUri; @Autowired TestConfig testConfig; @Autowired TESTHeaderInterceptor headerInterceptor; // 注册自定义Marshaller @Bean public Marshaller personActivityMarshaller() { return new PersonActivityMarshaller(); } // 注册自定义Unmarshaller @Bean public Unmarshaller personActivityUnmarshaller() { return new PersonActivityUnmarshaller(); } @Bean("PersonWebServiceTemplate") public WebServiceTemplate updateWebServiceTemplate() throws Exception { SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance()); messageFactory.afterPropertiesSet(); WebServiceTemplate updateWebServiceTemplate = new WebServiceTemplate(messageFactory); // 替换为自定义编解码器 updateWebServiceTemplate.setMarshaller(personActivityMarshaller()); updateWebServiceTemplate.setUnmarshaller(personActivityUnmarshaller()); updateWebServiceTemplate.setDefaultUri(defaultUri); updateWebServiceTemplate.setMessageSender(testConfig.createMessageSender()); updateWebServiceTemplate.afterPropertiesSet(); updateWebServiceTemplate.setInterceptors(new ClientInterceptor[]{headerInterceptor}); return updateWebServiceTemplate; } public String getDefaultUri() { return defaultUri; } }
3. 业务调用类(无需修改)
原业务调用逻辑可直接复用,WebServiceTemplate会自动使用自定义编解码器处理消息:
@Service public class PublicActivity{ @Autowired @Qualifier("PersonWebServiceTemplate") private WebServiceTemplate personWebServiceTemplate; public PersonActivityListResponse getPersonActivityList(PersonActivityList request) { return (PersonActivityListResponse) personWebServiceTemplate.marshalSendAndReceive(request); } }
关键注意事项
- 严格匹配XML结构:手动构建/解析XML时,必须完全匹配SOAP服务的命名空间、元素名和层级结构,可通过抓包或查看WSDL确认格式。
- 简化XML操作:若XML结构复杂,可引入
Apache Commons Digester或DOM4J框架简化节点解析,减少手动DOM操作的冗余代码。 - 底层SAAJ备选方案:也可直接使用SAAJ API构建完整SOAP消息,通过
WebServiceTemplate.sendAndReceive方法发送,可控性更强但代码量更大。
内容的提问来源于stack exchange,提问作者userKG
相关产品推荐
相关产品推荐

