Apache CXF - 快速指南



Apache CXF - 簡介

在當今的環境下,您可以使用多種選項建立 Web 服務應用程式。您可以使用一個或多個標準且廣泛接受的通訊協議。例如 SOAP、XML/HTTP、RESTful HTTPCORBA(公共物件請求代理體系結構,在過去非常流行,但現在使用頻率不高)。

您還可以選擇不同的傳輸方式,例如 HTTP、JMSJBI 以及前端 API 的選擇,如 JAX-RSJAX-WS。由於 Web 服務開發有如此多的選擇,因此需要一個開源服務框架來將上述所有選項粘合在一起,而 Apache CXF 正是為此而生。

在本教程中,您將學習如何使用 CXF 建立 Web 服務和使用該服務的客戶端,並使用我們上面列出的一個或多個選項。本教程將引導您完成伺服器和客戶端的整個程式碼開發過程。由於每個應用程式只能使用每個類別中的一個選項,即前端、傳輸和協議,考慮到這三個選項的所有排列和組合,應用程式的數量將非常高。

本教程詳細討論了以下專案的開發 -

  • 使用普通舊 Apache CXF 物件 (POJO) 的 CXF

  • CXF 與 JAX-WS

  • CXF 與 WSDL

  • CXF 與 JAX-RS

  • CXF 與 JMS

為了簡單起見,我們使用了 Maven 及其命令列介面。您可以使用您喜歡的 IDE 建立 Maven 專案。

在下一章中,讓我們開始第一個專案。

Apache CXF 與 POJO

在本章中,您將學習如何開發一個簡單的 Web 應用程式,該應用程式向用戶傳送問候訊息。Web 服務專案使用 WSDL 模型。CXF 允許您透過提供一個簡單的前端將 Apache CXF API 對映到底層 WSDL 來隱藏此 WSDL 模型。

在這個最簡單的專案中,Web 服務的介面將直接暴露給客戶端,客戶端將使用本地 Apache CXF API 呼叫 Web 服務。

首先,我們將建立一個 Web 服務。每個服務都有一個暴露給客戶端的介面。我們可以將此介面編寫為簡單的 Apache CXF 介面或 WSDL 文件。在這種 Apache CXF 優先方法中,我們將透過 Apache CXF 介面公開我們的服務。

開發 Web 服務

我們將在 Web 上建立的服務將有一個名為 greetings 的單個 Web 方法。該方法採用 string 型別引數,我們將在其中傳送使用者的姓名。服務將向呼叫者傳送一條問候訊息,其中包含接收到的使用者名稱。

Web 服務介面

為了公開我們 Web 服務的介面,我們將建立一個 Apache CXF 介面,如下所示 -

//HelloWorld.java
package com.tutorialspoint.cxf.pojo;
public interface HelloWorld {
   String greetings(String text);
}

該介面只有一個名為 greetings 的方法。伺服器將實現此介面。在我們簡單的應用程式中,此介面直接暴露給客戶端。通常,在 Web 服務應用程式中,您使用 WSDL 來描述 Web 服務介面。在這個簡單的應用程式中,我們將向客戶端開發人員提供此直接介面。然後,客戶端將在伺服器物件上呼叫 greetings 訊息。所以首先讓我們建立 Web 服務。

Web 服務實現

HelloWorld 介面在 HelloWorldImpl Apache CXF 類中實現,如下所示 -

//HelloWorldImpl.java
package com.tutorialspoint.cxf.pojo;
public class HelloWorldImpl implements HelloWorld {
   @Override
   public String greetings(String text) {
      return "Hi " + text;
   }
}

greetings 方法接收 string 型別引數,將其附加到問候訊息中,並將結果字串返回給呼叫者。

接下來,我們編寫伺服器應用程式來託管 HelloWorld 服務。

建立伺服器

伺服器應用程式包含兩個部分 -

  • 第一部分為我們的 Web 服務建立一個工廠,以及

  • 第二部分編寫一個 main 方法來例項化它。

伺服器使用 CXF 庫提供的 ServerFactoryBean 類將我們的 HelloWorld 介面暴露給遠端客戶端。因此,我們首先例項化 ServerFactoryBean 類,然後設定其各種屬性 -

ServerFactoryBean factory = new ServerFactoryBean();

我們透過在 factory 物件上呼叫 setServiceClass 方法來設定要呼叫的服務類 -

factory.setServiceClass(HelloWorld.class);

我們透過呼叫工廠的 setAddress 方法來設定呼叫我們服務的 URL。請注意,服務將在此 URL 上釋出。

factory.setAddress("https://:5000/Hello");

在這種情況下,服務部署在嵌入式伺服器上,並將偵聽埠 5000。您可以選擇任何您選擇的埠號。

在建立工廠之前,您需要告訴工廠我們的服務實現類。這是透過在 factory 物件上呼叫 setServiceBean 方法來完成的,如下所示 -

factory.setServiceBean(new HelloWorldImpl());

服務 Bean 設定為我們服務實現類的例項。最後,我們透過呼叫其 create 方法來建立工廠 -

factory.create();

現在,由於我們已經開發了執行 Web 服務的工廠,接下來我們將編寫一個 main 方法來例項化它並使其執行一段時間。

現在,編寫一個 main 方法來例項化 HelloServer 類,如下所示 -

public static void main(String[] args) throws Exception {
   new HelloServer();
   System.out.println("Listening on port 5000 ...");
}

一旦例項化,HelloServer 類將無限期地執行。對於生產部署,您肯定會讓伺服器永遠執行。在當前情況下,我們將按照以下方式在預定的時間後終止伺服器 -

Thread.sleep(5 * 60 * 1000);
System.out.println("Server exiting ...");
System.exit(0);

HelloServer 類的完整程式碼如下所示 -

//HelloServer.java
//HelloServer.java
package com.tutorialspoint.cxf.pojo;
import org.apache.cxf.frontend.ServerFactoryBean;
public class HelloServer {
   protected HelloServer() throws Exception {
      ServerFactoryBean factory = new ServerFactoryBean();
      factory.setServiceClass(HelloWorld.class);
      factory.setAddress("https://:5000/Hello");
      factory.setServiceBean(new HelloWorldImpl());
      factory.create();
   }
   public static void main(String[] args) throws Exception {
      new HelloServer();
      System.out.println("Listening on port 5000 ...");
      Thread.sleep(5 * 60 * 1000);
      System.out.println("Server exiting ...");
      System.exit(0);
   }
}

我們建立的伺服器應用程式使用 CXF 庫中的 ServerFactoryBean 類。我們現在必須將這些庫包含在我們的專案中才能成功編譯 HelloServer 類。我們將使用 Maven 來設定專案依賴項。

設定 Maven 專案

要建立 Maven 專案,請在命令列視窗中鍵入以下命令。請注意,我們已在 Mac 機器上對此進行了測試。對於 Windows 和 Linux 安裝,說明可能在某些地方有所不同。

mvn archetype:generate

當提示輸入屬性時,輸入以下值 -

Define value for property 'groupId': : com.tutorialspoint
Define value for property 'artifactId': : cxf-pojo
Define value for property 'version': 1.0-SNAPSHOT: : 1.0
Define value for property 'package': com.tutorialspoint: : com.tutorialspoint.cxf.pojo

Maven 命令完成後,您將在當前資料夾中找到建立的相應資料夾結構以及 pom.xml 檔案。

生成的目錄結構如下所示 -

Directory Structure

您將在 pom.xml 中新增 CXF 依賴項,並將上述建立的 Apache CXF 檔案複製到 Maven 建立的結構的相應資料夾中。為了方便您參考,我們提供了在我們機器上建立的專案的 pom.xml 檔案。

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>cxf-pojo</artifactId>
   <version>1.0</version>
   <packaging>jar</packaging>
   
   <profiles>
      <profile>
         <id>server</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.cxf.pojo.HelloServer
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
      
      <profile>
         <id>client</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                           com.tutorialspoint.cxf.pojo.HelloClient
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
   </profiles>

   <dependencies>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-features-logging</artifactId>
         <version>3.3.0</version>
         <type>jar</type>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-frontend-simple</artifactId>
         <version>3.3.0</version>
         <type>jar</type>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http</artifactId>
         <version>3.3.0</version>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-frontend-jaxws</artifactId>
         <version>3.3.0</version>
      </dependency>
      <!-- Jetty is needed if you're using the CXFServlet -->
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
   </dependencies>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
</project>

上面的 pom.xml 可能包含與此專案無關的其他依賴項,但我們的本教程中的下一個專案需要這些依賴項。無論如何,包含其他依賴項本身並無害處。

專案資料夾結構

放置伺服器和客戶端 Apache CXF 檔案後,我機器上的專案資料夾結構如下所示,供您快速參考 -

Project Folder Structure

執行伺服器

要構建專案,請在命令列視窗中使用以下命令 -

mvn clean install

您可以使用以下命令啟動伺服器 -

mvn -Pserver

這將啟動伺服器,您將在控制檯上看到以下提示 -

INFO: Creating Service {http://pojo.cxf.tutorialspoint.com/}HelloWorld from class com.tutorialspoint.cxf.pojo.HelloWorld
INFO: Setting the server's publish address to be https://:5000/Hello
Listening on port 5000 ...

現在,在您的瀏覽器視窗中指定我們釋出服務的 URL。您將看到以下輸出 -

Output Document Tree

這確認我們的服務正在 localhost 上的指定埠上執行。由於我們在呼叫中未指定 greetings 訊息,因此會將 SOAP 錯誤訊息返回到瀏覽器。

您可以使用您選擇的 SOAP 客戶端進一步測試您的 Web 服務。在這裡,我們使用了 Postman 來測試我們的伺服器。

輸出如下所示 -

Running Server Output

請注意,SOAP 請求是手動編碼的。釋出請求後,伺服器傳送了 SOAP 響應訊息,這在螢幕截圖的底部部分可見。

由此,您可以瞭解到 CXF 在為您提供對當今世界存在的各種 Web 技術的統一檢視的同時,保持使用 SOAP 協議進行請求和響應。這大大簡化了 Web 應用程式開發。

我們的下一個任務是建立一個客戶端,該客戶端將使用您建立的 Web 服務。

建立客戶端

在伺服器應用程式中,HelloWorld 是公開我們 Web 服務的介面。Web 服務本身只是向客戶端提供簡單的問候訊息。通常,Web 服務介面使用 WSDL(Web 服務描述語言)暴露給外部世界。在這個簡單的應用程式中,我們將透過直接公開服務介面,即 HelloWorld.class,來向客戶端公開我們的 Web 服務。

為此,CXF 提供了一個名為 ClientProxyFactoryBean 的工廠類,該類允許我們將所需的介面附加到建立的工廠例項。

首先,我們建立一個工廠 Bean 例項,如下所示 -

ClientProxyFactoryBean factory = new ClientProxyFactoryBean();

我們呼叫工廠 Bean 例項上的 setAddress 方法來設定可以呼叫我們 Web 服務的 URL。在我們的例子中,我們將使用我們在前面步驟中建立伺服器時使用的 URL -

factory.setAddress("https://:5000/Hello");

接下來,我們呼叫 factory 例項上的 create 方法將其附加到我們的服務介面 HelloWorld.class

HelloWorld helloServer = factory.create(HelloWorld.class);

最後,我們呼叫 greetings 方法來呼叫遠端 Web 服務。

System.out.println(helloServer.greetings(System.getProperty("user.name")));

這將在您的控制檯上列印一條問候訊息。

客戶端應用程式的完整原始碼如下所示 -

//HelloClient.java
package com.tutorialspoint.cxf.pojo;
import org.apache.cxf.frontend.ClientProxyFactoryBean;
public class HelloClient {
   public static void main(String[] args) throws Exception {
      ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
      factory.setAddress("https://:5000/Hello");
      HelloWorld helloServer = factory.create(HelloWorld.class);
      System.out.println(helloServer.greetings(System.getProperty("user.name")));
   }
}

執行客戶端

確保伺服器仍在您的機器上執行。如果伺服器超時,請使用以下命令重新啟動伺服器:

mvn -Pserver

您將在控制檯上看到以下訊息:

Listening on port 5000 ...

現在,在我們將伺服器超時時間設定為 5 分鐘之前,開啟另一個命令列視窗並使用以下命令啟動客戶端:

mvn -Pclient

您將在命令列上看到類似以下的訊息:

Hi tutorialspoint

請注意,tutorialspoint 是我們的使用者名稱。您將收到帶有您自己名字的問候語。

在下一章中,我們將學習如何在 JAX-WS(Apache CXF API for XML Web Services)專案中使用 CXF。

Apache CXF 與 JAX-WS

在這個 JAX-WS 應用程式中,我們將使用類似於早期 POJO 應用程式的 Apache CXF-first 方法。所以首先我們將為我們的 Web 服務建立一個介面。

宣告服務介面

與之前的情況一樣,我們將建立一個僅包含一個名為 greetings 的介面方法的簡單服務。服務介面的程式碼如下所示:

//HelloWorld.java
package com.tutorialspoint.cxf.jaxws.helloworld;
import javax.jws.WebService;

@WebService
public interface HelloWorld {
   String greetings(String text);
}

我們使用@WebService標籤註釋介面。接下來,我們將實現此介面。

實現 Web 介面

Web 介面的實現如下所示:

//HelloWorldImpl.java
package com.tutorialspoint.cxf.jaxws.helloworld;
public class HelloWorldImpl implements HelloWorld {
   @Override
   public String greetings(String name) {
      return ("hi " + name);
   }
}

greetings 方法使用@Override標籤進行註釋。該方法向呼叫方返回“hi”訊息。

接下來,我們將編寫開發伺服器的程式碼。

開發伺服器

與 POJO 應用程式不同,我們現在將使用 CXF 提供的 Endpoint 類來發布我們的服務,從而解耦介面。這在以下兩行程式碼中完成:

HelloWorld implementor = new HelloWorldImpl();
Endpoint.publish(
   "https://:9090/HelloServerPort",
   implementor,
   new LoggingFeature()
);

publish 方法的第一個引數指定我們的服務將向客戶端提供的 URL。第二個引數指定我們服務的實現類。伺服器的完整程式碼如下所示:

//Server.java
package com.tutorialspoint.cxf.jaxws.helloworld;
import javax.xml.ws.Endpoint;
import org.apache.cxf.ext.logging.LoggingFeature;
public class Server {
   public static void main(String[] args) throws Exception {
      HelloWorld implementor = new HelloWorldImpl();
      Endpoint.publish("https://:9090/HelloServerPort",
      implementor,
      new LoggingFeature());
      System.out.println("Server ready...");
      Thread.sleep(5 * 60 * 1000);
      System.out.println("Server exiting ...");
      System.exit(0);
   }
}

要部署我們的伺服器,您需要對專案進行一些修改,如下所示。

部署伺服器

最後,要部署伺服器應用程式,您需要在 pom.xml 中進行一項修改,以將您的應用程式設定為 Web 應用程式。您需要新增到 pom.xml 中的程式碼如下所示:

<profiles>
   <profile>
      <id>server</id>
      <build>
         <defaultGoal>test</defaultGoal>
         <plugins>
            <plugin>
               <groupId>org.codehaus.mojo</groupId>
               <artifactId>exec-maven-plugin</artifactId>
               <version>1.6.0</version>
               <executions>
                  <execution>
                     <phase>test</phase>
                     <goals>
                        <goal>java</goal>
                     </goals>
                     <configuration>
                        <mainClass>
                           com.tutorialspoint.cxf.jaxws.helloworld.Server
                        </mainClass>
                     </configuration>
                  </execution>
               </executions>
            </plugin>
         </plugins>
      </build>
   </profile>
</profiles>

在部署應用程式之前,您需要向專案新增兩個檔案。這些檔案在下面的螢幕截圖中顯示:

Before Deploy JAXWS Aplication

這些檔案是 CXF 標準檔案,用於定義 CXFServlet 的對映。為了便於您快速參考,這裡顯示了 web.xml 檔案中的程式碼:

//Web.xml
<?xml version = "1.0" encoding = "UTF-8"??>
<web-app xmlns = "http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5"
xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
   <display-name>cxf</display-name>
   <servlet>
      <description>Apache CXF Endpoint</description>
      <display-name>cxf</display-name>
      <servlet-name>cxf</servlet-name>
      <servlet-class>
         org.apache.cxf.transport.servlet.CXFServlet
      </servlet-class>
      <load-on-startup>
         1
      </load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>
         cxf
      </servlet-name>
      <url-pattern>
         /services/*
      </url-pattern>
   </servlet-mapping>
   <session-config>
      <session-timeout>60</session-timeout>
   </session-config>
</web-app>

cxf-servlet.xml 中,您聲明瞭服務端點的屬性。這在下面的程式碼片段中顯示:

<beans ...>
   <jaxws:endpoint xmlns:helloworld = "https://tutorialspoint.tw/"
      id = "helloHTTP"
      address = "https://:9090/HelloServerPort"
      serviceName = "helloworld:HelloServiceService"
      endpointName = "helloworld:HelloServicePort">
   </jaxws:endpoint>
</beans>

在這裡,我們定義了服務端點的 ID、服務可用的地址、服務名稱和端點名稱。現在,您瞭解了服務如何由 CXF servlet 路由和處理。

最終的 pom.xml

pom.xml 包含更多依賴項。我們沒有描述所有依賴項,而是在下面包含了 pom.xml 的最終版本:

<?xml version = "1.0" encoding = "UTF-8"??>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0
   http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>cxf-jaxws</artifactId>
   <version>1.0</version>
   <packaging>jar</packaging>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
   <profiles>
      <profile>
         <id>server</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <version>1.6.0</version>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.cxf.jaxws.helloworld.Server
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
      <profile>
         <id>client</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        <goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.cxf.jaxws.helloworld.Client
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
   </profiles>
   <dependencies>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-frontend-jaxws</artifactId>
         <version>3.3.0</version>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http</artifactId>
         <version>3.3.0</version>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-features-logging</artifactId>
         <version>3.3.0</version>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
   </dependencies>
</project>

請注意,它還包含一個用於構建客戶端的配置檔案,我們將在本教程的後續部分學習。

執行 HelloWorld 服務

現在,您已準備好執行 Web 應用程式。在命令視窗中,使用以下命令執行構建指令碼。

mvn clean install
mvn -Pserver

您將在控制檯上看到以下訊息:

INFO: Setting the server's publish address to be https://:9090/HelloServerPort
Server ready…

與之前一樣,您可以透過在瀏覽器中開啟伺服器 URL 來測試伺服器。

Opening Server URL

由於我們沒有指定任何操作,因此我們的應用程式僅向瀏覽器返回錯誤訊息。

現在,嘗試將 ?wsdl 新增到您的 URL,您將看到以下輸出:

因此,我們的伺服器應用程式按預期執行。您可以使用之前描述的 SOAP 客戶端(例如 Postman)進一步測試您的服務。

在下一節中,我們將學習如何編寫使用我們服務的客戶端。

開發客戶端

在 CXF 應用程式中編寫客戶端與編寫伺服器一樣簡單。以下是客戶端的完整程式碼:

//Client.java
package com.tutorialspoint.cxf.jaxws.helloworld;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
public final class Client {
   private static final QName SERVICE_NAME
   = new QName("http://helloworld.jaxws.cxf.tutorialspoint.com/",
   "HelloWorld");
   private static final QName PORT_NAME
   = new QName("http://helloworld.jaxws.cxf.tutorialspoint.com/",
   "HelloWorldPort");
   private Client() {
   }
   public static void main(String[] args) throws Exception {
      Service service = Service.create(SERVICE_NAME);
      System.out.println("service created");
      String endpointAddress = "https://:9090/HelloServerPort";
      service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING,
      endpointAddress);
      HelloWorld hw = service.getPort(HelloWorld.class);
      System.out.println(hw.greetings("World"));
   }
}

在這裡,我們使用 CXF 提供的 Service 類繫結到已知服務。我們呼叫 Service 類的 create 方法以獲取服務例項。我們透過呼叫 service 例項上的 addPort 方法來設定已知埠。

現在,我們已準備好使用服務,我們首先透過呼叫 service 例項上的 getPort 方法獲取服務介面來實現。最後,我們呼叫 greetings 方法以在控制檯上列印問候訊息。

現在,您已經學習了使用 Apache CXF-First 方法的基本 CXF 知識,您將在下一章學習如何在 WSDL-First 方法中使用 CXF。

Apache CXF 與 WSDL 優先

您開發的 CXF-POJO 應用程式導致客戶端和伺服器之間存在非常緊密的耦合。直接訪問服務介面也可能帶來嚴重的安全威脅。因此,通常希望在客戶端和伺服器之間進行解耦,這可以透過使用 WSDL(Web Services Description Language)來實現。

我們在基於 XML 的 WSDL 文件中編寫 Web 服務介面。我們將使用一個工具將此 WSDL 對映到 Apache CXF 介面,然後由我們的客戶端和伺服器應用程式實現和使用。為了提供解耦,從 WSDL 開始是一種首選方法。為此,您需要首先學習一門新語言 - WSDL。編寫 WSDL 需要謹慎的方法,如果您在開始使用它之前能夠對它有一些瞭解,那就更好了。

在本課中,我們將從在 WSDL 文件中定義 Web 服務介面開始。我們將學習如何使用 CXF 從 WSDL 建立伺服器和客戶端應用程式。我們將保持應用程式簡單,以保持對 CXF 用法的關注。建立伺服器應用程式後,我們將使用內建的 CXF 類將其釋出到所需的 URL。

首先,讓我們描述我們將要使用的 WSDL。

HelloWorld 的 WSDL

我們將要實現的 Web 服務將擁有一個名為 greetings 的 Web 方法,它接受一個包含使用者名稱的 string 引數,並在將問候訊息附加到使用者名稱後,向呼叫方返回字串訊息。完整的 wsdl 如下所示:

//Hello.wsdl
<?xml version = "1.0" encoding = "UTF-8"?>
<wsdl:definitions xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns = "http://helloworld.tutorialspoint.com/"
   xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/"
   xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
   name = "HelloWorld"
   targetNamespace = "http://helloworld.tutorialspoint.com/">
   <wsdl:types>
      <xsd:schema attributeFormDefault = "unqualified"
         elementFormDefault = "qualified"
         targetNamespace = "http://helloworld.tutorialspoint.com/">
         <xsd:element name = "greetings" type = "tns:greetings"/>
         <xsd:complexType name = "greetings">
            <xsd:sequence>
               <xsd:element minOccurs = "0" name = "arg0" type = "xsd:string"/>
            </xsd:sequence>
         </xsd:complexType>
         <xsd:element name = "greetingsResponse"
         type = "tns:greetingsResponse"/>
         <xsd:complexType name = "greetingsResponse">
            <xsd:sequence>
               <xsd:element minOccurs = "0" name = "return" type = "xsd:string"/>
            </xsd:sequence>
         </xsd:complexType>
      </xsd:schema>
   </wsdl:types>
   <wsdl:message name = "greetings">
      <wsdl:part element = "tns:greetings" name = "parameters"> </wsdl:part>
   </wsdl:message>
   <wsdl:message name = "greetingsResponse">
      <wsdl:part element = "tns:greetingsResponse" name = "parameters"> </wsdl:part>
   </wsdl:message>
   <wsdl:portType name = "HelloWorldPortType">
      <wsdl:operation name = "greetings">
         <wsdl:input message = "tns:greetings" name = "greetings">  </wsdl:input>
         <wsdl:output message = "tns:greetingsResponse" name = "greetingsResponse">
         </wsdl:output>
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name = "HelloWorldSoapBinding" type = "tns:HelloWorldPortType">
      <soap:binding style = "document"
      transport = "http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name = "greetings">
         <soap:operation soapAction = "" style = "document"/>
         <wsdl:input name = "greetings"></wsdl:input>
         <wsdl:output name = "greetingsResponse">
            <soap:body use = "literal"/>
         </wsdl:output>
         </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name = "HelloWorldService">
      <wsdl:port binding = "tns:HelloWorldSoapBinding" name = "HelloWorldPort">
         <soap:address location = "https://:9090/HelloServerPort"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

請注意,編寫語法正確的 wsdl 一直是開發人員面臨的挑戰;有許多工具和線上編輯器可用於建立 wsdl。這些編輯器會要求您輸入要實現的訊息名稱以及您希望在訊息中傳遞的引數以及您希望客戶端應用程式接收的返回訊息型別。如果您瞭解 wsdl 語法,您可以手動編寫整個文件或使用其中一個編輯器建立自己的文件。

在上面的 wsdl 中,我們定義了一個名為 greetings 的訊息。該訊息傳遞給名為 HelloWorldService 的服務,該服務在 https://:9090/HelloServerPort 上執行。

有了這個,我們現在將繼續進行伺服器開發。在開發伺服器之前,我們需要將 Apache CXF 介面生成到我們的 Web 服務。這需要從給定的 wsdl 完成。為此,您使用一個名為 wsdl2java 的工具。

wsdl2java 外掛

由於我們將使用 maven 構建專案,因此您需要將以下外掛新增到 pom.xml 檔案中。

<plugins>
   <plugin>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-codegen-plugin</artifactId>
      <version>3.3.0</version>
      <executions>
         <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <configuration>
               <wsdlOptions>
                  <wsdlOption>
                     <wsdl>src/main/resources/hello.wsdl</wsdl>
                     <faultSerialVersionUID> 1 </faultSerialVersionUID>
                  </wsdlOption>
               </wsdlOptions>
            </configuration>
            <goals>
               <goal>wsdl2java</goal>
            </goals>
         </execution>
      </executions>
   </plugin>
</plugins>

請注意,我們將 wsdl 檔案的位置指定為 src/main/resources/Hello.wsdl。您需要確保為您的專案建立了適當的目錄結構,並將前面顯示的 hello.wsdl 檔案新增到指定資料夾。

wsdl2java 外掛將編譯此 wsdl 並在預定義資料夾中建立 Apache CXF 類。為了便於您參考,這裡顯示了完整的專案結構。

WSDL2Apache CXF Predefined Folder

現在,您已準備好使用 wsdl2java 生成的類建立伺服器。wsdl2java 建立的類在下面的圖中顯示:

WSDL2Apache CXF generated classes

生成的 Service 介面

在生成的類列表中,您必須注意其中一個類是 Apache CXF 介面 - 即 HelloWorldPortType.java。在您的程式碼編輯器中檢查此檔案。為了便於您參考,這裡顯示了檔案內容:

//HelloWorldPortType.java
package com.tutorialspoint.helloworld;
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;
/**
* This class was generated by Apache CXF 3.3.0
* 2019-02-11T12:05:55.220+05:30
* Generated source version: 3.3.0
*
*/

@WebService(targetNamespace = "http://helloworld.tutorialspoint.com/",
   name = "HelloWorldPortType")
@XmlSeeAlso({ObjectFactory.class})
public interface HelloWorldPortType {
   @WebMethod
   @RequestWrapper(localName = "greetings", targetNamespace =
      "http://helloworld.tutorialspoint.com/", className =
      "com.tutorialspoint.helloworld.Greetings")
      @ResponseWrapper(localName = "greetingsResponse", targetNamespace =
         "http://helloworld.tutorialspoint.com/", className =
         "com.tutorialspoint.helloworld.GreetingsResponse")
   @WebResult(name = "return", targetNamespace =
      "http://helloworld.tutorialspoint.com/")
   public java.lang.String greetings(
      @WebParam(name = "arg0", targetNamespace =
      "http://helloworld.tutorialspoint.com/")
      java.lang.String arg0
   );
}

請注意,該介面包含一個名為 greetings 的方法。這在我們 wsdl 中是一種訊息型別。wsdl2java 工具已將此方法新增到生成的介面中。現在,您可以理解,您在 wsdl 中編寫的任何訊息,都會在介面中生成相應的方法。

現在,您的任務是實現與您在 wsdl 中定義的各種訊息相對應的方法。請注意,在 Apache CXF-First 的早期示例中,我們從 Web 服務的 Apache CXF 介面開始。在這種情況下,Apache CXF 介面是從 wsdl 建立的。

實現服務介面

服務介面的實現很簡單。完整的實現如下所示:

//HelloWorldImpl.java
package com.tutorialspoint.helloworld;
public class HelloWorldImpl implements HelloWorldPortType {
   @Override
   public String greetings(String name) {
      return ("hi " + name);
   }
}

該程式碼實現了名為 greetings 的唯一介面方法。該方法採用一個 string 型別的引數,在其前面新增一個“hi”訊息,並將結果字串返回給呼叫方。

接下來,我們將編寫伺服器應用程式。

開發伺服器

開發伺服器應用程式再次很簡單。在這裡,我們將使用 CXF 提供的 Endpoint 類來發布我們的服務。這在以下兩行程式碼中完成:

HelloWorldPortType implementor = new HelloWorldImpl();
   Endpoint.publish("https://:9090/HelloServerPort",
      implementor,
      new LoggingFeature());

首先,我們建立服務實現類 HelloWorldImpl 的物件。然後,我們將此引用作為第二個引數傳遞給 publish 方法。第一個引數是釋出服務到的地址 - 客戶端將使用此 URL 訪問服務。以下是伺服器應用程式的完整原始碼:

//Server.java
package com.tutorialspoint.helloworld;
import javax.xml.ws.Endpoint;
import org.apache.cxf.ext.logging.LoggingFeature;
public class Server {
   public static void main(String[] args) throws Exception {
      HelloWorldPortType implementor = new HelloWorldImpl();
      Endpoint.publish("https://:9090/HelloServerPort",
         implementor,
         new LoggingFeature());
      System.out.println("Server ready...");
      Thread.sleep(5 * 60 * 1000);
      System.out.println("Server exiting");
      System.exit(0);
   }
}

要構建此伺服器類,您需要在 pom.xml 中新增一個構建配置檔案。如下所示:

<profile>
   <id>server</id>
   <build>
      <defaultGoal>test</defaultGoal>
      <plugins>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.6.0</version>
            <executions>
               <execution>
                  <phase>test</phase>
                  <goals>
                     <goal>java</goal>
                  </goals>
                  <configuration>
                     <mainClass>
                        com.tutorialspoint.helloworld.Server
                     </mainClass>
                  </configuration>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>
   <dependencies>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
   </dependencies>
</profile>

請注意,在配置中指定了 Server 類的完全限定名稱。此外,dependency 標籤指定我們將使用嵌入式 jetty Web 伺服器來部署伺服器應用程式。

部署伺服器

最後,要部署伺服器應用程式,您需要在 pom.xml 中進行一項修改,以將您的應用程式設定為 Web 應用程式。您需要新增到 pom.xml 中的程式碼如下所示:

<defaultGoal>install</defaultGoal>
<pluginManagement>
   <plugins>
      <plugin>
         <artifactId>maven-war-plugin</artifactId>
         <version>3.2.2</version>
         <configuration>
            <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
            <webResources>
               <resource>
                  <directory>src/main/resources</directory>
                  <targetPath>WEB-INF</targetPath>
                  <includes>
                     <include>*.wsdl</include>
                  </includes>
               </resource>
            </webResources>
         </configuration>
      </plugin>
   </plugins>
</pluginManagement>

在部署應用程式之前,您需要向專案新增兩個檔案。這些檔案在下面的螢幕截圖中顯示:

Before Deploy WSDL Application

這些檔案是 CXF 標準檔案,用於定義 CXFServlet 的對映。為了便於您快速參考,這裡顯示了 web.xml 檔案中的程式碼:

//cxf-servlet.xml
<web-app xmlns = "http://java.sun.com/xml/ns/javaee"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" version="2.5"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
   <display-name>cxf</display-name>
   <servlet>
      <description>Apache CXF Endpoint</description>
      <display-name>cxf</display-name>
      <servlet-name>cxf</servlet-name>
      <servlet-class>
         org.apache.cxf.transport.servlet.CXFServlet
      </servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>cxf</servlet-name>
      <url-pattern>/services/*</url-pattern>
   </servlet-mapping>
   <session-config>
      <session-timeout>60</session-timeout>
   </session-config>
</web-app>

cxf-servlet.xml 中,您聲明瞭服務端點的屬性。這在下面的程式碼片段中顯示:

<beans ...>
   <jaxws:endpoint xmlns:helloworld = "https://tutorialspoint.tw/"
      id="helloHTTP"
      address = "https://:9090/HelloServerPort"
      serviceName = "helloworld:HelloServiceService"
      endpointName = "helloworld:HelloServicePort">
   </jaxws:endpoint>
</beans>

在這裡,我們定義了服務端點的 ID、服務可用的地址、服務名稱和端點名稱。現在,您瞭解了服務如何由 CXF servlet 路由和處理。

最終的 pom.xml

pom.xml 包含更多依賴項。我們沒有描述所有依賴項,而是在下面包含了 pom.xml 的最終版本:

<?xml version="1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>cxf-wsdl</artifactId>
   <version>1.0</version>
   <packaging>jar</packaging>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
   <build>
      <defaultGoal>install</defaultGoal>
      <pluginManagement>
         <plugins>
            <plugin>
               <artifactId>maven-war-plugin</artifactId>
               <version>3.2.2</version>
               <configuration>
                  <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
                  <webResources>
                     <resource>
                        <directory>src/main/resources</directory>
                        <targetPath>WEB-INF</targetPath>
                        <includes>
                           <include>*.wsdl</include>
                        </includes>
                     </resource>
                  </webResources>
               </configuration>
            </plugin>
         </plugins>
      </pluginManagement>
      <plugins>
         <plugin>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-codegen-plugin</artifactId>
            <version>3.3.0</version>
            <executions>
               <execution>
                  <id>generate-sources</id>
                  <phase>generate-sources</phase>
                  <configuration>
                     <wsdlOptions>
                        <wsdlOption>
                           <wsdl>src/main/resources/Hello.wsdl</wsdl>
                           <faultSerialVersionUID>1</faultSerialVersionUID>
                        </wsdlOption>
                     </wsdlOptions>
                  </configuration>
                  <goals>
                     <goal>wsdl2java</goal>
                  </goals>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>
   <profiles>
      <profile>
         <id>server</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <version>1.6.0</version>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.helloworld.Server
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
         <dependencies>
            <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-transports-http-jetty</artifactId>
               <version>3.3.0</version>
            </dependency>
         </dependencies>
      </profile>
      <profile>
         <id>client</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.helloworld.Client
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
   </profiles>
   <dependencies>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-frontend-jaxws</artifactId>
         <version>3.3.0</version>
      </dependency>
     
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-management</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-features-metrics</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf.xjc-utils</groupId>
         <artifactId>cxf-xjc-runtime</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-features-logging</artifactId>
         <version>3.3.0</version>
      </dependency>
     
     <dependency>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>exec-maven-plugin</artifactId>
         <version>1.6.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>1.8.0-beta2</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
   </dependencies>
</project>

請注意,它還包含一個用於構建客戶端的配置檔案,我們將在稍後的部分中學習。

執行 HelloWorld 服務

現在,您已準備好執行 Web 應用程式。在命令視窗中,使用以下命令執行構建指令碼。

mvn clean install

這將從您的 wsdl 生成相應的 Apache CXF 類,編譯您的 Apache CXF 類,將伺服器部署到嵌入式 jetty 伺服器上並執行您的應用程式。

您將在控制檯上看到以下訊息:

INFO: Setting the server's publish address to be 
https://:9090/HelloServerPort
Server ready...

與之前一樣,您可以透過在瀏覽器中開啟伺服器 URL 來測試伺服器。

Opening Server URL

由於我們沒有指定任何操作,因此我們的應用程式僅向瀏覽器返回錯誤訊息。現在,嘗試將 ?wsdl 新增到您的 URL,您將看到以下輸出:

WSDL Output

因此,我們的伺服器應用程式按預期執行。您可以使用之前描述的 SOAP 客戶端(例如 Postman)進一步測試您的服務。

本教程的下一部分是編寫使用我們服務的客戶端。

開發客戶端

在 CXF 應用程式中編寫客戶端與編寫伺服器一樣重要。以下是客戶端的完整程式碼,它基本上只包含三行,其餘行只是將服務資訊列印給使用者。

//Client.java
package com.tutorialspoint.helloworld;
public class Client {
   public static void main(String[] args) throws Exception {
      //Create the service client with its default wsdlurl
      HelloWorldService helloServiceService = new HelloWorldService();
      System.out.println("service: " +
         helloServiceService.getServiceName());
      System.out.println("wsdl location: " +
         helloServiceService.getWSDLDocumentLocation());
      HelloWorldPortType helloService =
         helloServiceService.getHelloWorldPort();
      System.out.println(helloService.greetings
      (System.getProperty("user.name")));
   }
}

在這裡,我們簡單地建立服務 HelloWorldService 的例項,透過呼叫 getHelloWorldPort 方法獲取其埠,然後將 greetings 訊息傳遞給它。執行客戶端,您將看到以下輸出:

service: {http://helloworld.tutorialspoint.com/}HelloWorldService
wsdl location: file:/Users/drsarang/Desktop/tutorialpoint/cxf-
wsdl/src/main/resources/Hello.wsdl
hi drsarang

到目前為止,您已經學習瞭如何使用 CXF 與 Apache CXF-First 和 WSDL-First 架構。在 Apache CXF-First 方法中,您使用了一個帶有來自 CXF 庫的 **ServerFactoryBean** 類的 POJO 來建立伺服器。要建立客戶端,您使用了來自 CXF 庫的 **ClientProxyFactoryBean** 類。在 WSDL-First 方法中,您使用 **Endpoint** 類在所需的 URL 和指定的實現者處釋出服務。現在,您可以擴充套件這些技術以整合不同的協議和傳輸。

Apache CXF 與 JAX-RS

在繼續下一章之前,我們假設您知道如何在 Java 中編寫 RESTful Web 服務。我將向您展示如何在 JAX-RS(Java API for RESTful Web Services)之上使用 CXF。我們將建立一個維護最新電影列表的 Web 服務。當用戶請求電影時,他在請求中指定電影 ID,伺服器將找到電影並將其返回給客戶端。在我們的簡單示例中,我們將僅向客戶端返回電影名稱,而不是實際的二進位制 MP4 檔案。因此,讓我們開始建立 JAX-RS 應用程式。

宣告 Movie 元素

我們將宣告一個名為 Movie 的 XML 根元素,用於儲存給定電影的 id 和名稱。該元素在名為 Movie.java 的檔案中宣告。檔案內容如下所示:

//Movie.java
package com.tutorialspoint.cxf.jaxrs.movie;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "Movie")
public class Movie {
   private long id;
   private String name;
   public long getId() {
      return id;
   }
   public void setId(long id) {
      this.id = id;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
}

請注意,使用 **XmlRootElement** 標籤為 **Movie** 標籤宣告 XML 元素。接下來,我們將建立一個服務,在其資料庫中儲存電影列表。

建立 Movie 服務資料庫

為了儲存電影列表,我們使用 Java 提供的 **Map**,它儲存鍵值對。如果列表很大,您將使用外部資料庫儲存,這將更容易管理。在我們的簡單示例中,我們將在資料庫中僅儲存五部電影。MovieService 類的程式碼如下所示:

//MovieService.java
package com.tutorialspoint.cxf.jaxrs.movie;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@Path("/movieservice/")
@Produces("text/xml")
public class MovieService {
   long currentId = 123;
   Map<Long, Movie> movies = new HashMap<>();
   public MovieService() {
      init();
   }
   @GET
   @Path("/movie/{id}/")
   public Movie getMovie(@PathParam("id") String id) {
      long idNumber = Long.parseLong(id);
      return movies.get(idNumber);
   }
   final void init() {
      Movie c1 = new Movie();
      c1.setName("Aquaman");
      c1.setId(1001);
      movies.put(c1.getId(), c1);
      
      Movie c2 = new Movie();
      c2.setName("Mission Imposssible");
      c2.setId(1002);
      movies.put(c2.getId(), c2);
      
      Movie c3 = new Movie();
      c3.setName("Black Panther");
      c3.setId(1003);
      movies.put(c3.getId(), c3);
      
      Movie c4 = new Movie();
      c4.setName("A Star is Born");
      c4.setId(1004);
      movies.put(c4.getId(), c4);
      
      Movie c5 = new Movie();
      c5.setName("The Meg");
      c5.setId(1005);
      movies.put(c5.getId(), c5);
   }
}

請注意,我們使用以下兩個註解來指定電影服務的 URL 路徑及其返回型別:

@Path("/movieservice/")
@Produces("text/xml")

我們使用 @GET 和 @Path 註解指定 GET 請求的 URL,如下所示:

@GET
@Path("/movie/{id}/")

電影資料庫本身在 init 方法中初始化,我們向資料庫中添加了五部電影。

我們的下一個任務是編寫伺服器應用程式。

開發伺服器

要建立伺服器,我們使用 CXF 提供的 **JAXRSServerFactoryBean** 類。

JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();

我們透過呼叫 **setResourceClasses** 方法設定其資源類。

factory.setResourceClasses(Movie.class);
factory.setResourceClasses(MovieService.class);

我們透過呼叫 **setResourceProvider** 方法設定服務提供程式。

factory.setResourceProvider(MovieService.class,
new SingletonResourceProvider(new MovieService()));

我們透過呼叫 **setAddress** 方法設定所需的 **釋出** 地址:

factory.setAddress("https://:9000/");

最後,我們透過在 **factory** 例項上呼叫 create 方法來發布伺服器。

factory.create();

伺服器應用程式的完整程式碼如下所示:

//Server.java
package com.tutorialspoint.cxf.jaxrs.movie;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
public class Server {
   public static void main(String[] args) throws Exception {
      JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
      factory.setResourceClasses(Movie.class);
      factory.setResourceClasses(MovieService.class);  
      factory.setResourceProvider(MovieService.class,
         new SingletonResourceProvider(new MovieService()));
      factory.setAddress("https://:9000/");
      factory.create();
      
      System.out.println("Server ready...");
      Thread.sleep(5 * 60 * 1000);
      
      System.out.println("Server exiting ...");
      System.exit(0);
   }
}

最終的 pom.xml

這裡我們包含了 pom.xml 的最終版本:

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0
   http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>cxf-jaxrs</artifactId>
   <version>1.0</version>
   <packaging>jar</packaging>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
   <profiles>
      <profile>
         <id>server</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <version>1.6.0</version>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.cxf.jaxrs.movie.Server
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
         <dependencies>
            <dependency>
               <groupId>org.apache.cxf</groupId>
               <artifactId>cxf-rt-transports-http-jetty</artifactId>
               <version>3.3.0</version>
            </dependency>
         </dependencies>
      </profile>
      <profile>
         <id>client</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.cxf.jaxrs.movie.Client
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
   </profiles>
   <dependencies>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http</artifactId>
         <version>3.3.0</version>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-frontend-jaxrs</artifactId>
         <version>3.3.0</version>
         </dependency>
      <dependency>
         <groupId>jakarta.ws.rs</groupId>
         <artifactId>jakarta.ws.rs-api</artifactId>
         <version>2.1.5</version>
      </dependency>
      <dependency>
         <groupId>org.apache.httpcomponents</groupId>
         <artifactId>httpclient</artifactId>
         <version>4.5.7</version>
      </dependency>
   </dependencies>
</project>

開發客戶端

編寫 RS 客戶端非常簡單。我們只需建立一個 URL 物件並開啟其流。我們使用 CXF 提供的 IOUtils 類將輸入流的內容複製到本地流。

URL url = new URL("https://:9000/movieservice/movie/1002");
try (InputStream instream = url.openStream();
CachedOutputStream outstream = new CachedOutputStream()) {
   IOUtils.copy(instream, outstream);
}

客戶端應用程式的完整程式碼如下所示:

//Client.java
package com.tutorialspoint.cxf.jaxrs.movie;
import java.io.InputStream;
import java.net.URL;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.io.CachedOutputStream;
public class Client {
   public static void main(String[] args) throws Exception {
      URL url = new URL("https://:9000/movieservice/movie/1002");
      try (InputStream instream = url.openStream();
      CachedOutputStream outstream = new CachedOutputStream()) {
         IOUtils.copy(instream, outstream);
         String str = outstream.getOut().toString();
         System.out.println(str);
      }
   }
}

測試 JAX-RS 應用程式

在命令列視窗中使用以下命令執行伺服器:

mvn -Pserver

現在,您將在控制檯上看到以下訊息:

INFO: Setting the server's publish address to be https://:9000

現在,開啟您的瀏覽器並輸入以下 URL:

https://:9000/movieservice/movie/1002

您將在瀏覽器視窗中看到以下內容。

browser window

您可以透過使用我們開發的 Java 客戶端應用程式來呼叫服務,在單獨的命令列視窗中執行以下命令。

mvn -Pclient

您將看到以下輸出:

<?xml version="1.0" encoding = "UTF-8" standalone="yes"?>
<Movie><id>1002</id><name>Mission Imposssible</name></Movie>

CXF 示例提供了多個關於如何將 CXF 與 JAX-RS 一起使用的示例。鼓勵感興趣的讀者學習這些示例。

Apache CXF 與 JMS

如前所述,您可以將 CXF 與 JMS 傳輸一起使用。在這種情況下,客戶端將向已知的 Messaging Server 傳送 JMS 訊息。我們的伺服器應用程式持續監聽訊息伺服器以接收傳入的訊息。當訊息到達時,它會處理訊息,執行客戶端請求並將響應作為另一條訊息傳送回客戶端。

與之前一樣,我們首先建立一個示例伺服器應用程式,該應用程式提供一個名為 **sayHi** 的單個 Web 方法。

建立服務介面

我們的 **HelloWorld** 服務的服務介面如下所示:

//HelloWorld.java
package com.tutorialspoint.service;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public interface HelloWorld {
   @WebMethod
   String sayHi(@WebParam(name = "name") String name);
}

實現服務

服務介面的實現定義如下:

//HelloWorldImpl.java
package com.tutorialspoint.service.impl;

import javax.jws.WebService;
import com.tutorialspoint.service.HelloWorld;

@WebService
public class HelloWorldImpl implements HelloWorld {
   @Override
   public String sayHi(String name) {
      return "Hello " + name;
   }
}

實現簡單地向用戶返回一個 Hello 訊息。如您所見,介面及其實現類似於本教程中您迄今為止學習的所有早期專案。

現在,最重要的是建立一個伺服器應用程式,該應用程式設定訊息佇列並持續監聽傳入的訊息。

建立伺服器

在伺服器應用程式中,我們首先建立一個 **JMS** 端點,如下所示:

private static final String JMS_ENDPOINT_URI =
   "jms:queue:test.cxf.jmstransport.queue?timeToLive=1000"
      + "&jndiConnectionFactoryName=ConnectionFactory"
      + "&jndiInitialContextFactory"
      + "= org.apache.activemq.jndi.ActiveMQInitialContextFactory"
      + "&jndiURL = tcp://:61616";

請注意,我們在指定的埠上設定了一個佇列,該佇列存在指定的時間段。我們現在透過例項化 **org.apache.activemq.broker.BrokerService** 類來建立訊息服務。這是 **ActiveMQ** 訊息伺服器的伺服器類。

BrokerService broker = new BrokerService();

您可以使用除 **ActiveMQ** 之外的任何其他您選擇的訊息伺服器。我們現在將此伺服器連線到所需的 URI。

broker.addConnector("tcp://:61616");

我們為傳入訊息的資料儲存設定目錄:

broker.setDataDirectory("target/activemq-data");

最後,我們使用 start 方法啟動伺服器:

broker.start();

接下來,我們使用伺服器工廠 Bean 類(在我們之前的 POJO 應用程式中使用)建立服務 Bean **HelloWorld** 的例項:

Object implementor = new HelloWorldImpl();
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
factory.setServiceClass(HelloWorld.class);

接下來,我們在工廠中設定 JMS 端點,以便工廠將持續監聽傳入的訊息:

factory.setTransportId
(JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID);
factory.setAddress(JMS_ENDPOINT_URI);

最後,我們在工廠中設定實現類並開始執行它:

factory.setServiceBean(implementor);
factory.create();

此時,您的伺服器已啟動並正在執行。請注意,由於我們使用了與 POJO 應用程式中相同的工廠 Bean 類,因此不需要 CXFServlet 和 web.xml 檔案。

完整的伺服器應用程式程式碼如下所示:

//ServerJMS.java
package com.tutorialspoint.server;

import java.util.Collections;
import org.apache.cxf.ext.logging.LoggingFeature;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.apache.cxf.transport.jms.spec.JMSSpecConstants;
import com.tutorialspoint.service.HelloWorld;
import com.tutorialspoint.service.impl.HelloWorldImpl;
import org.apache.activemq.broker.BrokerService;

public final class ServerJMS {

   private static final String JMS_ENDPOINT_URI = 
      "jms:queue:test.cxf.jmstransport.queue?timeToLive=1000"
         + "&jndiConnectionFactoryName=ConnectionFactory"
         + "&jndiInitialContextFactory"
         + "= org.apache.activemq.jndi.ActiveMQInitialContextFactory"
         + "&jndiURL = tcp://:61616";

   public static void main(String[] args) throws Exception {

      BrokerService broker = new BrokerService();
      broker.addConnector("tcp://:61616");
      broker.setDataDirectory("target/activemq-data");
      broker.start();

      Object implementor = new HelloWorldImpl();
      JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
      factory.setServiceClass(HelloWorld.class);
      factory.setTransportId
      (JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID);
      factory.setAddress(JMS_ENDPOINT_URI);
      factory.setServiceBean(implementor);
      factory.setFeatures(Collections.singletonList(new LoggingFeature()));
      factory.create();

      System.out.println("Server ready...");
      Thread.sleep(5 * 60 * 1000);
      System.out.println("Server exiting");
      System.exit(0);
   }
}

新增依賴項

我們建立的伺服器應用程式使用 ActiveMQ 訊息伺服器。因此,您需要向專案中新增更多依賴項。為了讓您瞭解所需的額外依賴項,這裡顯示了完整的 pom.xml 檔案。

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 
   http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>cxf-jms</artifactId>
   <version>1.0</version>
   <packaging>jar</packaging>
   
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
   </properties>

   <profiles>
      <profile>
         <id>server</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <version>1.6.0</version>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.server.ServerJMS
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
      <profile>
         <id>client</id>
         <build>
            <defaultGoal>test</defaultGoal>
            <plugins>
               <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>exec-maven-plugin</artifactId>
                  <executions>
                     <execution>
                        <phase>test</phase>
                        <goals>
                           <goal>java</goal>
                        </goals>
                        <configuration>
                           <mainClass>
                              com.tutorialspoint.client.ClientJMS
                           </mainClass>
                        </configuration>
                     </execution>
                  </executions>
               </plugin>
            </plugins>
         </build>
      </profile>
   </profiles>

   <dependencies>
      <dependency>
         <groupId>org.apache.activemq</groupId>
         <artifactId>activemq-broker</artifactId>
         <version>5.15.8</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.activemq</groupId>
         <artifactId>activemq-kahadb-store</artifactId>
         <version>5.15.8</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-frontend-jaxws</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-jms</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-features-logging</artifactId>
         <version>3.3.0</version>
      </dependency>
      
      <dependency>
         <groupId>org.apache.cxf</groupId>
         <artifactId>cxf-rt-transports-http-jetty</artifactId>
         <version>3.3.0</version>
      </dependency>
   </dependencies>
</project>

執行伺服器

要開始執行伺服器,與之前的情況一樣,在命令視窗中鍵入以下命令:

mvn -Pserver

這將啟動 ActiveMQ 訊息伺服器,設定訊息佇列並建立一個持續監聽此佇列的工廠 Bean。

我們的下一個任務是建立客戶端應用程式。

建立客戶端

在客戶端應用程式中,我們首先設定與伺服器應用程式中使用的相同的 JMS 端點:

private static final String JMS_ENDPOINT_URI =
   "jms:queue:test.cxf.jmstransport.queue?timeToLive=1000"
      + "&jndiConnectionFactoryName=ConnectionFactory"
      + "&jndiInitialContextFactory"
      + " = org.apache.activemq.jndi.ActiveMQInitialContextFactory"
      + "&jndiURL = tcp://:61616";

我們在 POJO 應用程式中建立工廠。

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

我們設定端點 URI 和實現類,如下所示:

factory.setTransportId (JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID);
factory.setAddress (JMS_ENDPOINT_URI);
HelloWorld client = factory.create(HelloWorld.class);

最後,我們呼叫服務方法並列印其結果輸出:

String reply = client.sayHi("TutorialsPoint");
System.out.println(reply);

完整的客戶端程式碼如下所示:

// ClientJMS.java
package com.tutorialspoint.client;

import com.tutorialspoint.service.HelloWorld;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.jms.spec.JMSSpecConstants;

public final class ClientJMS {
   private static final String JMS_ENDPOINT_URI =
   "jms:queue:test.cxf.jmstransport.queue?timeToLive=1000"
   + "&jndiConnectionFactoryName=ConnectionFactory"
   + "&jndiInitialContextFactory"
   + " = org.apache.activemq.jndi.ActiveMQInitialContextFactory"
   + "&jndiURL = tcp://:61616";

   public static void main(String[] args) throws Exception {
      JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
      factory.setTransportId(JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID);
      factory.setAddress(JMS_ENDPOINT_URI);
      HelloWorld client = factory.create(HelloWorld.class);
      String reply = client.sayHi("TutorialsPoint");
      System.out.println(reply);
      System.exit(0);
   }
}

Apache CXF - 結論

CXF 提供了一種統一的方法來混合和匹配當今世界中存在的多種 Web 協議和傳輸,以建立 Web 應用程式。您學習瞭如何從傳統的 Java 介面開始建立一個使用 CXF 的 Web 應用程式。接下來,您學習瞭如何從 WSDL 開始建立 Web 應用程式及其客戶端。

WSDL 提供了服務介面的 XML 表示。您使用 wsdl2java 工具從 WSDL 建立 Java 介面,最後使用建立的介面編寫伺服器和客戶端。本教程還簡要介紹瞭如何在 RESTful Web 服務應用程式中使用 CXF。最後,我們還討論瞭如何將 CXF 與 JMS 一起使用。您現在可以參考 CXF-samples 以進行進一步的研究。

注意 - 整個專案的原始碼可以從此處下載。

廣告

© . All rights reserved.