代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即經過代理對象訪問目標對象.這樣作的好處是:能夠在目標對象實現的基礎上,加強額外的功能操做,即擴展目標對象的功能.
這裏使用到編程中的一個思想:不要隨意去修改別人已經寫好的代碼或者方法,若是需改修改,能夠經過代理的方式來擴展該方法java
舉個例子來講明代理的做用:假設咱們想邀請一位明星,那麼並非直接鏈接明星,而是聯繫明星的經紀人,來達到一樣的目的.明星就是一個目標對象,他只要負責活動中的節目,而其餘瑣碎的事情就交給他的代理人(經紀人)來解決.這就是代理思想在現實中的一個例子編程
用圖表示以下:設計模式
代理模式的關鍵點是:代理對象與目標對象.代理對象是對目標對象的擴展,並會調用目標對象框架
靜態代理在使用時,須要定義接口或者父類,被代理對象與代理對象一塊兒實現相同的接口或者是繼承相同父類.ide
下面舉個案例來解釋:
模擬保存動做,定義一個保存動做的接口:IUserDao.java,而後目標對象實現這個接口的方法UserDao.java,此時若是使用靜態代理方式,就須要在代理對象(UserDaoProxy.java)中也實現IUserDao接口.調用的時候經過調用代理對象的方法來調用目標對象.
須要注意的是,代理對象與目標對象要實現相同的接口,而後經過調用相同的方法來調用目標對象的方法函數
代碼示例:
接口:IUserDao.java工具
/** * 接口 */ public interface IUserDao { void save(); }
目標對象:UserDao.java性能
/** * 接口實現 * 目標對象 */ public class UserDao implements IUserDao { public void save() { System.out.println("----已經保存數據!----"); } }
代理對象:UserDaoProxy.java測試
/** * 代理對象,靜態代理 */ public class UserDaoProxy implements IUserDao{ //接收保存目標對象 private IUserDao target; public UserDaoProxy(IUserDao target){ this.target=target; } public void save() { System.out.println("開始事務..."); target.save();//執行目標對象的方法 System.out.println("提交事務..."); } }
測試類:App.javathis
/** * 測試類 */ public class App { public static void main(String[] args) { //目標對象 UserDao target = new UserDao(); //代理對象,把目標對象傳給代理對象,創建代理關係 UserDaoProxy proxy = new UserDaoProxy(target); proxy.save();//執行的是代理的方法 } }
靜態代理總結:
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
/** * 建立動態代理對象 * 動態代理不須要實現接口,可是須要指定接口類型 */ public class ProxyFactory{ //維護一個目標對象 private Object 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; } } ); } }
測試類:App.java
/** * 測試類 */ public class App { public static void main(String[] args) { // 目標對象 IUserDao target = new UserDao(); // 【原始的類型 class cn.itcast.b_dynamic.UserDao】 System.out.println(target.getClass()); // 給目標對象,建立代理對象 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); // class $Proxy0 內存中動態生成的代理對象 System.out.println(proxy.getClass()); // 執行方法 【代理對象】 proxy.save(); } }
總結:
代理對象不須要實現接口,可是目標對象必定要實現接口,不然不能用動態代理
上面的靜態代理和動態代理模式都是要求目標對象是實現一個接口的目標對象,可是有時候目標對象只是一個單獨的對象,並無實現任何的接口,這個時候就能夠使用以目標對象子類的方式類實現代理,這種方法就叫作:Cglib代理
Cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.
Cglib子類代理實現方法:
1.須要引入cglib的jar文件,可是Spring的核心包中已經包括了Cglib功能,因此直接引入pring-core-3.2.5.jar
便可.
2.引入功能包後,就能夠在內存中動態構建子類
3.代理的類不能爲final,不然報錯
4.目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.
代碼示例:
目標對象類:UserDao.java
/** * 目標對象,沒有實現任何接口 */ public class UserDao { public void save() { System.out.println("----已經保存數據!----"); } }
Cglib代理工廠:ProxyFactory.java
/** * 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(); } }
在Spring的AOP編程中: 若是加入容器的目標對象有實現接口,用JDK代理 若是目標對象沒有實現接口,用Cglib代理