当前位置: 首页 > 工具软件 > JAX-WS > 使用案例 >

Web服务:JAX-WS与Spring

仲学真
2023-12-01

在不断寻求开发应用程序的最佳方法的过程中,我最近对Web服务感兴趣,尤其是对合同优先的Web服务。 当首先设计WSDL并从中生成类时,Web服务首先遵循合同。 由于WSDL与基础技术无关,因此它们被认为是Web服务中最可互操作的。

过去,我一直使用Axis2,然后使用CXF,但是现在,JavaEE为我们提供了JAX-WS(针对SOAP,针对REST的JAX-RS)的强大功能。 还有一个Spring Web Services子项目。

我的第一个目标是检查通过Spring注入这两种技术有多么容易,但是在我开发的过程中,我遇到了其他比较领域。

总览

使用Spring Web Services,发布新服务需要3个步骤:

  1. 在Web部署描述符中添加Spring 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>
  2. 创建Web服务类,并使用@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);
        }
    }
  3. 在相关的Spring Bean定义文件中将Web服务类配置为Spring Bean。
    <beans...>
        <sws:annotation-driven/>
        <beanclass="ch.frankel.blog.wsinject.impl.FindPersonServiceEndpoint">
            <constructor-arg>
                <refbean="personService"/>
            </constructor-arg>
        </bean>
    </beans>

对于JAX-WS(地铁实施),过程非常相似:

  1. 在Web部署描述符中添加WSServlet:
    <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>
  2. 创建Web服务类:
    @WebService(endpointInterface="ch.frankel.blog.wsinject.jaxws.FindPersonPortType")
    
    publicclassFindPersonSoapImplextendsSpringBeanAutowiringSupportimplementsFindPersonPortType{
    
        @Autowired
        privatePersonServicepersonService;
    
        @Override
        publicFindPersonResponseTypefindPerson(FindPersonRequestTypeparameters){
            returnnewFindPersonDelegate().findPerson(personService,parameters);
        }
    }
  3. 最后,我们还必须在标准Metro配置文件(sun-jaxws.xml)中配置服务:
    <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调用连接到正确的端点。

Web服务本身

在JAX-RS中创建Web服务只是实现包含服务方法的端口类型接口的问题。

在Spring中,必须使用@Endpoint注释服务类, @Endpoint将其识别为服务类。

URL配置

在JAX-RS中,sun-jaxws.xml文件语法使我们可以很好地配置每个URL如何映射到特定的Web服务。

在Spring WS中,没有这样的配置。

由于我希望对不同的URL有所了解,因此我的喜好转向JAX-WS。

Spring依赖注入

由于<sws:annotation-driven />部分,该服务已经是Spring bean,因此在Spring WS中注入bean非常容易。

相反,注入Spring bean要求我们的JAX-WS实现从SpringBeanAutowiringSupport继承,这使我们无法拥有自己的层次结构。 另外,我们当时禁止使用显式XML接线。

使用Spring WS进行依赖注入会更容易(但这是预料之中的)。

公开WSDL

JAX-WS和Spring WS都可以公开WSDL。 为此,JAX-WS使用生成的类,因此,公开的WSDL与我们最初设计的WSDL是相同的。

相反,Spring为我们提供了两种选择:

  • 要么使用静态WSDL替换掉<soap:address>部分中的域和端口,
  • 或从XSD文件生成动态WSDL,在这种情况下,设计的文件和生成的文件不相同

整合测试

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 :

http://localhost:8080/wsinject/jax/findPerson

Port name :

{http://impl.wsinject.blog.frankel.ch/} FindPersonSoapImplPort

WSDL:

http://localhost:8080/wsinject/jax/findPerson?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类。

翻译自: https://blog.frankel.ch/web-services-jax-ws-vs-spring/

 类似资料: