JDK17+SpringBoot3.3升级后Apache CXF无法返回自定义检查异常
问题
项目从JDK 11升级到JDK 17+Spring Boot 3.3后,使用Apache CXF(版本4.1.0)调用SOAP Web服务时,服务端抛出的自定义检查异常InputViolationException未被正确转换,客户端收到的是SOAPFaultException。调试发现CXF的ClientFaultConverter未完成转换,partInfo中的类型类名称为null。
相关代码与配置
自定义异常类
@WebFault(name = "InputViolationException", targetNamespace = "http://xmlns.hello.net/serviceException/2_1") public class InputViolationException extends Exception { private com.hello.InputViolationException faultInfo; public InputViolationException() { super(); } public InputViolationException(String message) { super(message); } public InputViolationException(String message, java.lang.Throwable cause) { super(message, cause); } public InputViolationException(String message, com.hello.InputViolationException inputViolationException) { super(message); this.faultInfo = inputViolationException; } public InputViolationException(String message, com.hello.InputViolationException inputViolationException, java.lang.Throwable cause) { super(message, cause); this.faultInfo = inputViolationException; } public com.hello.InputViolationException getFaultInfo() { return this.faultInfo; } }
CXF依赖与客户端Bean配置
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>4.1.0</version> </dependency>
@Bean public HelloService HelloPort() { JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setWsdlURL(String.valueOf(WSDL_URL)); factory.setServiceClass(HelloService.class); factory.setAddress(baseCcapsUrl); SoapInitializer.configureFactory(factory,new SoapInitializer.ConnectionParams(this.connectTimeout, this.requestTimeout), sanitizeXML); return (HelloService)factory.create(); }
服务端返回的SOAP响应
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Server</faultcode> <faultstring>This is an operation implementation generated fault</faultstring> <faultactor/> <detail xmlns:ns="http://xmlns.hello.net/serviceException/2_1" xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ns:InputViolationException> <serviceExceptionId>*****</serviceExceptionId> <exceptionName>***</exceptionName> <originatingComponentName>***</originatingComponentName> <detailedMessageText>Error occurred in a component</detailedMessageText> <exceptionHandledIndicator>true</exceptionHandledIndicator> <serviceExceptionSeverityType>Error</serviceExceptionSeverityType> <serviceExceptionType>InputViolationException</serviceExceptionType> </ns:InputViolationException> </detail> </SOAP-ENV:Fault> </SOAP-ENV:Body></SOAP-ENV:Envelope>
客户端报错堆栈
Caused by: jakarta.xml.ws.soap.SOAPFaultException: This is an operation implementation generated fault at org.apache.cxf.jaxws.JaxWsClientProxy.mapException(JaxWsClientProxy.java:195) at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:145) at jdk.proxy2/jdk.proxy2.$Proxy60.getEmployeeForADId(Unknown Source) at com.bmo.channels.docu.adapters.eice.EiceAdapter.getEmployee(EiceAdapter.java:50) at com.bmo.channels.docu.adapters.eice.EiceAdapterTest.getEmployeeFailure(EiceAdapterTest.java:76) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:76) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19) ... 25 more Caused by: org.apache.cxf.binding.soap.SoapFault: This is an operation implementation generated fault at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.unmarshalFault(Soap11FaultInInterceptor.java:87) at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:53)
解决方案
针对该问题,可从以下几个方面配置和排查:
同步WSDL与客户端异常定义
- 检查WSDL中
InputViolationException的fault定义,确保命名空间http://xmlns.hello.net/serviceException/2_1、元素结构与客户端faultInfo类完全匹配。 - 升级环境后建议重新通过
wsdl2java生成客户端代码,避免版本兼容导致的代码不一致问题。
- 检查WSDL中
自定义CXF异常转换器
实现自定义ClientFaultConverter,手动解析SOAP Fault的detail节点并转换为目标异常:public class CustomFaultConverter extends ClientFaultConverter { @Override public Exception convertFault(Message message, Throwable ex) { SoapFault soapFault = (SoapFault) ex; Element detailElement = soapFault.getDetail(); if (detailElement != null) { Node faultNode = detailElement.getElementsByTagNameNS("http://xmlns.hello.net/serviceException/2_1", "InputViolationException").item(0); if (faultNode != null) { try { JAXBContext context = JAXBContext.newInstance(com.hello.InputViolationException.class); com.hello.InputViolationException faultInfo = (com.hello.InputViolationException) context.createUnmarshaller().unmarshal(faultNode); return new InputViolationException(soapFault.getMessage(), faultInfo, soapFault); } catch (JAXBException e) { return super.convertFault(message, ex); } } } return super.convertFault(message, ex); } }将转换器注册到客户端工厂:
@Bean public HelloService HelloPort() { JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setWsdlURL(String.valueOf(WSDL_URL)); factory.setServiceClass(HelloService.class); factory.setAddress(baseCcapsUrl); // 注册自定义异常转换器 factory.getInInterceptors().add(new ClientFaultInterceptor(new CustomFaultConverter())); SoapInitializer.configureFactory(factory,new SoapInitializer.ConnectionParams(this.connectTimeout, this.requestTimeout), sanitizeXML); return (HelloService)factory.create(); }验证CXF与Spring Boot兼容性
- CXF 4.1.0兼容Spring Boot 3.x,但可尝试升级到CXF最新稳定版本(如4.1.x后续版本),修复已知的异常转换bug。
确认JAXB依赖配置
Spring Boot 3.x使用Jakarta EE API,确保已引入JAXB相关依赖:<dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <scope>runtime</scope> </dependency>
内容的提问来源于stack exchange,提问作者Suresh
相关产品推荐
相关产品推荐

