Spring WS处理SOAP请求报No adapter for endpoint异常排查
问题现象
使用Spring WS开发SOAP服务时,发送请求后日志显示已经成功匹配到对应的@Endpoint端点方法,但抛出如下异常:
java.lang.IllegalStateException: No adapter for endpoint [public localhost.soap.GetAllUserResponse com.driypeen.userServiceSOAP.endpoint.UserEndpoint.getAllUsers(localhost.soap.GetAllUserRequest)]: Is your endpoint annotated with @Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint?
将日志级别设置为trace后,请求处理流程日志如下:
2022-06-18 12:08:33.341 DEBUG 11612 --- [nio-8080-exec-1] yloadRootAnnotationMethodEndpointMapping : Looking up endpoint for [{localhost/soap}GetAllUser] 2022-06-18 12:08:33.341 DEBUG 11612 --- [nio-8080-exec-1] o.s.w.soap.server.SoapMessageDispatcher : Endpoint mapping [org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping@113c4ad6] maps request to endpoint [public localhost.soap.GetAllUserResponse com.driypeen.userServiceSOAP.endpoint.UserEndpoint.getAllUsers(localhost.soap.GetAllUserRequest)] Testing endpoint adapter [org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter@1f6f0fe2] 2022-06-18 12:08:33.343 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@352c3d70] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.344 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver@4c13ca07] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.344 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@7d17906] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.344 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver@97beeaf] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.345 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver@5d68954d] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.345 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.soap.server.endpoint.adapter.method.SoapHeaderElementMethodArgumentResolver@f793f15] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.345 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@3ec7eb5] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.351 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@5534e6f1] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.352 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.dom.JDomPayloadMethodProcessor@4c6fc3e7] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.352 TRACE 11612 --- [nio-8080-exec-1] o.s.w.s.e.a.DefaultMethodEndpointAdapter : Testing if argument resolver [org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver@aa8dce8] supports [class localhost.soap.GetAllUserRequest] 2022-06-18 12:08:33.353 DEBUG 11612 --- [nio-8080-exec-1] o.s.w.soap.server.SoapMessageDispatcher : Testing endpoint adapter [org.springframework.ws.server.endpoint.adapter.PayloadEndpointAdapter@ab2009f] 2022-06-18 12:08:33.353 DEBUG 11612 --- [nio-8080-exec-1] s.e.SoapFaultAnnotationExceptionResolver : Resolving exception from endpoint [public localhost.soap.GetAllUserResponse com.driypeen.userServiceSOAP.endpoint.UserEndpoint.getAllUsers(localhost.soap.GetAllUserRequest)]: java.lang.IllegalStateException: No adapter for endpoint [public localhost.soap.GetAllUserResponse com.driypeen.userServiceSOAP.endpoint.UserEndpoint.getAllUsers(localhost.soap.GetAllUserRequest)]: Is your endpoint annotated with @Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint? 2022-06-18 12:08:33.354 DEBUG 11612 --- [nio-8080-exec-1] o.s.w.s.s.e.SimpleSoapExceptionResolver : Resolving exception from endpoint [public localhost.soap.GetAllUserResponse com.driypeen.userServiceSOAP.endpoint.UserEndpoint.getAllUsers(localhost.soap.GetAllUserRequest)]: java.lang.IllegalStateException: No adapter for endpoint [public localhost.soap.GetAllUserResponse com.driypeen.userServiceSOAP.endpoint.UserEndpoint.getAllUsers(localhost.soap.GetAllUserRequest)]: Is your endpoint annotated with @Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint? 2022-06-18 12:08:33.370 DEBUG 11612 --- [nio-8080-exec-1] o.s.w.soap.server.SoapMessageDispatcher : Endpoint invocation resulted in exception - responding with Fault
为了确认容器内的适配器实例,添加如下检查代码:
@Autowired List<EndpointAdapter> adapters; @PostConstruct public void a () { log.info("ADAPTERS:"); adapters.forEach(System.out::println); }
启动后输出显示两个适配器都已存在:
2022-06-18 12:07:03.106 INFO 11612 --- [ main] c.d.u.endpoint.UserEndpoint : ADAPTERS: org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter@1f6f0fe2 org.springframework.ws.server.endpoint.adapter.PayloadEndpointAdapter@ab2009f
相关代码与配置
- XSD结构定义:
<xs:element name="GetAllUserRequest"> <xs:complexType> <xs:sequence> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="GetAllUserResponse"> <xs:complexType> <xs:sequence> <xs:element name="users" type="tns:UserWithoutRole" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element>
- JAXB生成的请求类:
@XmlAccessorType(XmlAccessType.FIELD) @XmlType( name = "" ) @XmlRootElement( name = "GetAllUserRequest" ) public class GetAllUserRequest { public GetAllUserRequest() { } }
- 测试SOAP请求:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:gs="localhost/soap"> <soapenv:Header/> <soapenv:Body> <gs:GetAllUser/> </soapenv:Body> </soapenv:Envelope>
- 端点类定义:
@Endpoint @Slf4j public class UserEndpoint { private static final String NAMESPACE_URI = "localhost/soap"; private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } @Autowired List<EndpointAdapter> adapters; @PostConstruct public void a () { log.info("ADAPTERS:"); adapters.forEach(System.out::println); } @PayloadRoot(localPart = "GetAllUser", namespace = NAMESPACE_URI) @ResponsePayload public GetAllUserResponse getAllUsers(@RequestPayload GetAllUserRequest request) { log.info("GET ALL USERS"); GetAllUserResponse response = new GetAllUserResponse(); response.getUsers().addAll(userService.findAll()); return response; } }
- Web服务配置类:
@EnableWs @Configuration public class WebServiceConfig extends WsConfigurerAdapter { @Bean public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); servlet.setApplicationContext(applicationContext); servlet.setTransformWsdlLocations(true); return new ServletRegistrationBean(servlet, "/ws/*"); } @Bean(name = "soap") public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) { DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition(); wsdl11Definition.setPortTypeName("SoapPort"); wsdl11Definition.setLocationUri("/ws"); wsdl11Definition.setTargetNamespace("localhost/soap"); wsdl11Definition.setSchema(countriesSchema); return wsdl11Definition; } @Bean public XsdSchema countriesSchema() { return new SimpleXsdSchema(new ClassPathResource("userServiceSOAP.xsd")); } @Bean public EndpointAdapter messageEndpointAdapter() { return new PayloadEndpointAdapter(); } }
问题根因
配置类中手动注册的PayloadEndpointAdapter Bean破坏了Spring WS默认的适配器装配逻辑。
Spring WS的自动配置规则为:如果应用上下文中不存在手动注册的EndpointAdapter类型Bean,框架会自动初始化DefaultMethodEndpointAdapter,并为其注入全量的参数解析器、返回值处理器,包括支持JAXB类的XmlRootElementPayloadMethodProcessor、DOM解析处理器等。一旦手动注册了任意EndpointAdapter Bean,框架会判定开发者需要自定义适配器链,不会再为默认的DefaultMethodEndpointAdapter注入内置的参数解析器,这也是日志中所有参数解析器都判定不支持GetAllUserRequest类型的原因,最终没有适配器能够匹配注解式编写的端点方法。PayloadEndpointAdapter仅支持实现了PayloadEndpoint接口的端点类,完全不适配@PayloadRoot注解标记的方法,手动注册这个Bean没有实际作用。
修复方案
直接删除WebServiceConfig中如下多余的Bean定义即可:
@Bean public EndpointAdapter messageEndpointAdapter() { return new PayloadEndpointAdapter(); }
删除后重启服务,框架会自动为DefaultMethodEndpointAdapter装配JAXB等类型的参数处理器,即可正常识别并调用对应的端点方法。
内容的提问来源于stack exchange,提问作者driypeen

