引入:html
咱們已經用前2篇的文章用2種方法建立了web service的endpoint而且對外提供服務。如今咱們假設要對來往的消息進行處理,而這些處理應該和咱們業務代碼正交(也就是AOP,好比咱們要吧消息寫入日誌,或者區分是去web service站點的請求消息仍是從web service站點返回的相應消息),爲了知足這個需求,咱們能夠利用SOAPHandler來完成。java
代碼實踐:web
簡單來講,SOAPHandler就是用來對於SOAP消息進行處理的類,爲了實現AOP,咱們有兩種方式來實現對SOAP消息的處理。apache
一種是實現SOAPHandler<SOAPMessageContext>接口,它會對於整個SOAP消息進行處理。服務器
一種是實現LogicalHandler<LogicalMessageContext>接口,它會對消息的payload 進行處理。cookie
咱們就分別寫2個處理器,來演示這2種用法。ide
服務器端:工具
首先,咱們開發一個LogHandler,它會攔截交互的SOAP消息而且打印出消息內容,咱們讓其採用第一種方式,實現SOAPHandler<SOAPMessageContext>接口:測試
/** * SOAP Handler能夠用來對SOAP消息進行訪問。 * 有2種Handler,一種是訪問整個消息的, 一種是隻訪問SOAP消息payload的 * 這裏演示的是第一種,它必須實現SOAPHandler<SOAPMessageContext>接口 */ package com.charles.cxfstudy.server.handlers; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; /** * 演示訪問整個SOAP消息的Handler的用法 * @author charles.wang * */ public class LogHandler implements SOAPHandler<SOAPMessageContext>{ /** * 如何去處理SOAP消息的邏輯。 * 這裏會先打印當前調用的方法,而後從消息上下文中取出消息,而後寫到標準輸出流 */ public boolean handleMessage(SOAPMessageContext context) { System.out.println("LogHandler->handleMessage(context) method invoked"); SOAPMessage message = context.getMessage(); try{ message.writeTo(System.out); System.out.println(); }catch(Exception ex){ System.err.print("Exception occured when handling message"); } return true; } /** * 如何去處理錯誤的SOAP消息 * 這裏會先打印當前調用的方法,而後從消息上下文中取出消息,而後寫到標準輸出流 */ public boolean handleFault(SOAPMessageContext context) { System.out.println("LogHandler->handleFault(context) method invoked"); SOAPMessage message = context.getMessage(); try{ message.writeTo(System.out); System.out.println(); }catch(Exception ex){ System.err.print("Exception occured when handling fault message"); } return true; } /** * 這裏沒有資源清理的需求,因此咱們只打印動做到控制檯 */ public void close(MessageContext context) { System.out.println("LogHandler->close(context) method invoked"); } public Set<QName> getHeaders() { return null; } }
而後咱們再開發AddCustomizedPartHandler,這個Handler會判斷這個消息是入站(往web service Endpoint發送的請求消息)消息仍是出站(從web service Endpoint返回的消息)消息而後打印出結果,咱們採用第二種方式,讓其實現 LogicalHandler<LogicalMessageContext>接口:spa
/** * SOAP Handler能夠用來對SOAP消息進行訪問。 * 有2種Handler,一種是訪問整個消息的, 一種是隻訪問SOAP消息payload的 * 這裏演示的是第二種,它必須實現LogicalHandler<LogicalMessageContext>接口 */ package com.charles.cxfstudy.server.handlers; import javax.xml.ws.handler.LogicalHandler; import javax.xml.ws.handler.LogicalMessageContext; import javax.xml.ws.handler.MessageContext; /** * 演示訪問SOAP消息的payload的Handler的用法 * @author charles.wang * */ public class AddCustomizedPartHandler implements LogicalHandler<LogicalMessageContext> { /** * 如何去處理SOAP消息的邏輯,它會去判斷這是入站仍是出站消息 */ public boolean handleMessage(LogicalMessageContext context) { System.out.println("AddCustomizedPartHandler->handleMessage(context) invoked"); //先判斷消息來源是入站仍是出站的 //(入站表示是發送到web service站點的消息,出站表示是從web service站點返回的消息) boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); //若是是出站消息 if(outbound){ System.out.println("This is an outbound message"); }else{ System.out.println("This is an inbound message"); } return true; } public boolean handleFault(LogicalMessageContext context) { System.out.println("AddCustomizedPartHandler->handleFault(context) invoked"); return true; } public void close(MessageContext context) { System.out.println("AddCustomizedPartHandler->close(context) invoked"); } }
爲了讓這2個handler生效,咱們必須寫一個配置文件,來配置處理器鏈,這裏咱們自定義某個handler_chains.xml(能夠是任意名字),而且在其中配置了2個處理器,定義他們的順序和實現類:
<?xml version="1.0" encoding="UTF-8"?> <handler-chains xmlns="http://java.sun.com/xml/ns/javaee"> <handler-chain> <handler> <handler-name>AddCustomizedPartHandler</handler-name> <handler-class>com.charles.cxfstudy.server.handlers.AddCustomizedPartHandler</handler-class> </handler> <handler> <handler-name>LogHandler</handler-name> <handler-class>com.charles.cxfstudy.server.handlers.LogHandler</handler-class> </handler> </handler-chain> </handler-chains>
從這裏看出,判斷入站出站的處理器在前,打印日誌的處理器在後。
爲了讓咱們的Handler鏈生效到咱們的web service的Endpoint,咱們必須在服務接口或者服務實現類上用@HandlerChain註解來配置這個Handler鏈定義文件。這裏,咱們爲前面開發的加法運算的web服務激活處理器,因此在CalcServiceImpl上咱們採用了這個註解:
@WebService(endpointInterface="com.charles.cxfstudy.server.services.ICalcService") //這裏展現如何用@HandlerChain來聲明一組Handler,他們會對指定的web service使用的SOAP消息進行處理,相似AOP @HandlerChain(file="/handler_chains.xml") public class CalcServiceImpl implements ICalcService { ...
這時候,打包應用而且部署在服務器上,咱們服務器端的代碼就完成了。
客戶端:
爲了演示客戶端和服務器端的交互是否會自動觸發Handler的執行,咱們寫一個客戶端,首先咱們用JDK的wsimport工具來從給定的wsdl文件生成一組客戶端的java文件:
這樣它會在咱們給定目錄用給定的包名生成一組文件:
咱們把這些文件複製到咱們客戶端的項目應用中,而後編寫測試方法以下:
/** * 客戶端測試代碼 */ package com.charles.cxfclient; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; /** * @author charles.wang * */ public class MainTest { public static void main(String [] args){ JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(ICalcService.class); factory.setAddress("http://localhost:8080/cxf_jaxws_server/services/calc"); //調用業務方法 ICalcService service = (ICalcService) factory.create(); int a = 3,b=5; System.out.println("調用CalcService進行加法計算,2個運算子分別是:"+a+","+b); System.out.println("加法運算結果爲:" + service.calcSum(a, b)); } }
執行,它顯然會打印出執行結果,不過這個執行的結果運算是經過 web service的調用完成的。
咱們來看服務器的日誌,以下圖:
顯然,這兩個Handler都被正確的調用了(能夠比較咱們的Handler的代碼),好比AddCustomizedPartHandler會正確的識別這是inbound 仍是outbound消息,好比LogHandler能吧inbound消息(就是圖片中內含<a>3</a><b>5</b>的)和outbound消息(就是圖片中內涵<return>8</return>的)都打印出來,因此證實咱們開發的Handler是正確的。
固然了,Handler還能夠作不少更高級別的功能,好比檢驗cookie,持久化消息,添加自定義頭等,有待咱們發覺,可是代碼的結構大致和咱們例子類似。