1. 程式人生 > >關於WebService 的用法demo 總結

關於WebService 的用法demo 總結

前言

在跨系統和跨平臺的系統通訊中,WebService是一個事實上的標準,其以平臺無關性,獲得了廣泛的應用。本文將講述如何基於spring來整合CXF,並開發出第一個Hello World的應用。

1.Web Service是什麼?

Web service是一個平臺獨立的,低耦合的,自包含的、基於可程式設計的web的應用程式,可使用開放的XML(標準通用標記語言下的一個子集)標準來描述、釋出、發現、協調和配置這些應用程式,用於開發分散式的互操作的應用程式。
Web Service技術,能使得執行在不同機器上的不同應用無須藉助附加的、專門的第三方軟體或硬體,就可相互交換資料或整合。依據Web Service規範實施的應用之間,無論它們所使用的語言、 平臺或內部協議是什麼, 都可以相互交換資料。Web Service是自描述、 自包含的可用網路模組,可以執行具體的業務功能。Web Service也很容易部署,因為它們基於一些常規的產業標準以及已有的一些技術,諸如標準通用標記語言下的子集XML、HTTP。Web Service減少了應用介面的花費。Web Service為整個企業甚至多個組織之間的業務流程的整合提供了一個通用機制。

2.Web Service模型

WebService體系結構基於三種角色(即服務提供者、服務註冊中心和服務請求者)之間的互動。互動涉及釋出、查詢和繫結操作,這些角色和操作一起作用於WebServices元件,即WebServices軟體模組及其描述。在典型情況下,服務提供者託管可通過網路訪問的的軟體模組,定義WebServices的服務描述並把它釋出到服務註冊中心;服務請求者使用查詢操作來從服務註冊中心檢索服務描述,然後使用服務描述與服務提供者進行繫結並呼叫WebServices實現和同它互動。
這裡寫圖片描述

從圖中可以看出,SOA結構中共有三種角色:
(1)、服務提供者:釋出自己的服務,並且對服務請求進行響應。
(2)、服務註冊中心:註冊已經發布的WebServices,對其進行分類,並提供搜尋服務。
(3)、服務請求者:利用服務註冊中心查詢所需的服務,然後使用該服務。

SOA體系結構中的元件必須具有上述一種或多種角色,這些角色之間使用三種操作:
(1)、釋出操作:使服務提供者可以向服務註冊中心註冊自己的功能及訪問介面。
(2)、查詢操作:使服務請求者可以通過服務註冊中心查詢特定種類的服務。
(3)、繫結操作:使服務請求者能夠真正使用服務提供者提供的服務。

3.WebService開源框架的選擇

目前主流的Web Service框架主要有3種: Axis 2, CXF, 和 Spring WS。Axis 2和CXF都是來自於Apache, 各個方面相差不多,但是目前市場上使用CXF比較多一點。

使用篇

cfx一套最簡配置jar包

cxf3.0.4框架必需的包為:
cxf-core-3.0.4.jar
cxf-rt-bindings-soap-3.0.4.jar
cxf-rt-databinding-jaxb-3.0.4.jar
cxf-rt-frontend-jaxws-3.0.4.jar
cxf-rt-frontend-simple-3.0.4.jar
cxf-rt-transports-http-3.0.4.jar
cxf-rt-transports-udp-3.0.4.jar
cxf-rt-ws-addr-3.0.4.jar
cxf-rt-wsdl-3.0.4.jar
cxf-rt-ws-policy-3.0.4.jar
cxf-rt-wsdl-3.0.4.jar
neethi-3.0.3.jar
slf4j-api-1.7.9.jar
xmlschema-core-2.2.1.jar
注意:這裡不包括spring依賴包和commoms下的jar包

maven依賴

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-core</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-bindings-soap</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-databinding-jaxb</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-simple</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-udp</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-ws-addr</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-wsdl</artifactId>
    <version>3.0.4</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-ws-policy</artifactId>
    <version>3.0.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.neethi/neethi -->
<dependency>
    <groupId>org.apache.neethi</groupId>
    <artifactId>neethi</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http-jetty</artifactId>
    <version>3.0.3</version>
</dependency>

1.最簡單的demo

服務端介面定義

服務端提供的獲取使用者的方法

@WebService
public interface IUserWebService {

    User getUserByName(@WebParam(name="userName") String userName);

    List<User> getAllUsers();

}

注意:“@WebService”標記表示該介面是一個WebService服務;“@WebMethod”表示表示以下方法為WebService服務中的方法;“@WebParam(name=”username”)”表示方法中的引數,username屬性限制了引數的名稱,若沒有指定該屬性,引數將被重新命名。
@WebParam(name=”userName”) 經測試, 可省略, 但為了便於閱讀,還是加上

服務端介面實現

模擬從服務端獲取使用者

public class UserWebServiceImpl implements IUserWebService {

    private static List<User> users = new ArrayList<User>();
    static {
        users.add(new User("001", "李四"));
        users.add(new User("002", "王五"));
        users.add(new User("003", "趙六"));
    }

    public User getUserByName(String userName) {
        if (StringUtils.isEmpty(userName)) {
            return null;
        }
        for (User user: users) {
            if (userName.equals(user.getname())) {
                return user;
            }
        }
        return null;
    }

    public List<User> getAllUsers() {
        return users;
    }

}

編寫服務端的啟動程式

模擬WebService服務啟動

public class WebServiceApp {

    public static void main(String[] args) {
        // webservice介面
        IUserWebService userService = new UserWebServiceImpl();
        // 訪問地址
        String address="http://localhost:8080/userWebService";
        // 釋出服務
        Endpoint.publish(address, userService);
        System.out.println("web service 已啟動");
   }
}
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="http://webservice.demo.coolframe/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
    name="UserWebServiceImplService" targetNamespace="http://webservice.demo.coolframe/">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://webservice.demo.coolframe/"
            elementFormDefault="unqualified" targetNamespace="http://webservice.demo.coolframe/" version="1.0">
            <xs:element name="getAllUsers" type="tns:getAllUsers" />
            <xs:element name="getAllUsersResponse" type="tns:getAllUsersResponse" />
            <xs:element name="getUserByName" type="tns:getUserByName" />
            <xs:element name="getUserByNameResponse" type="tns:getUserByNameResponse" />
            <xs:complexType name="getAllUsers">
                <xs:sequence />
            </xs:complexType>
            <xs:complexType name="getAllUsersResponse">
                <xs:sequence>
                    <xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:user" />
                </xs:sequence>
            </xs:complexType>
            <xs:complexType name="user">
                <xs:sequence>
                    <xs:element minOccurs="0" name="id" type="xs:string" />
                    <xs:element minOccurs="0" name="name" type="xs:string" />
                    <xs:element maxOccurs="unbounded" minOccurs="0" name="phones" nillable="true" type="xs:string" />
                </xs:sequence>
            </xs:complexType>
            <xs:complexType name="getUserByName">
                <xs:sequence>
                    <xs:element minOccurs="0" name="userName" type="xs:string" />
                </xs:sequence>
            </xs:complexType>
            <xs:complexType name="getUserByNameResponse">
                <xs:sequence>
                    <xs:element minOccurs="0" name="return" type="tns:user" />
                </xs:sequence>
            </xs:complexType>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="getUserByNameResponse">
        <wsdl:part element="tns:getUserByNameResponse" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:message name="getAllUsers">
        <wsdl:part element="tns:getAllUsers" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:message name="getAllUsersResponse">
        <wsdl:part element="tns:getAllUsersResponse" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:message name="getUserByName">
        <wsdl:part element="tns:getUserByName" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:portType name="IUserWebService">
        <wsdl:operation name="getAllUsers">
            <wsdl:input message="tns:getAllUsers" name="getAllUsers"></wsdl:input>
            <wsdl:output message="tns:getAllUsersResponse" name="getAllUsersResponse"></wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="getUserByName">
            <wsdl:input message="tns:getUserByName" name="getUserByName"></wsdl:input>
            <wsdl:output message="tns:getUserByNameResponse" name="getUserByNameResponse"></wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="UserWebServiceImplServiceSoapBinding" type="tns:IUserWebService">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="getAllUsers">
            <soap:operation soapAction="" style="document" />
            <wsdl:input name="getAllUsers">
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="getAllUsersResponse">
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="getUserByName">
            <soap:operation soapAction="" style="document" />
            <wsdl:input name="getUserByName">
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="getUserByNameResponse">
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="UserWebServiceImplService">
        <wsdl:port binding="tns:UserWebServiceImplServiceSoapBinding" name="UserWebServiceImplPort">
            <soap:address location="http://localhost:8080/userWebService" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

顯示這樣的頁面說明服務啟動成功了, 下面模擬客戶端訪問

編寫客戶端

public class WebServiceClient {

    public static void main(String[] args) {
        JaxWsProxyFactoryBean svr = new JaxWsProxyFactoryBean();
        // 服務端介面
        svr.setServiceClass(IUserWebService.class);
        // 服務端地址
        svr.setAddress("http://localhost:8080/userWebService");
        IUserWebService userWebService = (IUserWebService) svr.create();
        List<User> users = userWebService.getAllUsers();
        System.out.println(users);
    }
}

輸出結果:
[User [name=isName:李四], User [name=isName:王五], User [name=isName:趙六]]

2.如何整合spring框架

前提:檢查spring版本

瞭解你的spring版本, cxf對spring版本, 需要的依賴是不同的.至於什麼版本匹配, 還需要自行百度, 我只實驗了spring4.0 + ,需要cxf3.0以上的版本支援,不然報錯.

在Web.xml中宣告CXF監聽器

<servlet>  
<servlet-name>CXFServlet</servlet-name>  
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>  
    <load-on-startup>1</load-on-startup>  
</servlet>  
<servlet-mapping>  
     <servlet-name>CXFServlet</servlet-name>  
     <url-pattern>/webservice/*</url-pattern>  
</servlet-mapping>

注意:下的指明瞭服務訪問地址的形式,“/*”代表URL地址中,包名稱後直接跟服務endpoint地址,若指明為/webservice/*,則URL為“包名/webservice/endpoing?wsdl”。例如之前例子的:http://localhost:8080/userWebService?wsdl, 其中userWebService等同於這裡的endpoing

建立WebService宣告的Spring配置檔案spring-cxf.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:jaxws="http://cxf.apache.org/jaxws"  
    xmlns:http-conf = "http://cxf.apache.org/transports/http/configuration"    
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
        http://www.springframework.org/schema/beans/spring-beans.xsd     
        http://cxf.apache.org/jaxws   
        http://cxf.apache.org/schemas/jaxws.xsd  
        http://cxf.apache.org/transports/http/configuration      
        http://cxf.apache.org/schemas/configuration/http-conf.xsd"> 
  <!-- 2.7以上不用寫這些 -->
  <import resource="classpath:META-INF/cxf/cxf.xml" />  
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />  
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />  
  <!-- /2.7以上不用寫 -->

  <bean id="userWebService" class="coolframe.demo.webservice.IUserWebService"/>  

    <jaxws:endpoint id="隨便起這個" implementor="#userWebService" address="/UserWebService" /> 

注意:<jaxws:endpoint>定義了一個WebService,implementor是WebService處理類,值上面定義的bean的id,其具體的實現類在class中指明,address是它的訪問路徑,就是上面提到的將要在URL中顯示的endpoint的名稱。

  • 下面可以啟動專案, 訪問:http://host:port/專案名/webservice/services?wsdl
    看一下都有什麼webservice服務,
  • 讀取wsdl檔案內容,訪問路徑: http://host:port/專案名/services/serviceName?wsdl
    配置多個介面的方法
<bean id="roleWebService" class="coolframe.demo.webservice.RoleWebServiceImpl"/>  

<bean id="userWebService" class="coolframe.demo.webservice.UserWebServiceImpl"/>  

<jaxws:server id="aWebService"  
    serviceClass="coolframe.demo.webservice.IRoleWebService"  
    address="/RoleWebService">  
    <jaxws:serviceBean>      
        <ref bean="hello"/>  
    </jaxws:serviceBean> 
</jaxws:server> 

<jaxws:server id="bWebService"  
    serviceClass="coolframe.demo.webservice.IUserWebService"  
    address="/UserWebService">  
    <jaxws:serviceBean>      
        <ref bean="userWebService"/>  
    </jaxws:serviceBean> 
</jaxws:server>

總結: 配置方面基本完成了, 剩下還是客戶端的呼叫

客戶端的呼叫

模擬客戶端的呼叫

public class WebServiceClient {

    public static void main(String[] args) {

        String serviceURL = "http://localhost:8000/coolframe/webservice/UserWebService";
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.setServiceClass(IUserWebService.class);
        factory.setAddress(serviceURL);
        IUserWebService userWebService = (IUserWebService)factory.create();
        List<User> userList = userWebService.getAllUsers();
        System.out.println(userList);

    }
    // 輸出結果: [User [name=isName:李四], User [name=isName:王五], User [name=isName:趙六]]
}

異常問題總結

  • jar包缺失
    在使用cxf過程中經常出 Cannot find any registered HttpDestinationFactory from the Bus,一般是沒有引入cxf-rt-transports-http-jetty-xxx.jar。檢視apache.cxf.transport.http.HTTPTransportFactory.getDestination(HTTPTransportFactory.java:270)類,jettyFactory為null,也就是缺少http-jetty的實現。
Caused by: java.io.IOException: Cannot find any registered HttpDestinationFactory from the Bus.
解決辦法:
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http-jetty</artifactId>
    <version>3.0.3</version>
</dependency>
  • spring版本和cxf版本不匹配
java.lang.NoSuchMethodError: org.springframework.aop.support.AopUtils.isCglibProxyClass(Ljava/lang/Class;)Z

解決辦法:

根據spring版本自行百度,更換cxf依賴版本

  • xml載入取順序問題
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined

解決辦法:

把cxf的配置不要放在DispatcherServlet裡, 放到contextConfigLocation里加載

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
    classpath*:conf/spring-cxf.xml
</param-value>
</context-param>
  • xml檔案找不到
java.io.FileNotFoundException: class path resource [META-INF/cxf/cxf-extension-soap.xml] cannot be opened because it does not exist

cxf 2.7 以上spring-cxf.xml配置中不要寫下面的兩句

<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />