【java項目實戰】代理模式(Proxy Pattern),靜態代理 VS 動態代理
這篇博文,我們主要以類圖和代碼的形式來對照學習一下靜態代理和動態代理。重點解析各自的優缺點。
定義
代理模式(Proxy Pattern)是對象的結構型模式,代理模式給某一個對象提供了一個代理對象,並由代理對象控制對原對象的引用。
代理模式不會改變原來的接口和行為,僅僅是轉由代理幹某件事,代理能夠控制原來的目標,比如:代理商,代理商僅僅會買東西,但並不會改變行為。不會制造東西。
讓我們通過以下的代碼好好理解一下這句話。
分類
靜態代理和動態代理
靜態代理
靜態代理類圖
代碼演示樣例
接口
package com.liang.pattern; public interface UserManager { public void addUser(String userId,String userName); public void delUser(String userId); public void modifyUser(String userId,String userName); public String findUser(String userId); }
目標對象
package com.liang.pattern; public class UserManagerImpl implements UserManager { public void addUser(String userId, String userName) { try{ System.out.println("UserManagerImpl.addUser() userId-->>" + userId); }catch(Exception e){ e.printStackTrace(); throw new RuntimeException(); } } public void delUser(String userId) { System.out.println("UserManagerImpl.delUser() userId-->>" + userId); } public String findUser(String userId) { System.out.println("UserManagerImpl.findUser() userId-->>" + userId); return "於亮"; } public void modifyUser(String userId, String userName) { System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId); } }
代理類,我們使用代理對象做一些日誌記錄,我們將簡略的打印信息到控制臺。
package com.liang.pattern; public class UserManagerImplProxy implements UserManager { private UserManager userManager; public UserManagerImplProxy(UserManager userManager){ this.userManager = userManager; } public void addUser(String userId, String userName) { //記錄日誌等操作或打印輸入參數 System.out.println("start-->>addUser() userId-->>" + userId); try{ userManager.addUser(userId, userName); //運行成功。打印成功信息 System.out.println("success-->>addUser()"); }catch(Exception e){ e.printStackTrace(); //失敗時。打印失敗信息 System.out.println("error-->>addUser()"); //throw new RuntimeException(); } } public void delUser(String userId) { //同上,略 userManager.delUser(userId); } public String findUser(String userId) { //同上。略 userManager.findUser(userId); return null; } public void modifyUser(String userId, String userName) { //同上。略 userManager.modifyUser(userId, userName); } }
client調用
package com.liang.pattern; public class Client { /** * @param args */ public static void main(String[] args) { UserManager userManager = new UserManagerImplProxy(new UserManagerImpl()); userManager.addUser("001","於亮"); } }
輸出結果,此方法運行成功
start-->>addUser() userId-->>001 UserManagerImpl.addUser() userId-->>001 success-->>addUser()
從類圖我們能夠看出。client本來能夠直接和目標對象打交道,代理中間加了一個間接層。他們實現的功能是一樣的,也沒有改變參數。相信大家對上面的類圖和代碼非常熟悉。跟我們平時看別人的博文一樣。沒有不論什麽差別。以下我們看一下靜態代理的優缺點。
優缺點
長處:
1、直觀感受,靜態代理是實實在在的存在的,我們自己寫的。
2、在編譯期增加,提前就指定好了誰調用誰,效率高。
缺點:
相同,它的長處也成了它致命的缺點。
1、靜態代理非常麻煩。須要大量的代理類
當我們有多個目標對象須要代理時,我就須要建立多個代理類。改變原有的代碼,改的多了就非常有可能出問題,必須要又一次測試。
2、反復的代碼會出如今各個角落裏,違背了一個原則:反復不是好味道
我們應該杜絕一次次的反復。
3、在編譯期增加,系統的靈活性差
我們能夠看到代理類的每一個方法中,都有記錄日誌,運行成功或失敗的代碼,每一個方法都反復了一遍,假設我們須要改動的話,並沒有比不用靜態代理時降低改動的地方。僅僅是不用改動目標類。動態代理非常好的為我們攻克了這個問題。以下我們看一下動態代理。
動態代理
動態代理類圖
代碼演示樣例
代理類(不明確,就看看凝視吧)
package com.liang.pattern; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 採用JDK動態代理必須實現InvocationHandler接口。採用Proxy類創建對應的代理類 * @author liang * */ public class ProxyHandler implements InvocationHandler { private Object targetObject; /** * 目標的初始化方法。依據目標生成代理類 * @param targetObject * @return */ public Object newProxyInstance(Object targetObject){ this.targetObject = targetObject; //第一個參數,目標的裝載器 //第二個參數,目標接口,為每一個接口生成代理 //第三個參數,調用實現了InvocationHandler的對象。當你一調用代理,代理就會調用InvocationHandler的invoke方法 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } /** * 反射,這樣你能夠在不知道詳細的類的情況下,依據配置的參數去調用一個類的方法。在靈活編程的時候很實用。 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //記錄日誌等操作或打印輸入參數 System.out.println("start-->>" + method.getName()); for(int i=0;i<args.length;i++){ //打印調用目標方法的參數 System.out.println(args[i]); } Object ret = null; try{ //調用目標方法 ret = method.invoke(targetObject, args); //運行成功。打印成功信息 System.out.println("success-->>" + method.getName()); }catch(Exception e){ e.printStackTrace(); //失敗時,打印失敗信息 System.out.println("error-->>" + method.getName()); throw e; } return ret; } }
client調用
package com.liang.pattern; public class Client { /** * @param args */ public static void main(String[] args) { ProxyHandler proxyHandler = new ProxyHandler(); UserManager userManager = (UserManager)proxyHandler.newProxyInstance(new UserManagerImpl()); String name = userManager.findUser("0001"); System.out.println("client.main-->>" + name); } }
輸出結果,執行成功
start-->>findUser 0001 UserManagerImpl.findUser() userId-->>0001 success-->>findUser client.main-->>於亮
接口和目標類,同上,我就不再浪費大家的帶寬了。
優缺點
長處:
1、一個動態代理類更加簡單了,能夠解決創建多個靜態代理的麻煩,避免不斷的反復多余的代碼
2、調用目標代碼時,會在方法“執行時”動態的增加,決定你是什麽類型,才調誰,靈活
缺點:
1、系統靈活了。可是相比而言,效率減少了,比靜態代理慢一點
2、動態代理比靜態代理在代碼的可讀性上差了一點,不太easy理解
3、JDK動態代理僅僅能對實現了接口的類進行代理
總結
靜態代理VS動態代理,打成了平手,各自有各的獨特之處,均不可取代,在項目中究竟使用哪種代理,沒有最好。僅僅有更合適。
【java項目實戰】代理模式(Proxy Pattern),靜態代理 VS 動態代理