天天看点

Spring-ws提供SOAP服务的注意事项

本文在官方例子的基础上,使用JAXB marshall 和 unmarshall 处理 xml数据

依照官方的例子,我们需要处理一个假期的请求,以下是Holiday的xml格式:

<Holiday xmlns="http://mycompany.com/hr/schemas">
    <StartDate>2006-07-03</StartDate>
    <EndDate>2006-07-07</EndDate>
</Holiday>
           

以下是Employee的xml格式:

<Employee xmlns="http://mycompany.com/hr/schemas">
    <Number>42</Number>
    <FirstName>Arjen</FirstName>
    <LastName>Poutsma</LastName>
</Employee>
           

那么一个请求就是下面的xml格式:

<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
    <Holiday>
        <StartDate>2006-07-03</StartDate>
        <EndDate>2006-07-07</EndDate>
    </Holiday>
    <Employee>
        <Number>42</Number>
        <FirstName>Arjen</FirstName>
        <LastName>Poutsma</LastName>
    </Employee>
</HolidayRequest>
           

我们需要为这个请求定义一个schema文件hr.xsd:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:hr="http://mycompany.com/hr/schemas"
        elementFormDefault="qualified"
        targetNamespace="http://mycompany.com/hr/schemas">
    <xs:element name="HolidayRequest">
        <xs:complexType>
            <xs:all>
                <xs:element name="Holiday" type="hr:HolidayType"/> 
                <xs:element name="Employee" type="hr:EmployeeType"/>
            </xs:all>
        </xs:complexType>
    </xs:element>
    <xs:complexType name="HolidayType">
        <xs:sequence>
            <xs:element name="StartDate" type="xs:date"/>
            <xs:element name="EndDate" type="xs:date"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="EmployeeType">
        <xs:sequence>
            <xs:element name="Number" type="xs:integer"/>
            <xs:element name="FirstName" type="xs:string"/>
            <xs:element name="LastName" type="xs:string"/>  
        </xs:sequence>                  
    </xs:complexType>
</xs:schema>
           

在web.xml文件中添加处理soap的servlet:

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
             http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         version="2.4">

    <display-name>MyCompany HR Holiday Service</display-name>

    <!-- take especial notice of the name of this servlet -->
    <servlet>
        <servlet-name>spring-ws</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/spring-ws.xml</param-value>
        </init-param>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-ws</servlet-name>
        <url-pattern>/ws/*</url-pattern>
    </servlet-mapping>
</web-app>
           

像SpringMVC有一个配置文件一样,spring-ws.xml是spring-ws的配置文件。这里解释一下MessageDispatcherServlet,Spring-ws的服务端是围绕着这个servlet设计的,这个servlet将收到的xml报文转发到endpoint,这一点跟SpringMVC的DispacherServlet很相似。MessageDispatcherServlet处理请求的转发过程如下图所示。

Spring-ws提供SOAP服务的注意事项

spring-ws.xml的内容如下。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:sws="http://www.springframework.org/schema/web-services"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="com.chinamobile.cmss.eshub.ssb.wssimulator" />

    <sws:annotation-driven />

    <sws:dynamic-wsdl id="holiday" portTypeName="HumanResource" locationUri="/ws/holidayService/" targetNamespace="http://mycompany.com/hr/definitions">
        <sws:xsd location="classpath:schema/hr.xsd" />
    </sws:dynamic-wsdl>
</beans>
           

我们开启自动扫描@Endpoint注解的类。接着定义暴露接口的wsdl路径。

下面开始写Endpoint。

package com.chinamobile.cmss.eshub.ssb.wssimulator.endpoint;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;

import com.chinamobile.cmss.eshub.ssb.wssimulator.entity.HolidayRequest;

@Endpoint
public class HolidayEndpoint {

  private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";

  @PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")
  public void handleHolidayRequest(@RequestPayload HolidayRequest holidayRequest)
      throws Exception {
      System.out.println(holidayRequest.toString());
  }

}
           

这里使用了eclipselink的JAXB,项目的pom.xml需要特殊加入的配置如下。

<dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-core</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.3</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>eclipselink</artifactId>
            <version>2.5.0</version>
        </dependency>
           

编写Holiday.java实体类如下,注意与xml

date

对应的java类型不是

Date

,而是

XMLGregorianCalendar

,xml与java数据类型的对应关系参考这里。

package com.chinamobile.cmss.eshub.ssb.wssimulator.entity;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.datatype.XMLGregorianCalendar;

@XmlAccessorType(XmlAccessType.FIELD)
public class Holiday {

    @XmlElement(namespace="http://mycompany.com/hr/schemas")
    private XMLGregorianCalendar StartDate;
    @XmlElement(namespace="http://mycompany.com/hr/schemas")
    private XMLGregorianCalendar EndDate;

    public XMLGregorianCalendar getStartDate() {
        return StartDate;
    }
    public void setStartDate(XMLGregorianCalendar startDate) {
        StartDate = startDate;
    }
    public XMLGregorianCalendar getEndDate() {
        return EndDate;
    }
    public void setEndDate(XMLGregorianCalendar endDate) {
        EndDate = endDate;
    }
    @Override
    public String toString() {
        return "Holiday [StartDate=" + StartDate + ", EndDate=" + EndDate + "]";
    }


}
           

编写Employee.java如下。

package com.chinamobile.cmss.eshub.ssb.wssimulator.entity;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {

    @XmlElement(namespace="http://mycompany.com/hr/schemas")
    private int Number;
    @XmlElement(namespace="http://mycompany.com/hr/schemas")
    private String FirstName;
    @XmlElement(namespace="http://mycompany.com/hr/schemas")
    private String LastName;

    public int getNumber() {
        return Number;
    }
    public void setNumber(int number) {
        Number = number;
    }
    public String getFirstName() {
        return FirstName;
    }
    public void setFirstName(String firstName) {
        FirstName = firstName;
    }
    public String getLastName() {
        return LastName;
    }
    public void setLastName(String lastName) {
        LastName = lastName;
    }
    @Override
    public String toString() {
        return "Employee [Number=" + Number + ", FirstName=" + FirstName + ", LastName=" + LastName + "]";
    }


}
           

在Spring-ws中使用JAXB的时候要注意,每个类要注明namespace,否则会报

A descriptor with default root element

的错误。类中的每个属性也要加

@XmlElement(namespace="")

的标记,否则在接收的时候为空,namespace与你制定的相同。

HolidayRequest.java代码如下。

package com.chinamobile.cmss.eshub.ssb.wssimulator.entity;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "HolidayRequest", namespace="http://mycompany.com/hr/schemas")
@XmlAccessorType(XmlAccessType.FIELD)
public class HolidayRequest {
    @XmlElement(namespace="http://mycompany.com/hr/schemas")
    private Holiday Holiday;
    @XmlElement(namespace="http://mycompany.com/hr/schemas")
    private Employee Employee;

    public Holiday getHoliday() {
        return Holiday;
    }
    public void setHoliday(Holiday holiday) {
        Holiday = holiday;
    }
    public Employee getEmployee() {
        return Employee;
    }
    public void setEmployee(Employee employee) {
        Employee = employee;
    }
    @Override
    public String toString() {
        return "HolidayRequest [Holiday=" + Holiday + ", Employee=" + Employee + "]";
    }


}
           

在实体类相同的包下面创建

jaxb.properties

文件,内容为

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

,表示我们不使用默认的JAXB,而是使用eclipselink提供的实现,使用默认的JAXB要写更多的代码配置ObjectFactory,否则会报

doesnt contain ObjectFactory.class or jaxb.index

的错误。

到此我们在官方实例的基础上完成了一个更好用的样例。