1. 程式人生 > 實用技巧 >基於Spring + CXF框架的Web Service

基於Spring + CXF框架的Web Service

1、用CXF編寫基於Spring的Web service,也是需要分為Server伺服器端、Client客戶端的。

  1.1)、Server端,操作步驟,如下所示:

    第一步,建立spring的配置檔案beans.xml,在其中配置SEI。
    第二步,在web.xml中,配置上CXF的一些核心元件。

  1.2、Client端,操作步驟,如下所示:

    第一步,生成客戶端程式碼。
    第二步,建立客戶端的spring配置檔案beans-client.xml,並配置。
    第三步,編寫測試類請求web service。

2、建立一個動態web工程,將apache-cxf-2.5.9\lib目錄下面的包新增到此動態工程的lib目錄下面,然後Build Path一下的哦。如果要看原始碼,需要下載對應的src包的,不然無法進行檢視原始碼的。

建立web.xml配置檔案,如下所示:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 3     xmlns="http://java.sun.com/xml/ns/javaee" 
 4     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 5     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
 6     id="WebApp_ID
" version="2.5"> 7 <display-name>webServiceCxf_Spring</display-name> 8 <welcome-file-list> 9 <welcome-file>index.html</welcome-file> 10 <welcome-file>index.jsp</welcome-file> 11 </welcome-file-list> 12 13 <!-- 配置beans.xml --> 14
<context-param> 15 <param-name>contextConfigLocation</param-name> 16 <param-value>classpath:beans.xml</param-value> 17 </context-param> 18 <!-- 應用啟動的一個監聽器 --> 19 <listener> 20 <listener-class> 21 org.springframework.web.context.ContextLoaderListener 22 </listener-class> 23 </listener> 24 25 <!-- 所有請求都會先經過cxf框架 --> 26 <servlet> 27 <servlet-name>CXFServlet</servlet-name> 28 <servlet-class> 29 org.apache.cxf.transport.servlet.CXFServlet 30 </servlet-class> 31 <load-on-startup>1</load-on-startup> 32 </servlet> 33 <servlet-mapping> 34 <servlet-name>CXFServlet</servlet-name> 35 <url-pattern>/*</url-pattern> 36 </servlet-mapping> 37 38 </web-app>

建立webservice的介面服務,如下所示:

 1 package com.bie.webservice.ws;
 2 
 3 import javax.jws.WebMethod;
 4 import javax.jws.WebService;
 5 
 6 import com.bie.webservice.bean.Order;
 7 
 8 /**
 9  * 
10  * @author
11  *
12  */
13 @WebService
14 public interface OrderWS {
15 
16     /**
17      * 根據訂單號獲取到訂單資訊
18      * 
19      * @param id
20      * @return
21      */
22     @WebMethod
23     public Order getOrderById(int id);
24 
25 }

建立webservice的介面服務實現類,如下所示:

 1 package com.bie.webservice.ws.impl;
 2 
 3 import javax.jws.WebService;
 4 
 5 import com.bie.webservice.bean.Order;
 6 import com.bie.webservice.ws.OrderWS;
 7 
 8 /**
 9  * 
10  * @author 專案部署的時候就建立好了webservice服務了。專案啟動就自動部署了webservice了。
11  *
12  */
13 @WebService
14 public class OrderWSImpl implements OrderWS {
15 
16     /**
17      * 從物件什麼時候建立呢,結論,專案部署的時候執行了
18      */
19     public OrderWSImpl() {
20         System.out.println("無參構造器執行,OrderWSImpl......");
21     }
22 
23     @Override
24     public Order getOrderById(int id) {
25         System.out.println("Server getOrderById() :  " + id);
26         return new Order(1, "大飛機", 999999999.00);
27     }
28 
29 }

建立一個是實體類,如下所示:

 1 package com.bie.webservice.bean;
 2 
 3 /**
 4  * 
 5  * @author
 6  *
 7  */
 8 public class Order {
 9 
10     private int id;// 訂單編號
11     private String name;// 訂單名稱
12     private Double price;// 訂單價格
13 
14     public int getId() {
15         return id;
16     }
17 
18     public void setId(int id) {
19         this.id = id;
20     }
21 
22     public String getName() {
23         return name;
24     }
25 
26     public void setName(String name) {
27         this.name = name;
28     }
29 
30     public Double getPrice() {
31         return price;
32     }
33 
34     public void setPrice(Double price) {
35         this.price = price;
36     }
37 
38     @Override
39     public String toString() {
40         return "Order [id=" + id + ", name=" + name + ", price=" + price + "]";
41     }
42 
43     public Order(int id, String name, Double price) {
44         super();
45         this.id = id;
46         this.name = name;
47         this.price = price;
48     }
49 
50 }

建立beans.xml配置檔案,如下所示:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:jaxws="http://cxf.apache.org/jaxws"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6         http://www.springframework.org/schema/beans/spring-beans.xsd
 7         http://cxf.apache.org/jaxws 
 8         http://cxf.apache.org/jaxws">
 9     
10     <!-- 引cxf-2.5.9.jar此包下面的配置,這些配置不在此專案中,cxf的一些核心配置 -->
11     <import resource="classpath:META-INF/cxf/cxf.xml" />
12     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
13     <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
14 
15     <!-- 
16         jaxws:endpoint標籤配置終端。
17         implementor是實現類。
18         address是虛擬地址。
19      -->
20     <jaxws:endpoint id="orderWS"
21         implementor="com.bie.webservice.ws.impl.OrderWSImpl"
22         address="/orderws">
23         <!-- 
24             <jaxws:inInterceptors> 
25                 <bean class="com.bie.interceptor.CheckUserInterceptor"></bean> 
26             </jaxws:inInterceptors> 
27         -->
28     </jaxws:endpoint>
29 
30 </beans>

專案結構,如下所示:

然後可以進行釋出了,Run as -> Run on Server,啟動專案,專案啟動就自動部署了webservice了,可以進行訪問,如下所示:

訪問地址:http://localhost:8080/webServiceCxf_Spring/orderws?wsdl,其中專案名稱後面的地址是beans.xml裡面配置的address屬性的值。

可以使用eclipse自帶的web service瀏覽器進行檢視,如下所示:

3、然後,建立一個客戶端訪問的動態web工程,將apache-cxf-2.5.9\lib目錄下面的包新增到此動態工程的lib目錄下面,然後Build Path一下的哦。如果要看原始碼,需要下載對應的src包的,不然無法進行檢視原始碼的。此時,還是需要藉助java自帶的工具來生成客戶端的程式碼,如下所示:

重新整理專案,就可以看到生成的程式碼了,如下所示:

然後建立一個配置檔案client-beans.xml,裡面需要進行配置webservice的請求地址和所需要生成的動態代理物件。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:jaxws="http://cxf.apache.org/jaxws"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6        http://www.springframework.org/schema/beans/spring-beans.xsd
 7     http://cxf.apache.org/jaxws http://cxf.apache.org/jaxws">
 8 
 9     <!-- 
10         客戶端配置:
11         serviceClass根據此類動態產生介面的代理物件。
12         address是指的是webservice請求的地址。
13      -->
14     <jaxws:client id="orderClient"
15         serviceClass="com.bie.webservice.ws.OrderWS"
16         address="http://localhost:8080/webServiceCxf_Spring/orderws">
17 
18         <!-- <jaxws:outInterceptors>
19             <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
20             <bean
21                 class="com.bie.webservice.ws.interceptor.AddUserInterceptor">
22                 <constructor-arg name="name" value="張姍姍" />
23                 <constructor-arg name="password" value="123456" />
24             </bean>
25         </jaxws:outInterceptors> -->
26     </jaxws:client>
27 
28 </beans>

最後搞一個客戶端的訪問類,就可以進行運行了,如下所示:

 1 package com.bie.webservice.ws.client;
 2 
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 
 5 import com.bie.webservice.ws.Order;
 6 import com.bie.webservice.ws.OrderWS;
 7 
 8 /**
 9  * 
10  * @author 客戶端訪問webservice的程式碼
11  *
12  */
13 public class ClientCxfSpring {
14 
15     public static void main(String[] args) {
16         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
17                 new String[] { "client-beans.xml" });
18         OrderWS orderWS = (OrderWS) context.getBean("orderClient");
19         Order order = orderWS.getOrderById(1);
20         System.out.println(order);
21     }
22 
23 }

4、如何在此基礎上新增自定義攔截器,步驟如下所示:

第一步,Server端,在beans.xml中,在endpoint中配置上入攔截器。

 1 package com.bie.webservice.interceptor;
 2 
 3 import javax.xml.namespace.QName;
 4 
 5 import org.apache.cxf.binding.soap.SoapMessage;
 6 import org.apache.cxf.headers.Header;
 7 import org.apache.cxf.interceptor.Fault;
 8 import org.apache.cxf.phase.AbstractPhaseInterceptor;
 9 import org.apache.cxf.phase.Phase;
10 import org.w3c.dom.Element;
11 import org.w3c.dom.Node;
12 
13 /**
14  * 查檢使用者的攔截器
15  * 
16  * @author
17  *
18  */
19 
20 public class CheckUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
21 
22     private static final String name = "張姍姍";
23     private static final String password = "123456";
24     
25     public CheckUserInterceptor() {
26         super(Phase.PRE_PROTOCOL);
27         System.out.println("CheckUserInterceptor()");
28     }
29 
30     /**
31      * 請求體,伺服器端需要解析請求頭資訊
32      * 
33      * <soap:Envelope> 
34      *     <head>
35      *         <zhangsansan>
36      *             <name>zhangsansan</name>
37      *             <password>123456</password>
38      *         </zhangsansan>
39      *  </head>
40      *     <Body> 
41      *         <sayHello> 
42      *             <arg0>張姍姍</arg0> 
43      *         </sayHello> 
44      *     </Body>
45      * </soap:Envelope>
46      */
47     @Override
48     public void handleMessage(SoapMessage soapMessage) throws Fault {
49         // 獲取到請求頭的資訊
50         QName qName = QName.valueOf("zhangsansan");
51         // 獲取到請求頭
52         Header header = soapMessage.getHeader(qName);
53         // 判斷是否為空
54         if(header != null) {
55             // 獲取到物件,強轉為w3c的元素標籤
56             Element element = (Element) header.getObject();
57             // 獲取到name標籤的值
58             Node nameNode = element.getElementsByTagName("name").item(0);
59             // 獲取到name的值
60             String nameValue = nameNode.getTextContent();
61             // 獲取到pasword標籤的值
62             Node passwordNode = element.getElementsByTagName("password").item(0);
63             // 獲取到pasword的值
64             String paswordValue = passwordNode.getTextContent();
65             // 開始進行判斷
66             if(CheckUserInterceptor.name.equals(nameValue) && CheckUserInterceptor.password.equals(paswordValue)) {
67                 System.out.println("Server 通過攔截器......");
68                 return;
69             }
70         }
71         // 如果不能通過
72         System.out.println("Sorry Server 不通過攔截器......");
73         // 丟擲異常資訊
74         throw new Fault(new RuntimeException("賬號密碼錯誤......"));
75     }
76 
77 }

然後在伺服器端的beans.xml進行配置,如下所示:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4    xmlns:jaxws="http://cxf.apache.org/jaxws"
 5    xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6    http://www.springframework.org/schema/beans/spring-beans.xsd
 7         http://cxf.apache.org/jaxws http://cxf.apache.org/jaxws">
 8     
 9     <!-- 引cxf-2.5.9.jar此包下面的配置,這些配置不在此專案中,cxf的一些核心配置 -->
10     <import resource="classpath:META-INF/cxf/cxf.xml" />
11     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
12     <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
13 
14     <!-- 
15         jaxws:endpoint標籤配置終端。
16         implementor是實現類。
17         address是虛擬地址。
18      -->
19     <jaxws:endpoint id="orderWS"
20         implementor="com.bie.webservice.ws.impl.OrderWSImpl"
21         address="/orderws">
22             <!-- 伺服器端配置入攔截器 -->
23             <jaxws:inInterceptors> 
24                 <!-- 配置自定義的攔截器,注入到ioc容器中 -->
25                 <bean class="com.bie.webservice.interceptor.CheckUserInterceptor"></bean> 
26             </jaxws:inInterceptors> 
27     </jaxws:endpoint>
28 
29 </beans>

如果開發完畢,重啟專案,報下面的錯誤,我的操作是clean專案,清空tomcat下面的專案,清空tomcat,重啟專案解決的。

1 cvc-complex-type.2.4.c: 萬用字元的匹配很全面, 但無法找到元素 'context:property-placeholder' 的宣告

第二步,Client端,通過Client物件設定出攔截器。

  1 package com.bie.webservice.ws.interceptor;
  2 
  3 import java.util.List;
  4 
  5 import javax.xml.namespace.QName;
  6 import javax.xml.parsers.DocumentBuilder;
  7 import javax.xml.parsers.DocumentBuilderFactory;
  8 import javax.xml.parsers.ParserConfigurationException;
  9 
 10 import org.apache.cxf.binding.soap.SoapMessage;
 11 import org.apache.cxf.headers.Header;
 12 import org.apache.cxf.interceptor.Fault;
 13 import org.apache.cxf.phase.AbstractPhaseInterceptor;
 14 import org.apache.cxf.phase.Phase;
 15 import org.apache.xml.utils.DOMHelper;
 16 import org.w3c.dom.Document;
 17 import org.w3c.dom.Element;
 18 
 19 /**
 20  * 
 21  * @author 攔截的是某一個訊息,所以泛型是SoapMessage
 22  *
 23  */
 24 public class ClientValidateUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
 25 
 26     private String name;// 賬號
 27     private String password;// 密碼
 28 
 29     /**
 30      * 此構造器是關鍵點,決定了什麼時候攔截器會攔截到訊息
 31      * 
 32      * @param phase
 33      */
 34     public ClientValidateUserInterceptor(String name, String password) {
 35         // 準備協議化的時候進行攔截呼叫
 36         super(Phase.PRE_PROTOCOL);
 37         this.name = name;
 38         this.password = password;
 39         System.out.println("Client 客戶端,攔截器初始化......");
 40     }
 41 
 42     /**
 43      * 請求體
 44      * 
 45      * <soap:Envelope> 
 46      *     <head>
 47      *         <zhangsansan>
 48      *             <name>zhangsansan</name>
 49      *             <password>123456</password>
 50      *         </zhangsansan>
 51      *  </head>
 52      *     <Body> 
 53      *         <sayHello> 
 54      *             <arg0>張姍姍</arg0> 
 55      *         </sayHello> 
 56      *     </Body>
 57      * </soap:Envelope>
 58      */
 59     @Override
 60     public void handleMessage(SoapMessage soapMessage) throws Fault {
 61         // 獲取到頭資訊,向頭部資訊設定值
 62         List<Header> headers = soapMessage.getHeaders();
 63         // 此時需要構造這種結構的資料
 64 //        <zhangsansan>
 65 //            <name>zhangsansan</name>
 66 //            <password>123456</password>
 67 //        </zhangsansan>
 68         
 69         // 第一步:初始化一個XML解析工廠
 70         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 71         // 第二步:建立一個DocumentBuilder例項
 72         DocumentBuilder builder = null;
 73         try {
 74             builder = factory.newDocumentBuilder();
 75         } catch (ParserConfigurationException e) {
 76             // TODO Auto-generated catch block
 77             e.printStackTrace();
 78         }
 79         // 第三步:構建一個Document例項
 80         Document doc = builder.newDocument();
 81         // standalone用來表示該檔案是否呼叫其它外部的檔案。若值是 ”yes” 表示沒有呼叫外部檔案
 82         doc.setXmlStandalone(true);
 83         // 第四步:建立一個根節點,名稱為root,並設定一些基本屬性,建立標籤
 84         Element rootElement = doc.createElement("zhangsansan");
 85         // 設定節點屬性
 86         // rootElement.setAttribute("attr", "root");
 87         // 設定標籤之間的內容
 88         // rootElement.setTextContent("root attr");
 89         
 90         // 開始設定<zhangsansan>下面的標籤<name>zhangsansan</name>
 91         Element nameElement = doc.createElement("name");
 92         nameElement.setTextContent(this.name);
 93         // 第五步:把節點新增到Document中,再建立一些子節點加入,將子標籤新增到父標籤中
 94         rootElement.appendChild(nameElement);
 95         
 96         // 開始設定<zhangsansan>下面的標籤<name>zhangsansan</name>
 97         Element passwordElement = doc.createElement("password");
 98         passwordElement.setTextContent(this.password);
 99         // 第五步:把節點新增到Document中,再建立一些子節點加入,將子標籤新增到父標籤中
100         rootElement.appendChild(passwordElement);
101         
102         // 第六步:把構造的XML結構,寫入到具體的檔案中
103         // 引數一QName起一個唯一的名字,這個名稱必須和rootElement標籤的值必須一樣
104         // 引數二就是rootElement根節點
105         Header header = new Header(new QName("zhangsansan"), rootElement);
106         // 將此請求體和構建的請求頭髮送給伺服器端
107         headers.add(header);
108         
109         System.out.println("Client handleMessage Interceptor......");
110         
111         // DOMHelper.createDocument()方法過期了
112         // Document createDocument = DOMHelper.createDocument();
113     }
114 
115 }

然後在客戶端的beans.xml進行配置,如下所示:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:jaxws="http://cxf.apache.org/jaxws"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6        http://www.springframework.org/schema/beans/spring-beans.xsd
 7     http://cxf.apache.org/jaxws http://cxf.apache.org/jaxws">
 8 
 9     <!-- 
10         客戶端配置:
11         serviceClass根據此類動態產生介面的代理物件。
12         address是指的是webservice請求的地址。
13      -->
14     <jaxws:client id="orderClient"
15         serviceClass="com.bie.webservice.ws.OrderWS"
16         address="http://localhost:8080/webServiceCxf_Spring/orderws">
17 
18         <!-- 客戶端配置出攔截器類 -->
19         <jaxws:outInterceptors>
20             <!-- <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> -->
21             <bean
22                 class="com.bie.webservice.ws.interceptor.ClientValidateUserInterceptor">
23                 <!-- 通過構造引數傳遞給客戶端攔截器類 -->
24                 <constructor-arg name="name" value="張姍姍" />
25                 <constructor-arg name="password" value="123456" />
26             </bean>
27         </jaxws:outInterceptors>
28     </jaxws:client>
29 
30 </beans>

使用tomcat啟動服務端,然後客戶端訪問服務端,效果如下所示: