我正在使用springboot为Web服务创建易于配置的模拟,我已经在我的项目中嵌入了"spring-boot-starter-web-services"工件。 我遵循了 spring-io 中有关如何配置端点的指南。但是我想实例化一个 bean,它将处理每个使用定义的命名空间传入的 soap 请求并处理响应创建。 我已经搜索了很多关于spring-webservice的参考,尝试了拦截器和侦听器,但没有成功,我总是得到404"找不到[SaajSoapMessage {myNamespace}MyRequest]的端点映射"。
我还使用 javassist 生成包含所有操作映射的端点注释类,但调度程序不加载此端点。
感谢您的帮助/建议,
编辑:
按照建议,我添加我的 Poc 存储库以显示我的 wip:https://github.com/Servhome/sb2-ws-sample
这是我运行应用程序时获得的起始日志:
2019-09-23 11:16:06.504 DEBUG 6184 --- [ restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Creating port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [ restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchByName] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [ restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchById] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.513 DEBUG 6184 --- [ restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider : Creating binding [{http://sample.com/int/Sample/v1}SamplePortTypeSoap11]
2019-09-23 11:16:06.519 DEBUG 6184 --- [ restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider : Creating service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.520 DEBUG 6184 --- [ restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider : Adding port [SamplePortTypeSoap11] to service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.529 DEBUG 6184 --- [ restartedMain] yloadRootAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.562 DEBUG 6184 --- [ restartedMain] oapActionAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.593 DEBUG 6184 --- [ restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodArgumentResolvers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@7c332390, org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver@4c642359, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@31c15cce, org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver@3c00b80a, org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver@594a23b9, org.springframework.ws.soap.server.endpoint.adapter.method.SoapHeaderElementMethodArgumentResolver@3459ad22, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@f45b04b, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@363c8941, org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver@3ee333d4]
2019-09-23 11:16:06.599 DEBUG 6184 --- [ restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodReturnValueHandlers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@47aaa997, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@65cf9ec1, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@376b65b8, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@10b19d12]
这表示 WSDL 定义工作正常,但不能将任何端点绑定到这些操作定义。 例如,我希望能够通过单个 Bean "SampleEndpoint" 处理来自命名空间 http://sample.com/int/Sample/v1 的每个请求。此外,能够使其可配置,因为我通过 CustomWsInitializer 类使 WSDL 定义可配置。
我设法找到了一个解决方案,该解决方案可以满足我对给定 wsdl 的可配置列表操作的全局 soap 端点的需求。
如果您对此类解决方案感兴趣,请参阅 https://github.com/Servhome/sb2-ws-sample 我将解释几个步骤的解决方案。
目标 #1 : 创建 wsdl 定义并按配置自动发布这些端点:
application-local.properties(GitHub link(
soap.endpoints.path=/services
soap.endpoints=Sample
soap.endpoints.Sample.wsdl.location=classpath:/xsd/Sample.xsd
soap.endpoints.Sample.portType.name=SamplePortType
soap.endpoints.Sample.target.namespace=http://sample.com/int/Sample/v1
下面是 wsdl 定义配置:wsdl 位置、端口类型名称和命名空间(请参阅 xsd 的目标命名空间(。
我在应用程序主中注册了一个上下文初始值设定项,CustomWsInitializer(github链接(:
private void registerEndpointService(GenericApplicationContext genericApplicationContext, Environment env, String endpointName, String locationUri) {
Resource resource = genericApplicationContext.getResource(env.getProperty(SOAP_ENDPOINTS + endpointName + ".wsdl.location"));
SimpleXsdSchema schema = new SimpleXsdSchema(resource);
genericApplicationContext.registerBean(endpointName + "Schema", SimpleXsdSchema.class, () -> schema);
String portTypeName = env.getProperty(SOAP_ENDPOINTS + endpointName + ".portType.name");
String targetNamespace = env.getProperty(SOAP_ENDPOINTS + endpointName + ".target.namespace");
genericApplicationContext.registerBean(endpointName + "Service",
DefaultWsdl11Definition.class,
() -> {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName(portTypeName);
wsdl11Definition.setLocationUri(locationUri);
wsdl11Definition.setTargetNamespace(targetNamespace);
wsdl11Definition.setSchema(schema);
return wsdl11Definition;
});
}
此实用工具方法首先注册 xsd 架构实例,然后根据以前的属性初始化配置的 DefaultWsdl11Definition。
如果此时运行应用,您将看到此 WsdlDefinition 已正确加载的确认日志:
2019-09-23 11:16:06.504 DEBUG 6184 --- [ restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Creating port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [ restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchByName] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [ restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchById] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.513 DEBUG 6184 --- [ restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider : Creating binding [{http://sample.com/int/Sample/v1}SamplePortTypeSoap11]
2019-09-23 11:16:06.519 DEBUG 6184 --- [ restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider : Creating service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.520 DEBUG 6184 --- [ restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider : Adding port [SamplePortTypeSoap11] to service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.529 DEBUG 6184 --- [ restartedMain] yloadRootAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.562 DEBUG 6184 --- [ restartedMain] oapActionAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.593 DEBUG 6184 --- [ restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodArgumentResolvers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@7c332390, org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver@4c642359, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@31c15cce, org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver@3c00b80a, org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver@594a23b9, org.springframework.ws.soap.server.endpoint.adapter.method.SoapHeaderElementMethodArgumentResolver@3459ad22, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@f45b04b, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@363c8941, org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver@3ee333d4]
2019-09-23 11:16:06.599 DEBUG 6184 --- [ restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodReturnValueHandlers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@47aaa997, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@65cf9ec1, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@376b65b8, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@10b19d12]
最后一个日志行表明 API 正在搜索端点,我带来了第二个目标(我卡了一会儿的原始目标(。
目标 #2: 创建可配置 Web 服务操作映射的动态单个端点:
application-local.properties(GitHub link(
soap.endpoints.Sample.operations.size=1
soap.endpoints.Sample.operations.0.localPart=searchByNameRequest
soap.endpoints.Sample.operations.0.requestType=com.sample._int.sample.v1.SearchByNameRequestType
soap.endpoints.Sample.operations.0.responseType=com.sample._int.sample.v1.GeneralResponseType
这些配置行指示哪些操作必须映射到我希望调度程序将请求转发到的全局终结点。
这是棘手的部分,我在我的项目中添加了javassist和速度伪影。原因有两个:能够自动生成带有映射的带注释的类。为此,我创建了一个要加载的方法模板(github链接(:
public javax.xml.transform.dom.DOMSource $localPart(javax.xml.transform.dom.DOMSource request) throws Exception {
org.slf4j.LoggerFactory.getLogger("custom.EndpointMapping").debug("Entered endpoint $localPart : " + request.toString());
return com.sample.controller.GlobalSoapEndpoint.handle(request, "$namespaceUri", "$localPart", "$requestType", "$responseType");
}
它由 MockedEndpointGenerator 类加载,该实用程序类生成一个带注释的编译类:
MockedEndpointGenerator.java (github链接(:
public static Class<?> generateMockEndpoint(MockEndpointDefinition def) {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass(def.getServiceName() + "Endpoint");
ClassFile classFile = cc.getClassFile();
ConstPool constpool = classFile.getConstPool();
classFile.addAttribute(addSingleAnnotation(constpool, Endpoint.class.getName()));
for (MockEndpointDefinition.MockOperation operation : def.getOperations()) {
try {
CtMethod mthd = CtNewMethod.make(templateMethod(operation, def.getNamespace()), cc);
ConstPool mthdConstPool = mthd.getMethodInfo().getConstPool();
// add method annotations
AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
Annotation[] annotations = new Annotation[]{
addAnnotation(mthdConstPool, ResponsePayload.class.getName()),
addAnnotation(mthdConstPool, PayloadRoot.class.getName(),
new String[][]{
new String[]{"namespace", def.getNamespace()},
new String[]{"localPart", operation.getLocalPart()}
}
)
};
annotationsAttribute.setAnnotations(annotations);
mthd.getMethodInfo().addAttribute(annotationsAttribute);
// add method's parameter annotation
ParameterAnnotationsAttribute parameterAttributeInfo = new ParameterAnnotationsAttribute(mthdConstPool, ParameterAnnotationsAttribute.visibleTag);
ConstPool parameterConstPool = parameterAttributeInfo.getConstPool();
Annotation annotation = addAnnotation(parameterConstPool, RequestPayload.class.getName());
Annotation[][] annotations2 = new Annotation[][]{
new Annotation[] {annotation}
};
parameterAttributeInfo.setAnnotations(annotations2);
mthd.getMethodInfo().addAttribute(parameterAttributeInfo);
cc.addMethod(mthd);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
try {
return cc.toClass();
} catch (Exception e) {
throw new IllegalStateException("Custom Endpoint class creation failed", e);
}
}
然后,该类被实例化并注册到 Spring 的上下文中。 然后,您在启动日志中看到 api 的确认:
2019-09-24 11:42:34.614 DEBUG 16220 --- [ restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider : Adding port [SamplePortTypeSoap11] to service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-24 11:42:34.621 DEBUG 16220 --- [ restartedMain] yloadRootAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5289cd7, started on Tue Sep 24 11:42:28 CEST 2019
2019-09-24 11:42:34.624 DEBUG 16220 --- [ restartedMain] yloadRootAnnotationMethodEndpointMapping : Mapped [{http://sample.com/int/Sample/v1}searchByNameRequest] onto endpoint [public javax.xml.transform.dom.DOMSource SampleEndpoint.searchByNameRequest(javax.xml.transform.dom.DOMSource) throws java.lang.Exception]
2019-09-24 11:42:34.649 DEBUG 16220 --- [ restartedMain] oapActionAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5289cd7, started on Tue Sep 24 11:42:28 CEST 2019
2019-09-24 11:42:34.690 DEBUG 16220 --- [ restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodArgumentResolvers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@1d88a335, org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver@5c309f36, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@349ea8a8, org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver@72d801b0, org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver@2a1185d5, org.springframework.ws.soap.server.endpoint.adapter.method.SoapHeaderElementMethodArgumentResolver@7200768e, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@331a4b8e, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@1fd09dc, org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver@27e40b1b]
2019-09-24 11:42:34.693 DEBUG 16220 --- [ restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodReturnValueHandlers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@5621ad6f, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@3706ca1e, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@5edc86cb, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@59e7f2d8]
下一步:使用单个控制器,我现在可以询问一个缓存管理器,该管理器按操作/场景保存模拟响应。
改进:使要通过操作调用的类可配置(但此目的已经通过使用@Endpoint创建带注释的类来完成(。