天天看點

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

的錯誤。

到此我們在官方執行個體的基礎上完成了一個更好用的樣例。