1. 程式人生 > >cxf 入門

cxf 入門

name tro 數據表示 tag tor 啟動 repos 客戶端 ppi

1 WebService 簡單描述

? 一言以蔽之:WebService是一種跨編程語言和跨操作系統平臺的遠程調用技術。常用的webservice框架 : cfx、jwx、XFire與Axis2,jwx也就是jax-ws,是java6提供的對webservice的一種實現。cxf框架則簡化了服務發布過程。Axis與XFire已隨著技術不斷的更替慢慢落幕,而目前也只有axis2和cxf官方有更新,Axis與XFire都已不再更新。

? XML+XSD , SOAPWSDL 就是構成WebService平臺的三大技術。

1 XML+XSD

? XML 封裝數據 , 解決數據表示問題 , 因為與平臺和廠商無關 , 保證了通用性 ; XSD 解決數據類型問題 , 定義了 webService 所使用的數據的類型 , 保證了不同系統下數據通用的問題

2 SOAP

? soap協議 . WebService 是通過 http 協議發送請求和接收數據結果 , http 消息內容都有特定的消息頭和消息內容 . 而 SOAP 協議限定了這些消息頭和內容的格式. 簡單理解就是 , SOAP 約束了 WebService 的請求信息和響應信息應該長什麽樣 .

3 WSDL

? WSDL(Web Services Description Language) 服務描述語言 , WebService 作為一個服務 , 必須告訴調用者服務地址在哪?提供什麽服務?調用服務需要傳什麽參數?等 這些信息 , 就是通過 WSDL文件來描述的 .

? WSDL文件保存在Web服務器上,通過一個url地址就可以訪問到它。客戶端要調用一個WebService服務之前,要知道該服務的WSDL文件的地址。WebService服務提供商可以通過兩種方式來暴露它的WSDL文件地址:1.註冊到UDDI服務器,以便被人查找;2.直接告訴給客戶端調用者。

4 UDDI

? UDDI (Universal Description, Discovery, and Integration) 是一個主要針對Web服務供應商和使用者的新項目。在用戶能夠調用Web服務之前,必須確定這個服務內包含哪些商務方法,找到被調用的接口定義,還要在服務端來編制軟件,UDDI是一種根據描述文檔來引導系統查找相應服務的機制。UDDI利用SOAP消息機制(標準的XML/HTTP)來發布,編輯,瀏覽以及查找註冊信息。它采用XML格式來封裝各種不同類型的數據,並且發送到註冊中心或者由註冊中心來返回需要的數據。

2 WebService服務工作流程

技術分享圖片

? 註冊到 UUID服務器的 webservice

工作流程大致如下 :

  1. 服務提供者實現服務 , 並在服務註冊中心(UDDI)進行註冊

  2. 服務請求者向服務註冊中心請求特定服務(其實就是請求對應的 wsdl 文件)

  3. 服務註冊中心找到對應的服務後 , 將該服務的描述(wsdl)文件返回為請求者

  4. 請求者根據返回的 wsdl 文件 , 生成相應的 soap消息 , 發送給服務提供者

  5. 服務提供者根據 soap消息執行對應的服務 , 並將結果返回給請求者

3 使用 cxf 搭建一個 webService

? CXF : Apache CXF 的前身叫 Apache CeltiXfire,現在已經正式更名為 Apache CXF 了,簡稱為 CXF。CXF 繼承了 Celtix 和 XFire 兩大開源項目的精華。

? 搭建 webService的步驟大致如下:

  1. 創建一個接口聲明提供的服務方法

  2. 實現接口

  3. 搭建web服務器,並註冊 CXFServlet , 用於對外提供服務

  4. 註冊服務

  5. 啟動web服務器

1 maven 坐標

<!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-frontend-jaxws -->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-transports-http -->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.0.1</version>
</dependency>
?
<!-- 服務以 war 包形式發布,並且和 spring 整合所以依賴了 springmvc-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.10.RELEASE</version>
</dependency>

2 配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
?
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>
?
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    
    <!--cxf 服務的入口-->
    <servlet>
        <servlet-name>cxf</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/webservice/*</url-pattern>
    </servlet-mapping>
</web-app>

3 創建服務接口和實現類

? HelloWorld.java

package com.fmi110.cxf.service;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
 * 一定要添加 @WebService 註解 !!!
 */
@WebService
public interface HelloWorld {
    String sayHi(@WebParam(name="msg") String msg);
?
}

ps:

@WebService 註解一定要有!!!否則報錯

@WebParam(name="msg") 不是必須的 , 加了以後生成的wsdl文件中對參數 msg 的描述會用"msg" 表示 , 否則使用 "arg0" 表述

? HelloWorldImpl.java

package com.fmi110.cxf.service.impl;
import com.fmi110.cxf.service.HelloWorld;
?
public class HelloWorldImpl implements HelloWorld{
    @Override
    public String sayHi(String msg) {
        System.out.println("sayHi() 被調用...");
        return "sayHi : "+msg;
    }
}

4 spring配置文件 applicationContext.xml

? 因為在web.xml 中已經通過 <param-value>classpath*:applicationContext.xml</param-value> 指定配置文件位置 , 所以文件放在 src/main/resource 目錄下(maven工程 , 非maven工程放在 src 目錄下即可)

<?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:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://cxf.apache.org/jaxws
       http://cxf.apache.org/schemas/jaxws.xsd">
?
?
    <!--開啟註解掃描聲明-->
    <context:component-scan base-package="com.fmi110"/>
?
    <!--聲明服務方式1-->
    <bean id="hello" class="com.fmi110.cxf.service.impl.HelloWorldImpl"/>
    <jaxws:server address="/helloworld">
        <jaxws:serviceBean>
            <ref bean="hello"/>
        </jaxws:serviceBean>
    </jaxws:server>
?
    <!--聲明服務方式2-->
    <!--
        <jaxws:endpoint id="helloWorld" implementor="com.fmi110.cxf.service.impl.HelloWorldImpl" address="/helloworld"/>
    -->
?
</beans>

? 如上提供了兩種註冊服務的方式 , <jaxws:server/><jaxws:endpoint/> 實現效果是等價的 , <jaxws:server/> 來源於 JAXWS 的API , 而 <jaxws:endpoint/> 來自 Xfire 的API , 選用哪個都可以...

? 這裏需要註意的是 <jaxws:server address="/helloworld"> 中的 address 屬性指定了服務的訪問地址時 /helloworld , 則在瀏覽器中輸入的地址應該是 ${request.ContextPath}/helloworld

? jaxws 標簽的詳細配置請參考 : jax-ws-configuration

5 啟動服務並訪問

? 配置好後,啟動tomcat並部署應用 , 此時在瀏覽器輸入地址 : http://localhost:8080/cxf/webservice/helloworld?wsdl

可看到如下內容 :

技術分享圖片

? 這就說明我們的服務註冊成功了...

4 創建一個客戶端

? webService 是用來實現遠程調用的 , 所以這裏我們重新建一個java工程 , 遠程調用服務

1 使用 wsimport 命令生成接口文件

? 在一個空文件夾下 , 打開cmd 窗口 , 輸入如下命令 :

wsimport  -s  .   http://localhost:8080/cxf/webservice/helloworld?wsdl

技術分享圖片

此時會生成一堆文件 , 這裏我們只需要生成的 HelloWorld.java 這個接口文件就可以了 , 文件內容如下 :

package com.fmi110.cxf.service;
?
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
?
 (name = "HelloWorld", targetNamespace = "http://service.cxf.fmi110.com/")
@XmlSeeAlso({
    // 這個類文件用不到,刪掉了,所以這裏也註釋掉    
    // ObjectFactory.class
})
public interface HelloWorld {
?
    @WebMethod
    @WebResult(targetNamespace = "")
    @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.cxf.fmi110.com/", className = "com.fmi110.cxf.service.SayHi")
    @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.cxf.fmi110.com/", className = "com.fmi110.cxf.service.SayHiResponse")
    public String sayHi(
        @WebParam(name = "msg", targetNamespace = "")
        String msg);
?
}

註意 :

  1. 接口上的註解 @XmlSeeAlso 可以刪除,不影響

  2. 接口上的註解 @WebService , name 屬性可以隨便寫不影響 , targetNamespace 屬性不能修改 , 否則服務找不到,程序報錯

2 新建一個 java 工程作為客戶端

? 這裏還是使用maven工具來構建工程,需要導入的依賴跟搭建服務端的一致,這裏不在寫了 , 項目結構如下圖 :

技術分享圖片

? 這裏接口文件 HelloWorld.java 放在了 com.fmi110.cxf.service 包下 , 只是為了與服務端接口的定義保持一致 , 事實上放在哪個包下都可以 , 因為 cxf 生成代理類對象時 是通過接口文件裏的內容描述(class字節碼) 生成 , 所在包不影響.

3 註冊客戶端

? 在spring配置文件 client-beans.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:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://cxf.apache.org/jaxws
       http://cxf.apache.org/schemas/jaxws.xsd">
?
?
    <!--聲明客戶端-->
    <jaxws:client id="client" serviceClass="com.fmi110.cxf.service.HelloWorld"
                  address="http://localhost:8080/cxf/webservice/helloworld"/>
</beans>
  1. serviceClass 的值是項目中 HelloWorld 類的全限定名

  2. address 地址必須寫正確 , 否則調用不了服務 , 該地址時服務端暴露的服務的地址

4 編寫測試代碼

import com.fmi110.cxf.service.HelloWorld;
import org.springframework.context.support.ClassPathXmlApplicationContext;
?
/**
 * Created by huangyunning on 2017/12/20.
 */
public class HelloClient {
?
    public static void main(String args[]) throws Exception {
?
        ApplicationContext context = new ClassPathXmlApplicationContext("client-beans.xml");
?
        HelloWorld client = (HelloWorld)context.getBean("client");
?
        String response = client.sayHi("Joe");
        System.out.println("Response: " + response);
?
        System.exit(0);
    }
}

5 cxf 支持的參數

? cxf 定義的接口方法參數和返回值類型可以是基本數據類型 , java 對象 或這 List 集合 , 如果要使用 Map 集合或者 接口 , 則需要註冊XmlAdapter 進行轉換

cxf 入門