標籤:des 修改 事件處理 cto 結果 htm 增強 分享 高效能
目錄
代理模式
1.1.靜態代理
1.2.動態代理
1.3.Cglib代理
代理模式
代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象.這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴充目標對象的功能.
這裡使用到編程中的一個思想:不要隨意去修改別人已經寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴充該方法
舉個例子來說明代理的作用:假設我們想邀請一位明星,那麼並不是直接連接明星,而是聯絡明星的經紀人,來達到同樣的目的.明星就是一個目標對象,他只要負責活動中的節目,而其他瑣碎的事情就交給他的代理人(經紀人)來解決.這就是代理思想在現實中的一個例子
用圖表示如下:
代理模式的關鍵點是:代理對象與目標對象.代理對象是對目標對象的擴充,並會調用目標對象
1.1.靜態代理
靜態代理在使用時,需要定義介面或者父類,被代理對象與代理對象一起實現相同的介面或者是繼承相同父類.
下面舉個案例來解釋:
類比儲存動作,定義一個儲存動作的介面:IUserDao.java,然後目標對象實現這個介面的方法UserDao.java,此時如果使用靜態代理方式,就需要在代理對象(UserDaoProxy.java)中也實現IUserDao介面.調用的時候通過調用代理對象的方法來調用目標對象.
需要注意的是,代理對象與目標對象要實現相同的介面,然後通過調用相同的方法來調用目標對象的方法
程式碼範例:
package com.blankjor.proxy;/** * @desc 使用者儲存介面 * @author Blankjor * @date 2017年5月30日 下午4:23:44 */public interface IUserDao { // 儲存方法 void save();}package com.blankjor.proxy;/** * @desc 使用者儲存 * @author Blankjor * @date 2017年5月30日 下午4:24:15 */public class UserDao implements IUserDao { @Override public void save() { System.out.println("OK,已儲存資料!"); }}package com.blankjor.proxy;/** * @desc 代理對象,靜態代理 * @author Blankjor * @date 2017年5月30日 下午4:25:34 */public class UserDaoProxy implements IUserDao { private IUserDao target; public UserDaoProxy(IUserDao user) { this.target = user; } @Override public void save() { System.out.println("開始事物."); target.save(); System.out.println("提交事物."); }}package com.blankjor.proxy;/** * @desc 靜態代理測試方法 * @author Blankjor * @date 2017年5月30日 下午4:27:48 */public class MainTest { public static void main(String[] args) { // 目標對象 IUserDao userDao = new UserDao(); // 代理對象,把目標對象傳給代理,建立代理關係 UserDaoProxy proxy = new UserDaoProxy(userDao); proxy.save(); }}
執行結果:
靜態代理總結:
1.可以做到在不修改目標對象的功能前提下,對目標功能擴充.
2.缺點:
- 因為代理對象需要與目標對象實現一樣的介面,所以會有很多代理類,類太多.同時,一旦介面增加方法,目標對象與代理對象都要維護.
如何解決靜態代理中的缺點呢?答案是可以使用動態代理方式
1.2.動態代理
動態代理有以下特點:
1.代理對象,不需要實現介面
2.代理對象的產生,是利用JDK的API,動態在記憶體中構建代理對象(需要我們指定建立代理對象/目標對象實現的介面的類型)
3.動態代理也叫做:JDK代理,介面代理
JDK中組建代理程式對象的API
代理類所在包:java.lang.reflect.Proxy
JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數,完整的寫法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意該方法是在Proxy類中是靜態方法,且接收的三個參數依次為:
ClassLoader loader,:指定當前目標對象使用類載入器,擷取載入器的方法是固定的
Class<?>[] interfaces,:目標對象實現的介面的類型,使用泛型方式確認類型
InvocationHandler h:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法作為參數傳入
程式碼範例:
介面類IUserDao.java以及介面實作類別,目標對象UserDao是一樣的,沒有做修改.在這個基礎上,增加一個代理工廠類(ProxyFactory.java),將代理類寫在這個地方,然後在測試類別(需要使用到代理的代碼)中先建立目標對象和代理對象的聯絡,然後代用代理對象的中同名方法
代理工廠類:ProxyFactory.java
package com.blankjor.proxy1;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * @desc 動態代理工廠 * @author Blankjor * @date 2017年5月30日 下午4:32:49 */public class ProxyFactory { private Object target; /** * 維護一個目標對象 * * @param target */ public ProxyFactory(Object target) { this.target = target; } public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開始事物2."); // 執行目標方法 Object returnValue = method.invoke(target, args); System.out.println("提交事物2."); return returnValue; } }); }}package com.blankjor.proxy1;/** * @desc 動態代理測試方法 * @author Blankjor * @date 2017年5月30日 下午4:27:48 */public class MainTest { public static void main(String[] args) { // 目標對象 IUserDao userDao = new UserDao(); // 原始類型 System.out.println(userDao.getClass()); // 給目標對象,建立代理對象 IUserDao proxy = (IUserDao) new ProxyFactory(userDao).getProxyInstance(); // 記憶體中動態產生的代理對象 System.out.println(proxy.getClass()); // 執行方法 proxy.save(); }}
運行結果:
總結:
代理對象不需要實現介面,但是目標對象一定要實現介面,否則不能用動態代理
1.3.Cglib代理
上面的靜態代理和動態代理模式都是要求目標對象是實現一個介面的目標對象,但是有時候目標對象只是一個單獨的對象,並沒有實現任何的介面,這個時候就可以使用以目標對象子類的方式類實現代理,這種方法就叫做:Cglib代理
Cglib代理,也叫作子類代理,它是在記憶體中構建一個子類對象從而實現對目標對象功能的擴充.
- JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個介面,如果想代理沒有實現介面的類,就可以使用Cglib實現.
- Cglib是一個強大的高效能的代碼產生包,它可以在運行期擴充java類與實現java介面.它廣泛的被許多AOP的架構使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
- Cglib包的底層是通過使用一個小而塊的位元組碼處理架構ASM來轉換位元組碼並產生新的類.不鼓勵直接使用ASM,因為它要求你必須對JVM內部結構包括class檔案的格式和指令集都很熟悉.
Cglib子類代理實現方法:
1.需要引入cglib的jar檔案,但是Spring的核心包中已經包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包後,就可以在記憶體中動態構建子類
3.代理的類不能為final,否則報錯
4.目標對象的方法如果為final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.
程式碼範例:
/** * 目標對象,沒有實現任何介面 */public class UserDao { public void save() { System.out.println("----已經儲存資料!----"); }}/** * Cglib子類代理工廠 * 對UserDao在記憶體中動態構建一個子類對象 */public class ProxyFactory implements MethodInterceptor{ //維護目標對象 private Object target; public ProxyFactory(Object target) { this.target = target; } //給目標對象建立一個代理對象 public Object getProxyInstance(){ //1.工具類 Enhancer en = new Enhancer(); //2.設定父類 en.setSuperclass(target.getClass()); //3.設定回呼函數 en.setCallback(this); //4.建立子類(代理對象) return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開始事務..."); //執行目標對象的方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務..."); return returnValue; }}/** * 測試類別 */public class App { @Test public void test(){ //目標對象 UserDao target = new UserDao(); //代理對象 UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); //執行代理對象的方法 proxy.save(); }}
總結:
動態代理與靜態代理相比較,最大的好處是介面中聲明的所有方法都被轉移到調用處理器一個集中的方法中處理。在介面方法數量比較多的時候,我們可以進行靈活處理,而不需要像靜態代理那樣對每一個方法或方法組合進行處理。Proxy 很美很強大,但是僅支援 interface 代理。Java 的單繼承機制註定了這些動態代理類們無法實現對 class 的動態代理。好在有cglib為Proxy提供了彌補。class與interface的區別本來就模糊,在java8中更是增加了一些新特性,使得interface越來越接近class,當有一日,java突破了單繼承的限制,動態代理將會更加強大。
參考:http://www.cnblogs.com/cenyu/p/6289209.html
http://blog.csdn.net/goskalrie/article/details/52458773
Java設計模式の代理模式