本篇的“Interceptor”,主要是來說說攔截器的阻止攔截的問題,這個問題是我們自定義攔截器時經常要遇到的挑戰之一。
與阻止攔截很近的一個意思是不攔截,即我們可以攔截某個方法,但我們不對攔截做任何實質性的動作,即我們簡單的將攔截的動作放行。這是一種被動的不攔截行為。而我們的阻止攔截卻更為主動一些,即不讓攔截器攔截到某個方法。
下面,我們將分別就放行攔截和阻止攔截來舉例說明。
首先,我們還是設計一個需要被攔截的類來:
class Foo {
def foo()
{
println 'foo'
}
def bar()
{
println 'bar'
}
}
假設我們不想攔截“bar”方法,只想攔截“foo”方法,則我們可以這樣設計攔截器:
class NoBarInterceptor implements Interceptor{
Object beforeInvoke(Object object, String methodName, Object[] arguments){
if(methodName == 'foo')
{
println 'interceptor the function: foo'
}
}
boolean doInvoke(){ true }
Object afterInvoke(Object object, String methodName, Object[] arguments,
Object result){
result
}
}
在代碼中,我們只攔截了方法名為“foo”的方法,對其他方法則未做處理,即放行了。我們來寫一點測試代碼:
def proxy= ProxyMetaClass.getInstance( Foo )
proxy.interceptor= new NoBarInterceptor()
proxy.use{
def f= new Foo()
f.foo()
f.bar()
}
運行結果為:
interceptor the function: foo
foo
bar
當然了,如果我們想更加主動的確定對哪些方法放行,則可以這樣設計我們的攔截器:
class NoMethodInterceptor implements Interceptor{
def methods
Object beforeInvoke(Object object, String methodName, Object[] arguments){
if(!(methodName in this.methods))
{
println "before invoking $methodName"
}
null
}
boolean doInvoke(){ true }
Object afterInvoke(Object object, String methodName, Object[] arguments,
Object result){
result
}
}
在上面的代碼中,使用if判斷語句對需要攔截的方法做了限制,使得該攔截器不能攔截我們給定的某些方法。一切的邏輯都很簡單,所以測試代碼在這裡也不再給出了,你們可以自己寫點測試代碼來測試一下。
而阻止攔截的概念則更強一些,即我們強烈的要求對某些方法不做攔截,這樣使得攔截器不去攔截它們。比如我們有如下的一個需要被攔截的類:
class Hello {
def hello(name)
{
"hello,$name"
}
}
我們需要攔截“hello”方法,並在前面打印一些log信息,我們可能會這樣寫這個攔截器:
class StopInterceptor 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 == 'hello')
{
result = new Hello().hello('log')+'\n'+result
}
result
}
}
我們在攔截器裡攔截了“hello”方法,並且在它的返回結果使用“hello”方法添加了一些log記錄。現在我們可以寫代碼來測試它:
def proxy= ProxyMetaClass.getInstance( Hello )
proxy.interceptor= new StopInterceptor()
proxy.use{
def hello = new Hello()
println hello.hello('World')
}
運行結果卻發現了Exception:
Exception in thread "main" java.lang.StackOverflowError
並且是堆棧溢出。這是因為下面的語句:
if(methodName == 'hello')
{
result = new Hello().hello('log')+'\n'+result
}
我們在攔截器裡又調用了“hello”方法,這個“hello”方法又被攔截器攔截,然後又調用“hello”方法,然後又被攔截,…這樣就進入了死循環,導致了堆棧溢出。
那麼,我們該怎麼解決上面的問題呢?當然是希望攔截器裡的“hello”方法不要再被攔截器攔截,解決方法很簡單,就是在不希望被攔截的方法前面加上“&”標識符,如上面的語句可以修改為:
if(methodName == 'hello')
{
result = new Hello().&hello('log')+'\n'+result
}
這樣,我們再運行上面的測試代碼,結果為:
hello,log
hello,World