aop原理學習——動態代理

來源:互聯網
上載者:User

aop:面向切面編程。

我的理解就是:對於多個執行個體對象,把它們碼放在一起,在一個或多個特定位置切上一刀,在這個切面的位置上做一些統一的操作。

aop最常用的情境就是日誌。

一般來說我們希望在某些函數調用前後記錄日誌。最容易想到的辦法如下:package com.red.myaop.bean;<br />public class EasyLog {<br />/**<br /> * @param args<br /> */<br />public static void main(String[] args) {<br />EasyLog easy = new EasyLog();</p><p>System.out.println("do before...");<br />easy.say();<br />System.out.println("do after...");<br />}</p><p>public void say(){</p><p>System.out.println("hello");</p><p>}<br />} 

每次在調用函數的前後加上日誌資訊。

但如果要對所有對象調用say()函數進行日誌,照這樣的方法就會出現如下情況。。。。。<br />System.out.println("do before...");<br />easy.say();<br />System.out.println("do after...");<br />System.out.println("do before...");<br />easy1.say();<br />System.out.println("do after...");<br />System.out.println("do before...");<br />easy2.say();<br />System.out.println("do after...");<br />。。。。。。

這就產生了面向切面的需求,也就是:我要對所有對象在say函數上切一刀,在這個切面上做一些日誌。

現利用java的動態代理方法來實現對函數調用進行代理,可以在代理函數調用中對實際的調用進行日誌。

假設有一個介面聲明了函數say:

package com.red.myaop.bean;<br />public interface ISay {<br />public void say();<br />}

有兩個類分別實現了該介面:

package com.red.myaop.bean;<br />public class Hello implements ISay{<br />@Override<br />public void say() {<br />System.out.println("Hello!");<br />}<br />}

package com.red.myaop.bean;<br />public class Bye implements ISay {<br />@Override<br />public void say() {<br />System.out.println("Bye bye!");<br />}<br />}

我希望這兩個類的執行個體在調用say的前後都能做一下日誌。

可以用到java反射中的一些編程介面。

最核心的的方法為,調用

Object

java.lang.reflect.Proxy
.newProxyInstance(ClassLoader

loader, Class
<?>[]
interfaces, InvocationHandler

h) throws IllegalArgumentException

 

它是一個靜態方法,需要提供一個類載入器,介面和一個實現InvocationHandler的代理執行個體,返回一個對應的代理類。

其中第一個參數表示被代理的對象的類的載入器,這裡指的就是Hello和Bye的類載入器,第二個參數表示被代理類的介面,這裡指的就是ISay。最後一個參數是一個實現InvocationHandler的代理執行個體,他需要實現
Object
invoke(Object

proxy, Method

method, Object
[]
arg2) throws Throwable

其中proxy就是這個代理本身(可以暫時這樣理解,實際有區別)。method就是被代理的對象的當前被調用函數,arg2是這個被調用函數的參數。

 

我要做的事情是這樣的,一般而言產生一個執行個體對象往往用new來實現,比如ISay h=new Hello(); 而當調用h.say()時,函數會直接被調用。現在我希望劫持這個對象,讓的函數調用也可以自訂的進行,此處就是讓它在調用say時前後加上日誌。

怎麼劫持這個對象,就是建立一個該對象的代理,當要調用say方法時委託這個代理對象來完成實際say方法的調用,而代理則可以在實際調用say方法時做一些動作。

實現的方法如下:

package com.red.myaop.proxy;<br />import java.lang.reflect.InvocationHandler;<br />import java.lang.reflect.Method;<br />public class MyProxy implements InvocationHandler {<br />private Object delegate;</p><p>public Object getDelegate() {<br />return delegate;<br />}<br />public void setDelegate(Object delegate) {<br />this.delegate = delegate;<br />}<br />@Override<br />public Object invoke(Object proxy, Method method, Object[] arg2)<br />throws Throwable {</p><p>System.out.println("do before...");<br />Object ret = method.invoke(this.delegate, arg2);<br />System.out.println("do after...");<br />return ret;<br />}<br />}<br />

這是一個函數調用代理類,首先它實現了invoke方法。其次他有一個delegate成員,類型是Object,即MyProxy可以成為任何對象的函數調用代理,也就是說他可以劫持任何類對象,使得被劫持對象的某個函數被調用時,自動調用invoke方法,你可以在這個方法內自訂被劫持對象的方法會被怎麼調用,在此處,當被劫持的任何函數被調用的前後都會列印一些日誌。

package com.red.myaop.proxy;<br />import java.lang.reflect.Proxy;<br />public class ProxyFactory {<br />public static Object getInstance(Class clazz) {<br />MyProxy proxy = new MyProxy();<br />try {<br />proxy.setDelegate(clazz.newInstance());<br />} catch (InstantiationException e) {<br />e.printStackTrace();<br />} catch (IllegalAccessException e) {<br />e.printStackTrace();<br />}<br />return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), proxy);<br />}<br />}

這是一個工廠類,只有一個靜態方法,他接受一個Class類型的參數,返回一個該類的代理對象,即這個類對象已經被劫持,返回的是這個劫持者,但是它的外表看上去沒有任何特別,它的類載入器是和原來一樣,並且它實現的介面也和原來一樣。在getInstance裡,首先根據類來建立執行個體,這就和new的效果一樣,然後用MyProxy的執行個體來做它的函數調用代理,即這個對象的任何函數調用都要經proxy來處理。最後返回用Proxy.newProxyInstance方法產生的代理對象。

測試主函數如下:

package com.red.myaop.bean;<br />import com.red.myaop.proxy.ProxyFactory;<br />public class Tester {<br />public static void main(String[] args) {</p><p>ISay hello = (ISay)ProxyFactory.getInstance(Hello.class);<br />ISay bye= (ISay)ProxyFactory.getInstance(Bye.class);</p><p>hello.say();<br />bye.say();</p><p>}<br />}

可見用ProxyFactory產生的對象和用new產生的對象在調用者看來沒有區別,但實際卻被劫持了,所以運行結果為:

do before...
Hello!
do after...
do before...
Bye bye!
do after...

但是j2se標準介面中只提供了對實現介面的類的動態代理,所以被代理類必須實現某個介面,否則不能對某個函數進行代理。如果要對普通類進行動態代理,這裡不做討論。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.