CXF攔截器(Interceptor)的使用
CXF的攔截器是CXF功能最主要的擴充點。通過自定義的Interceptor,可以改變請求和響應的一些消息處理,其中最基本的原理還是一個動态代理。
Interceptor是CXF架構中一個很有特色的模式。你可以在不對核心子產品進行修改的情況下,動态添加很多功能。這對于CXF這個以處理消息為中心的服務架構來說是非常有用的,CXF通過在Interceptor中對消息進行特殊處理,實作了很多重要功能子產品,例如:日志記錄,Soap消息處理,消息的壓縮處理。
如果你想對CXF進行擴充,建議你先從interceptor開始。
為了更好的學習和使用CXF,最好先閱讀官方的使用者手冊:
http://cwiki.apache.org/CXF20DOC/index.html
一、基本原理
下面看看CFX Interceptor在整個請求響應的處理過程中所處的位置。
二、CFX Interceptor的核心API
先看攔截器核心包org.apache.cxf.interceptor的說明:Core interceptor interfaces which form the basis for message processing chains in CXF.
翻譯:CXF消息處理鍊最基本的攔截器接口。
一下幾個的API的介紹和翻譯來自網際網路:
Interceptor
定義兩個方法,一個處理消息 handleMessage, 一個是處理錯誤 handleFault。别看Interceptor這麼簡單,這裡需要提醒注意的是,在實行具體的Interceptor的這兩個方法中,千萬别調用Interceptor内部的成員變量。這是由于Interceptor是面向消息來進行處理的,每個Interceptor都有可能運作在不同的線程中,如果調用了Interceptor中的内部成員變量,就有在Interceptor中造成臨界資源的通路的情況,而這時的Interceptor也就不是線程安全的Interc eptor了。
在CXF中最常使用的Interceptor都放在cxf-rt-core中的org.apache.cxf.interceptor中,有興趣的朋友可以研究一下。
InterceptorChain
單個的Interceptor功能有限,CXF要實作一個SOAP消息處理,需要将許許多多的Interceptor組合在一起使用。是以設計了 InterceptorChain,在我看了InterceptorChain就像是一個Interceptor的小隊長。 小隊長有調配安置Interceptor的權力(add,remove),也有控制消息處理的權力(doInterceptor,pause,resume,reset,abort),同時也有傳遞錯誤處理的權力( {get|set}FaultObserver)。更有意思的是為靈活控制Interceptor的處理消息順序(doInterceptStartingAt,doInterceptorStartingAfter),這也是InterceptorChain比較難了解的地方。
有興趣的朋友可以跟蹤一下,CXF的Client與Server之間通訊是走過哪些Interceptor,這些Interceptor是如何被調用的。
Fault
定義了CXF中的錯誤消息。
InterceptorProvider
這裡定義了Interceptor的後備保障部隊。我們可以在InterceptorProvider中設定In,Out,InFault,OutFault 後備小分隊,添加我們所希望添加的Interceptor。而InterceptorChain會根據這些後備小分隊,組建自己的小分隊執行個體,完成具體的作戰功能任務。
AbstractAttributedInterceptorProvider
InterceptorProvider實作的抽象類,由于這個類來繼承了HashMap,我們可以像這個類中存儲一些屬性資訊。
AbstractBasicInterceptorProvider
與AbstractAttributedInterceptorProvider不同,這個Interceptor隻是簡單實作了InterceptorProvider的功能,并不提供對其屬性存儲的擴充。
Message
由于Interceptor是針對Message來進行處理的,當你打開Message這個類檔案時,你會發現在Message中定義了很多常量,同時你還可以從Message中擷取到很多與Message操作相關的資訊。可以擷取設定的對象有InterceptorChain Exchange Destination,還有擷取設定Content的泛型接口,是不是感覺Message和Bus差不多,都成了大雜貨鋪,一切與消息處理相關的資訊都可以放在Message中。我想這也是咱CXF以Message處理為中心的設計思想的具體表現吧。
Exchange
和Message打交道就離不開Exchange。Exchange建立In/Out,InFault/OutFault Message 之間的聯系。你可以從Exchange中擷取到與消息傳輸相關的Conduit,Destination的資訊,同時也可以設定和Session相關的其他資訊,以及知道是否是OneWay的消息。
AbstractFeature
為了簡化配置Interceptor的複雜操作,在這裡設定了AbstractFeature,通過Feature我們可以向Bus,Client,Endpoint配置不同功能的Interceptor組。這樣可以極大減輕我們配置檔案的體積。
在此之前我們如果想把一組Log Interceptors添加到Bus中,需要寫的配置檔案如下
<jaxws:endpoint id="greeter" address="http://localhost:8080/hello" implementor="ws.HelloWorldImpl">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor">
</jaxws:outInterceptors>
<jaxws:inFaultInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
</jaxws:inInterceptors>
<jaxws:outFaultInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></jaxws:outInterceptors>
</jaxws:endpoint>
而一旦使用了Feature,我們的配置檔案就變成了
<jaxws:endpoint
id="greeter"
address="http://localhost:8080/hello"
implementor="ws.HelloWorldImpl">
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature"/>
</jaxws:features>
</jaxws:endpoint>
三、CXF攔截器使用
CXF的使用也比Axis容易很多,可以單獨使用,也可以與Spring完美整合,這裡就通過上一個HelloWorld的例子來說明如何使用攔截器。
比如,要實作一個SOAP消息請求日志的功能,以便能動态監控請求消息的資訊。
之需要在在上文例子的基礎做一點點改動:
服務端
package ws;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
public class HelloWorldServer {
public static void main(String args) {
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
factory.setServiceClass(HelloWorldImpl.class);
factory.setAddress("http://localhost:8080/service/HelloWorld");
factory.getInInterceptors().add(new LoggingInInterceptor());
Server server = factory.create();
server.start();
}
}
用戶端
package client;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import ws.HelloWorld;
public class TestClient {
public static void main(String args) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setAddress("http://localhost:8080/service/HelloWorld");
factory.setServiceClass(HelloWorld.class);
factory.getInInterceptors().add(new LoggingInInterceptor());
HelloWorld helloWorld = (HelloWorld) factory.create();
String msg = helloWorld.sayHello("World");
System.out.println(msg);
}
}
在服務端和用戶端分别加上一個日志攔截器,并将日志攔截器加入到攔截器鍊中。不過這裡的日志攔截器是CXF自己定義好的,直接拿來使用即可。
四、測試
先運作服務端,然後運作用戶端。
1、服務端控制台視窗:
在服務端控制台列印的日志截圖是如下
圖檔看不清楚?請點選這裡檢視原圖(大圖)。
實際的日志内容如下:
資訊: Inbound Message
----------------------------
ID: 1
Address: /service/HelloWorld
Encoding: UTF-8
Content-Type: text/xml; charset=UTF-8
Headers: {Content-Length=[179], Host=[localhost:8080], User-Agent=[Apache CXF 2.2.2], connection=[keep-alive], SOAPAction=[""], Pragma=[no-cache], Content-Type=[text/xml; charset=UTF-8], content-type=[text/xml; charset=UTF-8], Cache-Control=[no-cache], Accept=[*/*]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHello xmlns:ns2="http://ws/"><arg0>World</arg0></ns2:sayHello></soap:Body></soap:Envelope>
--------------------------------------
分析日志可以看出,服務端日志輸出了消息id、位址、編碼、以及消息内容。
2、用戶端控制台視窗:
在用戶端控制台列印的日志截圖是如下
圖檔看不清楚?請點選這裡檢視原圖(大圖)。
實際的日志内容如下:
資訊: Inbound Message
----------------------------
ID: 1
Encoding: UTF-8
Content-Type: text/xml; charset=utf-8
Headers: {Content-Length=[206], Server=[Jetty(6.1.18)], content-type=[text/xml; charset=utf-8]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHelloResponse xmlns:ns2="http://ws/"><return>Hello World!</return></ns2:sayHelloResponse></soap:Body></soap:Envelope>
--------------------------------------
分析日志可以看出,用戶端日志輸出了消息id、位址、編碼、以及消息内容,這裡的消息是請求後得到的響應消息。
五、定義自己的Interceptor
自定的Interceptor一定要實作CXF的Interceptor接口,這個接口中有兩個方法:
void handleFault(T message)
當攔截消息處理失敗時候所調用的方法。
void handleMessage(T message)
攔截了一個消息,并做處理的方法。
對于SOAP這種XML格式的消息,開發者處理太過于麻煩,并且CXF提供大量的已經實作好的攔截器可供使用,隻要靈活運用這些攔截器就可以滿足大部分開發的要求。
參考文檔:
http://cwiki.apache.org/CXF20DOC/index.html
網際網路的API翻譯。
出處:http://lavasoft.blog.51cto.com/62575/167288