天天看點

cxf攔截器學習

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