利用spring-ws 現實soap webservice服務

背景:有的系統還用soap協議來作webservice.特別要和他們對接的時候。咱們須要實現一套。
今天說說,利用spring-ws來(部署,調用)webservcie,能很好的和主流架構(spring-mvc)結合。
參考資料,官方文檔https://docs.spring.io/spring-ws/docs/3.0.0.RELEASE/reference/前端

spring-ws像spring-mvc同樣,在集成到web項目時,前端有個servlet分發請求消息的概念。
這個servlet接受soap消息,經過映射轉發到後端的服務實現類方法中(Endpiont)
在請求進來處理過程當中,能夠添加,攔截器(Interceptor),異常處理器(ExceptionResolver)。
經過攔截器能夠作一些額外的定製功能,好比安全。經過異常處理器定製異常信息顯示,處理等。java

一個soap消息進來的處理流程圖以下:web

實踐過程:

依賴的jar:
官方給出的依賴jar關係圖:spring

最新版,須要jdk1.8.後端

我在jdk1.7用的包依賴版本api

<dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-ws-core</artifactId>
        <version>2.4.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-xml</artifactId>
        <version>2.4.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>wsdl4j</groupId>
        <artifactId>wsdl4j</artifactId>
        <version>1.6.3</version>
    </dependency>

 

沒有用spring-oxmspring-mvc

1,配置servlet web.xml文件裏添加servlet 攔截webservice消息請求。緩存

<servlet>
		<servlet-name>spring-ws</servlet-name>
		<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>spring-ws</servlet-name>
		<!--webservice攔截路徑-->
		<url-pattern>/webservice/*</url-pattern>
	</servlet-mapping>

2,定義,發佈webservice服務,具體定義服務操做和操做的請求返回的xml格式。這裏咱們在/WEB-INF/hr.xsd目錄下,編寫好xsd文件。安全

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:hr="http://j2eeweb.wannshan.cn/hr/schemas"
           elementFormDefault="qualified"
           targetNamespace="http://j2eeweb.wannshan.cn/hr/schemas">
  <!--按約定,每一個operationNameRequest格式的element每一個對應一個operation,同時operationNameRequest是請求參數元素名稱-->
  <!--同理 operationNameResponse 格式的element是對應一個operation的返回參數-->
    <xs:element name="saveCountryRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="country" type="hr:country"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="saveCountryResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <!--getCountry操做,請求返回值定義-->
    <xs:element name="getCountryRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="getCountryResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="country" type="hr:country"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="country">
        <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="population" type="xs:int"/>
            <xs:element name="capital" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

經過xsd文件咱們定義個兩個操做(方法)和每一個方法的請求和返回格式
到這裏咱們雖然尚未服務實現,但能夠以wsdl形式發佈服務了。具體:架構

在WEB-INF目錄下,新建spring-ws-servlet.xml文件([servletName-servlet.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:mvc="http://www.springframework.org/schema/mvc"
       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.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.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/mvc  http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!--動態生成wsdl文件url http://host:port/webservice/queryService/queryContry.wsdl id值是wsdl文件路徑-->
    <sws:dynamic-wsdl id="queryContry"
                      portTypeName="query"
                      locationUri="/webservice/queryService/"
                      targetNamespace="http://j2eeweb.wannshan.cn/hr/schemas">
   <!--hr.xsd路徑-->
    <sws:xsd location="/WEB-INF/hr.xsd"/>
    </sws:dynamic-wsdl>
    <!--靜態wsdl 指定文件 通常現有動態生成,最後上生產考到靜態文件中去-->
    <!--<sws:static-wsdl id="queryContry" location="/WEB-INF/queryContry.wsdl"/>-->

</beans>

這裏說下,或許咱們不太會編寫wsdl文件,上面的配置文件裏,spring-ws給咱們提供了一種動態生成wsdl的機制。
由於動態生成有緩存,能夠在生產環境上配置靜態wsdl,測試環境用動態。
而後啓動web服務。

3,編寫webservice服務實現類(Endpoint)完成具體的服務業務

編寫前,咱們能夠用maven-jaxb2-plugin插件根據wsdl文件生成業務請求對象類
GetCountryRequest,GetCountryRequest,SaveCountryResponse,GetCountryResponse,Country
裏面有jaxb綁定註解。具體在項目pom.xml文件裏配置

<build>
    <plugins>
        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>0.13.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <schemaLanguage>WSDL</schemaLanguage>
		<!--pojo存放包名-->
                <generatePackage>cn.wannshan.j2ee.ws.dto</generatePackage>
                <schemas>
                    <schema>
                        <url>http://localhost:8080/webservice/queryService/queryContry.wsdl</url>
                    </schema>
                </schemas>
            </configuration>
        </plugin>
    </plugins>
    </build>

 而後對項目執行 maven package 任務

類生成好後,能夠編寫webservice實現類了,我本身的實現,以下:

package cn.wannshan.j2ee.ws;

import cn.wannshan.j2ee.ws.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.ws.server.endpoint.annotation.ResponsePayload;
/**
 * xxx
 * 
 */
@Endpoint
public class CountryQuery {

    private static final String NAMESPACE_URI = "http://j2eeweb.wannshan.cn/hr/schemas";
    //注入spring bean
    @Autowired
    CountryRepository countryRepository;

    /***
     * 查詢country
     * @param request
     * @return
     * @throws Exception
     */
    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
    @ResponsePayload
    public GetCountryResponse queryCountry(@RequestPayload GetCountryRequest request) throws Exception {
        GetCountryResponse response = new GetCountryResponse();
        response.setCountry(countryRepository.findCountry(request.getName()));
        return response;
    }

    /**
     * 保存country
     * @param request
     * @return
     * @throws Exception
     */
    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "saveCountryRequest")
    @ResponsePayload
    public SaveCountryResponse saveCountry(@RequestPayload SaveCountryRequest request) throws Exception {
        SaveCountryResponse response = new SaveCountryResponse();
        countryRepository.putCounttry(request.getCountry());
        response.setName(request.getCountry().getName());
        return response;
    }

}


//CountryRepository 類,簡單利用map在內存中保存數據

package cn.wannshan.j2ee.ws;

import cn.wannshan.j2ee.ws.dto.Country;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 *
 *
 */
@Component
public class CountryRepository {
    private static final Map<String, Country> countries = new HashMap();

    @PostConstruct
    public void initData() {
        Country spain = new Country();
        spain.setName("Spain");
        spain.setCapital("Madrid");
        spain.setPopulation(46704314);
        countries.put(spain.getName(), spain);

        Country poland = new Country();
        poland.setName("Poland");
        poland.setCapital("Warsaw");
        poland.setPopulation(38186860);
        countries.put(poland.getName(), poland);

        Country uk = new Country();
        uk.setName("United Kingdom");
        uk.setCapital("London");
        uk.setPopulation(63705000);
        countries.put(uk.getName(), uk);
    }
    public String  putCounttry(Country country){
        Assert.notNull(country, "The country must not be null");
        Assert.notNull(country.getName(), "The country's name must not be null");
        countries.put(country.getName(),country);
        return country.getName();
    }
    public Country findCountry(String name) {
        Assert.notNull(name, "The country's name must not be null");
        return countries.get(name);
    }
}

這裏簡單說下,CountryQuery 類:

類要用@Endpoint註解,代表它是spring-ws承認的webservice服務類。

類中兩個方法,一個查詢country,一個保存新的country.
兩個方法上都用到了註解
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
@ResponsePayload

@PayloadRoot:實現soap消息的映射路由功能,簡單點說,就是soap消息裏,namespace = NAMESPACE_URI而且有getCountryRequest元素節點的,才能由這個方法接受處理。
@ResponsePayload:表示這個方法有返回值,而且會把返回值封裝格式化爲soap消息格式。

而方法名參數上用到了@RequestPayload GetCountryRequest request,表示soap消息的,有效負載,映射到參數request對象上。

以上都是spring-ws自動幫你作好的。

3,部署webservice服務實現

實現類作好後,在spring-ws-servlet.xml文件文件里加入以下配置,重啓web服務。就能夠接受處理合適的webservice soap消息請求了。
    <!--webservice實現類 endpoint 所在包-->
    <context:component-scan base-package="cn.wannshan.j2ee.ws"></context:component-scan>
    <!--開啓sws掃描,beans裏要加入xmlns:sws命名空間-->
    <sws:annotation-driven/>

以上是webservcie服務部署過程。

spring-ws還提供了webservcie服務客戶端類,用於請求soap webservice叫WebServiceTemplate。只要在spring文件裏配置一個bean

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="messageFactory"/>
        <!--webserivce服務地址-->
        <property name="defaultUri" value="http://localhost:8080/webservice/queryService"/>
    </bean>

而後就能夠用了
下面是我寫的測試類片斷

@Test
    public void testWebservice() throws JAXBException {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        //這是個包名,是你利用maven插件根據xsd文件生成pojo類,存放包名
        marshaller.setContextPath("cn.wannshan.j2ee.ws.dto");
        //指定Jaxb方案實現類。spring提供Jaxb2Marshaller
        webServiceTemplate.setMarshaller(marshaller);
        webServiceTemplate.setUnmarshaller(marshaller);

        //查詢Country
        GetCountryRequest getCountryRequest=new GetCountryRequest();
        getCountryRequest.setName("Spain");
        GetCountryResponse getCountryResponse= (GetCountryResponse) webServiceTemplate.marshalSendAndReceive(getCountryRequest);
        Assert.assertEquals(getCountryResponse.getCountry().getName(), "Spain");

        //保存Country
        Country country=new Country();
        country.setName("中國");
        country.setCapital("北京");
        country.setPopulation(1400000000);
        SaveCountryRequest saveCountryRequest=new SaveCountryRequest();
        saveCountryRequest.setCountry(country);
        SaveCountryResponse saveCountryResponse= (SaveCountryResponse) webServiceTemplate.marshalSendAndReceive(saveCountryRequest);
        Assert.assertEquals(saveCountryResponse.getName(), "中國");
    }
相關文章
相關標籤/搜索