Spring-WS多版本WebService端点注册冲突问题求解
同一VM中运行同名命名空间/操作的多版本WebService端点解决方案
方案一:配置独立的MessageDispatcherServlet与EndpointMapping
Spring WS的端点由MessageDispatcherServlet管理,每个Servlet可绑定独立的EndpointMapping实例。通过为不同版本服务配置各自的Servlet和Mapping,实现端点隔离,避免注册键冲突。
示例配置代码:
@Configuration public class WebServiceConfig { // 版本1服务配置 @Bean(name = "v1MessageDispatcherServlet") public MessageDispatcherServlet v1MessageDispatcherServlet(ApplicationContext applicationContext) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); servlet.setApplicationContext(applicationContext); servlet.setTransformWsdlLocations(true); return servlet; } @Bean public ServletRegistrationBean<MessageDispatcherServlet> v1ServletRegistrationBean( @Qualifier("v1MessageDispatcherServlet") MessageDispatcherServlet servlet) { return new ServletRegistrationBean<>(servlet, "/v1/services/*"); } @Bean(name = "v1EndpointMapping") public PayloadRootAnnotationMethodEndpointMapping v1EndpointMapping() { PayloadRootAnnotationMethodEndpointMapping mapping = new PayloadRootAnnotationMethodEndpointMapping(); mapping.setBasePackage("com.example.webservice.v1"); // 仅扫描v1包端点 return mapping; } // 版本2服务配置 @Bean(name = "v2MessageDispatcherServlet") public MessageDispatcherServlet v2MessageDispatcherServlet(ApplicationContext applicationContext) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); servlet.setApplicationContext(applicationContext); servlet.setTransformWsdlLocations(true); return servlet; } @Bean public ServletRegistrationBean<MessageDispatcherServlet> v2ServletRegistrationBean( @Qualifier("v2MessageDispatcherServlet") MessageDispatcherServlet servlet) { return new ServletRegistrationBean<>(servlet, "/v2/services/*"); } @Bean(name = "v2EndpointMapping") public PayloadRootAnnotationMethodEndpointMapping v2EndpointMapping() { PayloadRootAnnotationMethodEndpointMapping mapping = new PayloadRootAnnotationMethodEndpointMapping(); mapping.setBasePackage("com.example.webservice.v2"); // 仅扫描v2包端点 return mapping; } }
两个版本服务分别通过/v1/services/*和/v2/services/*路径访问,各自的EndpointMapping独立管理端点,不存在注册键冲突问题。
方案二:自定义EndpointMapping重写注册键生成逻辑
继承Spring WS的PayloadRootAnnotationMethodEndpointMapping,重写注册键生成逻辑,在默认的「命名空间+操作名」基础上加入版本标识,确保键的唯一性。
自定义Mapping代码:
public class VersionedPayloadRootEndpointMapping extends PayloadRootAnnotationMethodEndpointMapping { private String versionIdentifier; public void setVersionIdentifier(String versionIdentifier) { this.versionIdentifier = versionIdentifier; } @Override protected String getLookupKeyForMethod(Method method) { // 获取默认注册键,拼接版本标识生成唯一键 String defaultKey = super.getLookupKeyForMethod(method); return versionIdentifier + ":" + defaultKey; } }
配置类中注册两个自定义Mapping:
@Configuration public class WebServiceConfig { @Bean(name = "v1EndpointMapping") public VersionedPayloadRootEndpointMapping v1EndpointMapping() { VersionedPayloadRootEndpointMapping mapping = new VersionedPayloadRootEndpointMapping(); mapping.setBasePackage("com.example.webservice.v1"); mapping.setVersionIdentifier("v1"); return mapping; } @Bean(name = "v2EndpointMapping") public VersionedPayloadRootEndpointMapping v2EndpointMapping() { VersionedPayloadRootEndpointMapping mapping = new VersionedPayloadRootEndpointMapping(); mapping.setBasePackage("com.example.webservice.v2"); mapping.setVersionIdentifier("v2"); return mapping; } }
此方案无需修改Servlet配置,通过自定义键生成逻辑解决冲突。
方案三:修改WSDL命名空间(需客户端配合)
若业务允许,为不同版本服务修改WSDL命名空间,在原有命名空间后添加版本后缀,使注册键自然区分。
示例端点代码:
// v1版本端点 @PayloadRoot(namespace = "urn:x.y.z:v1", localPart = "Req") @ResponsePayload public Res handleV1Req(@RequestPayload Req req) { // 处理逻辑 } // v2版本端点 @PayloadRoot(namespace = "urn:x.y.z:v2", localPart = "Req") @ResponsePayload public Res handleV2Req(@RequestPayload Req req) { // 处理逻辑 }
此方案简单直接,但需要客户端同步修改请求的命名空间,若无法协调客户端则不适用。
内容的提问来源于stack exchange,提问作者Marinos An
相关产品推荐
相关产品推荐

