基於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啟動服務端,然後客戶端訪問服務端,效果如下所示: