JAVA反射機制的應用(2)
Spring中的AOP(面向切面的編程)
在MVC中,業務層對象扮演了相當重要的作用,它的方法代表了核心業務邏輯,但是可能還有一些附加的操作,比如寫日誌等其它操作也會包含在其中,那麼可能會帶來幾個問題:
- 冗餘代碼的出現
- 破壞了面向對象的思想。各個方法應該各司其職,只做它應該做的那一部分工作,而這裏這些輔助操作的引入會破壞這個特性。
這裏我們把這些附加操作(比如寫日誌操作)看成一個切面,並且把它從業務方法中抽離出來,然後把它植入到各個業務方法中,這樣維護這些輔助操作是相當容易了,而且也不會出現冗餘代碼。那麼這種思想怎麼實現呢?
這裏把上面提到的業務對象稱爲目標對象,再引入一個委託對象(下面會講到兩種委託對象),我們不直接對目標對象進行操作,而是對委託對象進行方法調用,在委託對象的方法中可以完成切面操作,接着再把方法調用轉交到相應的目標對象上。
AOP的兩種實現方式:
(1)CgLib 運行時代碼增強工具,它是一個第三方的工具,運行時使用cglib可以
動態地給一個類生成一個子類
,並使用子類對象作爲父類對象的委託。
CGLIB包對代理那些沒有實現接口的類非常有用。
它是通過動態的生成一個子類去覆蓋所要代理類的不是final的方法,並設置好callback
,setCallback的參數是一個MethodInterceptor接口的實現類實例,則原有類的每個方法調用就會轉變成調用用戶在
MethodInterceptor
自定義的攔截方法(intercept)。
在這個攔截方法中,我們可以在intercept方法中首先處理我們要輔助的邏輯,完了之後再使用
proxy.invokeSuper(o, os)來調用父類中的相應
方法來處理
。
步驟:首先需要一個cglib的jar包,並配置到classPath中,cglib有一個核心接口:MethodInterceptor接口,我們需要創建類來實現這個接口,並實現其中的方法:
public Object intercept(Object o, Method m, Object[] os, MethodProxy proxy)
該方法中我們可以定義一些特殊的邏輯,比如寫日誌等操作,這些操作完了之後再調用父類中的相關方法。
除此之外,在該類中還要創建一個Enhancer對象,參考以下內容 :
用Enhancer生成一個原有類的子類,並且設置好callback , 則
原有類的每個方法調用都會轉成調用實現了MethodInterceptor接口的proxy的intercept() 函數
:
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
在intercept()函數裏,你可以執行Object
result=proxy.invokeSuper(o,args)來執行原有函數,在執行前後加入自己的東西,改變它的參數,也可以瞞天過海,完全乾別
的。說白了,就是AOP中的around advice。
public class TestCglibProxy { public static void main(String[] args) { AProxy ap=new AProxy(); A a=(A)ap.getProxy(A.class); //利用cglib創建A類的子類對象(代理對象) a.method1(); //調用父對象的method1方法時會會調用代理對象的intercept方法 a.method2(); } } class A{ public void method1(){ System.out.println("A method1"); } public void method2(){ System.out.println("A method2"); } } class AProxy implements MethodInterceptor{ private Logger log=Logger.getLogger("A"); private Enhancer en=new Enhancer(); public Object getProxy(Class c){ //c是所要代理的父類的Class對象 en.setCallback(this); //表示調用父對象的方法時,回調當前對象的intercept方法 en.setSuperclass(c); //設置父類對象 return en.create(); //創建一個代理類的實例 } public Object intercept(Object o, Method m, Object[] os, MethodProxy proxy) throws Throwable { log.info("invoke method "+m.getName()); Object result=proxy.invokeSuper(o, os); return result; } }
(2)動態代理方式
目標類T和代理類P都實現同樣一個接口I , 其中 P類對象中包含了一個T類的對象 ,在P和T實現接口I的同一個方法M時,可以在P中的M中調用T的M 方法 , 同時在調用T的M之前或者之後進行一些自定義的操作。
一個簡單的代理模式實現
Interface I{
void method1();
}
class T implements I{
public void method() {
...
}
}
class P implements I {
private T t;
public P(T t) {
this.t = t;
}
public void methid() {
//do something
t.method();
//do something
}
}
上面的代碼相信大家都能看懂 ,這就是一個代理模式的簡單實現。
下面來介紹一下JAVA中已有的動態代理框架,三個關鍵類:Proxy,InvocationHandler,Method對象,首先我們需要創建目標類動態代理對象,怎麼創建呢?調用:
newProxyInstance ( ClassLoader loader, Class <?>[] interfaces, InvocationHandler h)
前面講過了代理類和目標類實現共同的接口,因此需要將目標類所實現的接口(這裏是一個Class數組)傳給Proxy,除此之外還需要傳 遞目標類的類加載器和InvocationHandler實例,類加載器當然是用來加載生成的代理類的class文件,而這裏的 InvocationHandler是什麼呢?顧名思義,調用處理器,當對代理類調用相應的方法時(這裏的方法其實是我們想對目標類調用的),該方法調用 會被攔截,並回調InvocationHandler實現類的invoke方法,我們來簡單看看invoke方法:
invoke
(
Object
proxy,
Method
method,
Object
[] args)
invoke方法中又包含了三個參數,第一個是代理對象proxy,第二個是Method對象,第三個是一個Object數組,這裏簡單介紹下這三個參 數:proxy對象大家應該都知道了,即代理對象 , 上面利用Proxy的靜態newProxyInstance方法創建的 ;method對象,即方法對象,這裏的方法對象是什麼呢?剛纔說到了對代理對象調用方法時會被攔截,而這裏的method就是被攔截的方法對象 ; 最後一個是args,相信大家對這個變量不會陌生,這裏表示的是對代理對象調用方法時所傳遞的參數,參數可能爲一個,兩個或者多個,當然也可能沒有,因此這裏用一個數組表示。
肯定有人會問我在這個方法裏寫什麼呢?愛寫什麼你寫什麼,呵呵,開個玩笑。 按照常理來說,這裏面肯定會調用目標對象的method方法了,不然這代理還有什麼意義!!!那怎麼調用呢?相信有java反射基礎的人大家應該都知道吧,不知道或者不熟練就查查API:
invoke ( Object obj, Object ... args)
第 一個對象是目標對象,第二個是參數數組,這裏又涉及問題,怎麼把目標對象傳遞到這兒呢?我這裏介紹兩種方法,其實大家應該都能想到,在定義 InvocationHandler實現類時,把目標類作爲它的成員變量,通過構造函數把目標對象賦值給它,那麼在invoke方法中當然就可以直接使用 了;另外一種更簡單的方法,InvocationHandler的實例使用匿名內部類創建:
new InvocationHandler() {
public
Object invoke(Object proxy, Method method, Object[] args)
{
...
method,invoke(target, args);
...
}
}
這裏的target可以來自方法參數,但一定要是final的。
public class TestProxy { public static void main(String[] args){ /* I target=new Target(); I proxy=(I)ProxyFactory.getProxy(target); proxy.method(); */ List list=new ArrayList(); List listProxy=(List)ProxyFactory.getProxy(list); listProxy.add("abc"); listProxy.add("def"); listProxy.add(0,"123"); listProxy.remove(1); listProxy.size(); for(Object o:list){ System.out.println(o); } } } interface I{ void method(); } class Target implements I{ public void method(){ System.out.println("Target method"); } } class TargetProxy implements I{ I target; public TargetProxy(I target){ this.target=target; } public void method(){ System.out.println("do sth in proxy"); target.method(); } } class ProxyFactory{ public static Object getProxy(final Object target){ Class c=target.getClass(); ClassLoader loader=c.getClassLoader(); Class[] is=c.getInterfaces(); return Proxy.newProxyInstance(loader,is,new InvocationHandler(){ public Object invoke(Object proxy, Method m, Object[] os) throws Throwable { System.out.println("invoke "+m.getName()); return m.invoke(target, os); } }); } }
以上內容部分來自:http://nucchenyibin.iteye.com/blog/667733
http://www.zx2010.com/program/java-cglib-proxy.asp