近幾年以來,AOP(面向方面的編程)得到了廣泛的應用,我們把它應用到例如打印日志、權限控制等各個方面。而在實現AOP的時候,我們一般都借助於工具,如Spring AOP等等。
當然,我們借助於工具一般都用在實現系統級的AOP上,這種實現一般都要借助於配置文檔來實現。
當我們需要實現較小范圍的AOP的時候,比如對有限幾個類的某些方法進行AOP,這時候,我們一般不希望使用工具。原因可能是第一工具不夠靈活第二需要做繁瑣的配置工作。這時候,我們需要自己來實現AOP的方方面面。
Java語言一般來說,有兩種方法來實現自己的AOP技術:一是通過反射技術來實現;二是使用動態代理。這兩種方法在這裡就不多說,但實現起來都比較復雜。
在Groovy語言中,我們可以通過Interceptor(攔截器)來實現AOP技術。使用Interceptor來實現AOP技術,技術難度就小多了,而且更加的靈活。
本系列的文字,正是來談談Interceptor技術的方方面面,從它怎麼樣攔截一個方法起,一直講到怎麼使用它來靈活多變的使用我們自己的AOP。
還是從一個簡單的例子說起吧。比如,我們有如下的一個類:
class HelloWorld {
def hello()
{
println 'hello'
}
}
我們現在要攔截它的"hello"方法。
首先,我們要實現自己的攔截器類,在Groovy語言的編程中,實現自己的攔截器類很簡單,就是要實現Interceptor接口,而該接口只有簡簡單單的三個需要實現的方法。如下所示:
class SampleInterceptor implements Interceptor{
Object beforeInvoke(Object object, String methodName, Object[] arguments)
{
if(methodName == 'hello')
{
println 'before invoke hello'
}
}
boolean doInvoke(){ true }
Object afterInvoke(Object object, String methodName, Object[] arguments,
Object result){
result
}
}
從上面的實例代碼可以看出,我們的攔截器類需要實現三個方法,即"beforeInvoke"方法,用來做調用方法之前的動作;"doInvoke"方法,決定是否調用需要攔截的方法;"afterInvoke"方法,用來做調用方法之後的動作。
上面的實例代碼中,我們攔截了HelloWorld類的"hello"方法,在調用該方法之前打印了"before invoke hello"這樣的字樣。
現在,我們就可以來測試我們的第一個攔截器類了:
def proxy= ProxyMetaClass.getInstance( HelloWorld )
proxy.interceptor= new SampleInterceptor()
proxy.use{
def helloworld= new HelloWorld()
helloworld.hello()
}
攔截器的使用也十分簡單,首先是實例化一個"ProxyMetaClass"類的實例"proxy",然後把我們的攔截器類的實例賦給"proxy"對象的"interceptor"。最後,就是在"proxy"對象的"use"方法裡實例化需要被攔截的類,調用它的方法。
上面代碼的結果為:
before invoke hello
hello
這樣就完成了一個簡單的攔截任務。
我們初步認識了我們自己的一個簡單的攔截器以後,就需要深入的理解攔截器的方方面面了。
首先,我們使用攔截器攔截了一個類的某個方法,就可以實際調用該方法,就像上面的例子一樣,也可以實際上不調用該方法。如下所示:
class SampleInterceptor implements Interceptor{
Object beforeInvoke(Object object, String methodName, Object[] arguments)
{
if(methodName == 'hello')
{
println 'before invoke hello'
}
object
}
boolean doInvoke(){ false }
Object afterInvoke(Object object, String methodName, Object[] arguments,
Object result){
result
}
}
還是上面的攔截器類,為了不調用我們所要攔截的方法,我們做了一點改動。首先,方法"doInvoke"的返回值為"false",它能決定不調用所要攔截的方法。如果不調用所要攔截的方法,那麼"beforeInvoke"方法一定要有返回值,如上面的代碼返回"object"。否則會報空指針的違例。
現在,我們還是使用上面的使用過的代碼來測試它:
def proxy= ProxyMetaClass.getInstance( HelloWorld )
proxy.interceptor= new SampleInterceptor()
proxy.use{
def helloworld= new HelloWorld()
helloworld.hello()
}
運行結果為:
before invoke hello
值得注意的是,在上面的例子中,我們都使用了"beforeInvoke"方法作為例子,它是用來在調用被攔截的方法之前做動作,如果要在調用被攔截的方法之後做動作,就要使用到"afterInvoke",用法兩者也大體一樣。
現在,我們再來看看"beforeInvoke"和"afterInvoke"方法的參數。其中,"object"是被攔截的對象,"methodName"是被攔截的方法名,"arguments"是被攔截方法的參數。下面一並做測試。
首先,我們對上面的"HelloWorld"類做稍微的改動,以便於我們的測試:
class HelloWorld {
def hello(name)
{
println "hello,$name!"
}
}
同樣,我們也需要改動一下我們的攔截器類:
class SampleInterceptor implements Interceptor{
Object beforeInvoke(Object object, String methodName, Object[] arguments)
{
if(methodName == 'hello')
{
println "object: $object"
println "method name: $methodName"
println "method arguments: $arguments"
}
}
boolean doInvoke(){ true } //whether or not to invoke the intercepted
Object afterInvoke(Object object, String methodName, Object[] arguments,
Object result){
result
}
}
最後,我們來測試上面的改動:
def proxy= ProxyMetaClass.getInstance( HelloWorld )
proxy.interceptor= new SampleInterceptor()
proxy.use{
def helloworld= new HelloWorld()
helloworld.hello('world')
}
結果為:
object: mop.interceptor.basic.HelloWorld@f0eed6
method name: hello
method arguments: {"world"}
hello,world!
最後,我們還要看看"afterInvoke"方法的"result"參數,它是被攔截方法的返回值,在上面的例子中,我們都是直接把該參數返回了,表示我們不改變被攔截方法的返回值。說到這裡,我們就想到了,當然,我們可以改變被攔截方法的返回值。如下所示:
class HelloWorld {
def test()
{
'hello'
}
}
我們來修改一下攔截器類:
class SampleInterceptor implements Interceptor{
Object beforeInvoke(Object object, String methodName, Object[] arguments)
{
}
boolean doInvoke(){ true }
Object afterInvoke(Object object, String methodName, Object[] arguments,
Object result){
if(methodName == 'test')
{
println result
result = 'world'
}
result
}
}
最後是測試:
def proxy= ProxyMetaClass.getInstance( HelloWorld )
proxy.interceptor= new SampleInterceptor()
proxy.use{
def helloworld= new HelloWorld()
def result = helloworld.test()
println 'after interceptor, the result: '+result
}
結果為:
hello
after interceptor, the result: world