在學習Spring框架的時候,有一個重要的思想就是AOP,面向切面編程,利用AOP的思想結合Spring的一些API可以實現核心業務與輔助業務的分離,即可以在執行核心業務時,將一些輔助的業務加進來,而輔助業務(如日志,權限控制等)一般是一些公共業務,這樣就實現了兩者的分離,使得核心業務的代碼更加純粹,而且輔助業務也能得到復用,這一篇筆記是當時學習spring的時候寫的,使用springAPI以及自定義類 實現AOP的一個例子 ,.AOP底層就是通過動態代理來實現的,最近專門學習了一下代理模式,反射機制,(動態代理也是基於反射機制來實現的)對代理的理解又立體了一點點,下面通過例子來講解一下代理.
為什么需要代理:
打一個最簡單的比方,我現在想要學習,那么就必須得把書包拿過來,把書掏出來,准備好紙筆,然后開始學習,等學完了我還得收拾書,把書塞回書包里,還得整理一下書包,這是一個完整的學習的過程,但是我很懶,不想動彈,只想學習,那可能就得讓媽媽幫我把書包拿過來,把書打開,我只管學習就好了,學完以后,媽媽再幫我把書整理好放回去.(我這是打個什么破比方...),在這里,媽媽就代表了一個代理對象,要學習的人是我,而我只管學習,這樣效率才最高,至於其他的交給代理對象(媽媽)做就好了,畫一個丑陋的的圖表示一下:
再聯想我們最開始接觸jdbc操作數據庫的時候,業務層每一個方法,都需要1.打開數據庫連接,2.執行我們想要的操作3.關閉數據庫連接.這樣就使得業務層代碼不夠純粹,我的功能是查詢用戶數據,打開和關閉數據庫連接關我毛事?我干嘛要去干這件事?這就是傳統開發中存在的一個問題,現在通過代碼演示一下:
/** * 簡單業務層接口,只有一個save方法 */
interface UserService{ public void saveUser(); }
//////////////////////////////////////////////////////// /** * 業務層實現類,實現save方法 */
class UserServiceImpl implements UserService{ @Override public void saveUser() { System.out.println("1:打開數據庫連接"); System.out.println("2:保存用戶信息"); System.out.println("3:關閉數據庫連接"); } }
我們可以看到其實這個方法的實現是有問題的,核心業務與輔助業務寫在了一個方法中,不但業務冗余了不說,像開關數據庫連接這樣的公共操作也大量的重復,這時候就出現了代理模式的思想,我們可以使用代理模式的思想改寫一下上面的代碼:
package com.wang.proxy; /** * 簡單業務層接口,只有一個save方法 */
interface UserService{ public void saveUser(); } /** * 代理類 */
class UserServiceProxy implements UserService{ private UserService userService; public UserServiceProxy(UserService userService) { super(); this.userService = userService; } public void open(){ System.out.println("1:打開數據庫連接"); } public void close(){ System.out.println("3:關閉數據庫連接"); } @Override public void saveUser() { this.open(); userService.saveUser(); this.close(); } } /** * 業務層實現類,實現save方法 */
class UserServiceImpl implements UserService{ @Override public void saveUser() { System.out.println("2:保存用戶信息"); } } /** * 測試類 */
public class TestProxy { public static void main(String[] args) { UserService userService =new UserServiceProxy(new UserServiceImpl()); userService.saveUser(); } }
通過測試代碼打印結果,和上面沒有使用代理的代碼是完全一樣,但是通過修改可以清晰地看到,業務層代碼變得很純很純的,只剩下最核心的保存用戶信息的代碼.通過代理模式,我們可以抽取出核心業務與輔助業務,但是問題隨之而來了,我這里編寫的UserServiceProxy是挺不錯,可是它只能服務與UserService這個接口的對象啊,如果我有一千個業務,那豈不是要編寫一千個代理類,畢竟一千個人心中就有一千個哈姆雷特啊,其實這種代理模式就是靜態代理,它的缺點很明顯,靜態代理只能服務於一種類型的對象,不利於業務的擴展,那么我們就想了,能不能設計一個代理類可以服務於所有的業務對象呢?於是,這時候,動態代理就閃亮登場了.
如果要實現動態代理,那么你要編寫的那個代理類就需要實現一個InvocationHandle接口.這個接口所在位置是java.lang.reflect.InvocationHandler.看到reflect我們就能知道,動態代理肯定是通過反射來實現的了,這個接口中有一個方法:
Object invoke(Object proxy, Method method, Object[] args) :在代理實例上處理方法調用並返回結果。
invoke方法其實是反射里邊的一個方法,在這個方法中有三個參數:
Ojbect proxy:表示需要代理的對象
Method method:表示要操作的方法
Object[] args:method方法所需要傳入的參數(可能沒有為,null.也可能有多個)
如果要想讓代理設計真正可用,我們還必須有一個代理類對象產生,這有用到了另一個類:java.lang.reflect.Proxy.我的中文jdk文檔對他的描述是:
Proxy
提供用於創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。
在這個類下面,我們找到了這樣一個方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
該方法返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序.方法中有三個參數:
參數:
loader
- 定義代理類的類加載器
interfaces
- 代理類要實現的接口列表
h
- 指派方法調用的調用處理程序
返回:
一個帶有代理類的指定調用處理程序的代理實例,它由指定的類加載器定義,並實現指定的接口
ok,有了這些知識,我們就可以來編寫一個萬能的動態代理類了.
class ServiceProxy implements InvocationHandler { private Object target=null;//保存真實業務對象
/** * 返回動態代理類的對象,這樣用戶才可以利用代理類對象去操作真實對象 * @param obj 包含有真實業務實現的對象 * @return 返回代理對象 */
public Object getProxy(Object obj) { this.target=obj;//保存真實業務對象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj .getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=method.invoke(target, args);//通過反射調用真實業務對象的業務方法,並且返回
return result; } }
現在來測試一下好不好使:
/** * 測試類 */
public class TestProxy { public static void main(String[] args) { UserService service=(UserService) new ServiceProxy().getProxy(new UserServiceImpl()); service.saveUser(); } } //打印結果: // 2.保存用戶信息
看!!!!完美,你說什么?為什么沒有打開數據庫連接,和關閉數據庫連接呢? 簡單,我們直接在ServiceProxy中加入兩個方法,open()和close(),一個放到method.invoke()上面,一個放到下面,就可以了,注意,我們現在編寫的是一個萬能的動態代理類了,沒有和任何的業務層接口關聯,所以接下來你就可以為所欲為了.
下面來總結一下:
動態代理和靜態代理相比較,最大的好處就是接口中聲明的所有的方法都被轉移到一個集中的方法中去處理,就是invocke()方法.這樣在接口中聲明的方法比較多的情況下我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。
動態代理只能代理接口,代理類都需要實現InvocationHandler類,實現invoke方法。該invoke方法就是調用被代理接口的所有方法時需要調用的,該invoke方法的返回值是被代理接口的一個實現類。
那么有沒有可能我們可以不依賴接口呢?這時候就需要CGLIB實現動態代理了,這個jar包可以讓我們擺脫接口的煩惱,感興趣的自己去查一下吧,反正我也沒用,不會...