在不断寻求开发应用程序的最佳方法的过程中,我最近对Web服务感兴趣,尤其是对合同优先的Web服务。 当首先设计WSDL并从中生成类时,Web服务首先遵循合同。 由于WSDL与基础技术无关,因此它们被认为是Web服务中最可互操作的。
过去,我一直使用Axis2,然后使用CXF,但是现在,JavaEE为我们提供了JAX-WS(针对SOAP,针对REST的JAX-RS)的强大功能。 还有一个Spring Web Services子项目。
我的第一个目标是检查通过Spring注入这两种技术有多么容易,但是在我开发的过程中,我遇到了其他比较领域。
使用Spring Web Services,发布新服务需要3个步骤:
MessageDispatcherServlet
。
<web-appversion="2.5"...>
<context-param>
<param-name> contextConfigLocation </param-name>
<param-value> classpath:/applicationContext.xml </param-value>
</context-param>
<servlet>
<servlet-name> spring-ws </servlet-name>
<servlet-class> org.springframework.ws.transport.http.MessageDispatcherServlet </servlet-class>
<init-param>
<param-name> transformWsdlLocations </param-name>
<param-value> true </param-value>
</init-param>
<init-param>
<param-name> contextConfigLocation </param-name>
<param-value> classpath:/spring-ws-servlet.xml </param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name> spring-ws </servlet-name>
<url-pattern> /spring/* </url-pattern>
</servlet-mapping>
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>
</web-app>
@Endpoint
对其进行@Endpoint
。 使用@PayloadRoot
注释相关的服务方法。 将方法参数与@RequestPayload
绑定,并将返回类型与@ResponsePayload
。
@Endpoint
publicclassFindPersonServiceEndpoint{
privatefinalPersonServicepersonService;
publicFindPersonServiceEndpoint(PersonServicepersonService){
this.personService=personService;
}
@PayloadRoot(localPart="findPersonRequestType",namespace="http://blog.frankel.ch/ws-inject")
@ResponsePayload
publicFindPersonResponseTypefindPerson(@RequestPayloadFindPersonRequestTypeparameters){
returnnewFindPersonDelegate().findPerson(personService,parameters);
}
}
<beans...>
<sws:annotation-driven/>
<beanclass="ch.frankel.blog.wsinject.impl.FindPersonServiceEndpoint">
<constructor-arg>
<refbean="personService"/>
</constructor-arg>
</bean>
</beans>
对于JAX-WS(地铁实施),过程非常相似:
<web-appversion="2.5"...>
<servlet>
<servlet-name> jaxws-servlet </servlet-name>
<servlet-class> com.sun.xml.ws.transport.http.servlet.WSServlet </servlet-class>
</servlet>
<servlet-mapping>
<servlet-name> jaxws-servlet </servlet-name>
<url-pattern> /jax/* </url-pattern>
</servlet-mapping>
<listener>
<listener-class> com.sun.xml.ws.transport.http.servlet.WSServletContextListener </listener-class>
</listener>
</web-app>
@WebService(endpointInterface="ch.frankel.blog.wsinject.jaxws.FindPersonPortType")
publicclassFindPersonSoapImplextendsSpringBeanAutowiringSupportimplementsFindPersonPortType{
@Autowired
privatePersonServicepersonService;
@Override
publicFindPersonResponseTypefindPerson(FindPersonRequestTypeparameters){
returnnewFindPersonDelegate().findPerson(personService,parameters);
}
}
<endpointsversion="2.0"...>
<endpointname="FindPerson"
implementation="ch.frankel.blog.wsinject.impl.FindPersonSoapImpl"
url-pattern="/jax/findPerson"/>
</endpoints>
在Spring或JAX-WS中创建Web服务需要相同数量的步骤,并且具有相同的复杂性。
在这两种情况下,我们都需要从Web服务定义文件(WSDL)生成Java类。 这完全与所选技术无关。
但是,尽管JAX-WS使用所有生成的Java类,但是Spring WS仅使用映射到WSD类型和元素的类:通过映射请求和响应类型,将WS调用连接到正确的端点。
在JAX-RS中创建Web服务只是实现包含服务方法的端口类型接口的问题。
在Spring中,必须使用@Endpoint
注释服务类, @Endpoint
将其识别为服务类。
在JAX-RS中,sun-jaxws.xml文件语法使我们可以很好地配置每个URL如何映射到特定的Web服务。
在Spring WS中,没有这样的配置。
由于我希望对不同的URL有所了解,因此我的喜好转向JAX-WS。
由于<sws:annotation-driven />
部分,该服务已经是Spring bean,因此在Spring WS中注入bean非常容易。
相反,注入Spring bean要求我们的JAX-WS实现从SpringBeanAutowiringSupport
继承,这使我们无法拥有自己的层次结构。 另外,我们当时禁止使用显式XML接线。
使用Spring WS进行依赖注入会更容易(但这是预料之中的)。
JAX-WS和Spring WS都可以公开WSDL。 为此,JAX-WS使用生成的类,因此,公开的WSDL与我们最初设计的WSDL是相同的。
相反,Spring为我们提供了两种选择:
<soap:address>
部分中的域和端口, Spring WS具有JAX-WS缺少的一项功能:集成测试。 可以创建一个将用Spring Beans定义文件配置的测试类,以断言发送的输出消息和已知的输入。 这是一个基于Spring测试和TestNG的测试示例:
@ContextConfiguration(locations={"classpath:/spring-ws-servlet.xml","classpath:/applicationContext.xml"})
publicclassFindPersonServiceEndpointITextendsAbstractTestNGSpringContextTests{
@Autowired
privateApplicationContextapplicationContext;
privateMockWebServiceClientclient;
@BeforeMethod
protectedvoidsetUp(){
client=MockWebServiceClient.createClient(applicationContext);
}
@Test
publicvoidfindRequestPayloadShouldBeSameAsExpected(Stringrequest,StringexpectedResponse)
throwsDatatypeConfigurationException{
intid=5;
SourcerequestPayload=newStringSource(request);
SourceexpectedResponsePayload=newStringSource(expectedResponse);
Stringrequest="<a:findPersonRequestType xmlns:a='http://blog.frankel.ch/ws-inject'><id>"
+id+"</id></a:findPersonRequestType>";
GregorianCalendarcalendar=newGregorianCalendar();
XMLGregorianCalendarxmlCalendar=DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);
xmlCalendar.setHour(FIELD_UNDEFINED);
xmlCalendar.setMinute(FIELD_UNDEFINED);
xmlCalendar.setSecond(FIELD_UNDEFINED);
xmlCalendar.setMillisecond(FIELD_UNDEFINED);
StringexpectedResponse="<ns3:findPersonResponseTypexmlns:ns3='http://blog.frankel.ch/ws-inject'><person><id>"
+id
+"</id><firstName>John</firstName><lastName>Doe</lastName><birthdate>"
+xmlCalendar.toXMLFormat()
+"</birthdate></person></ns3:findPersonResponseType>";
client.sendRequest(withPayload(requestPayload)).andExpect(payload(expectedResponsePayload));
}
}
请注意,我遇到了有关XML后缀的问题,因为不仅检查了名称空间,还检查了前缀名称本身(这是一个糟糕的主意)。
一方面,JAX-WS固有地解决了包含/导入的模式而没有任何故障。
另一方面,我们需要在Spring上下文中添加特定的bean,以便能够使用Spring WS做到这一点:
<!-- Let us reference XML schemas -->
<beanclass="org.springframework.ws.transport.http.XsdSchemaHandlerAdapter"/>
<!-- Let us resolve person.xsd reference in the WSDL -->
<beanid="person"class="org.springframework.xml.xsd.SimpleXsdSchema">
<propertyname="xsd"value="classpath:/wsdl/person.xsd"/>
</bean>
JAX-WS在JAX-WS Servlet映射的根目录提供了所有已发布服务的概述:
地址 | 信息 | ||
---|---|---|---|
Service name : | {http://impl.wsinject.blog.frankel.ch/} FindPersonSoapImplService | Adress : | |
Port name : | {http://impl.wsinject.blog.frankel.ch/} FindPersonSoapImplPort | WSDL: | |
Implementation class : | ch.frankel.blog.wsinject.impl.FindPersonSoapImpl |
考虑到合同优先的Web服务,我很少的经验促使我选择JAX-WS来支持Spring WS:测试的好处并不能平衡该标准的易用性。 我必须承认,对于这些结果我感到有些惊讶,因为大多数Spring组件比标准组件更易于使用和配置,但是结果在这里。
您可以在[此处]找到本文的资源(资产/资源/web-services-jax-ws-vs-spring/wsinject-example-1.0.0.zip)。
注意:使用的JAX-WS版本是2.2,因此Maven POM相当复杂,以覆盖Java 6本机JAX-WS 2.1类。