日常工作中經常會接觸到代理模式,但一直沒有對其進行深究。代理模式一直就像一團迷霧一樣存在我心里,什么是代理模式?為什么要使用代理?代理模式有哪些實現?它的底層機制是怎樣的?這些問題促使着我迫切想要揭開代理模式的神秘面紗。
1. 什么是代理模式?
日常生活中我們經常會碰到代理模式,例如我們找房產中介幫我們介紹房子,找婚姻中介幫我們介紹對象,找保潔幫我們打理房間,找律師幫我們進行訴訟等。我們在無形中運用到了代理模式,卻不知道它的存在。
2. 為什么要使用代理?
運用代理可以使我們的生活更加便利,有了代理,我們不需要自己去找房子,不需要自己去找對象,不需要自己去打理房間,不需要自己去訴訟。當然,你也可以選擇一切都自己來干,但是存在前提條件,一是你是否都具備這樣的資源和能力來做這些事情,二是你是否願意花費這么多精力和時間來做這些事情。總之,代理模式使我們各專其事,我們可以將時間浪費在美好的事情上,而不用天天被一些瑣事所羈絆。
3. 代理模式有哪些實現?
Java中的代理有靜態代理和動態代理,下面我會分別用一個簡單的例子來介紹一下靜態代理和動態代理代碼實現。
3.1 靜態代理
代理接口:UserDao.java
1 public interface UserDao { 2 3 void save(); 4 5 }
目標對象:UserDaoImpl.java
1 public class UserDaoImpl implements UserDao { 2 3 @Override 4 public void save() { 5 System.out.println("正在保存用戶..."); 6 } 7 8 }
代理對象:TransactionHandler.java
1 public class TransactionHandler implements UserDao { 2 3 //目標代理對象 4 private UserDaoImpl target; 5 6 //構造代理對象時傳入目標對象 7 public TransactionHandler(UserDaoImpl target) { 8 this.target = target; 9 } 10 11 @Override 12 public void save() { 13 //調用目標方法前的處理 14 System.out.println("開啟事務控制..."); 15 //調用目標對象的方法 16 target.save(); 17 //調用目標方法后的處理 18 System.out.println("關閉事務控制..."); 19 } 20 21 }
測試類:Main.java
1 public class Main { 2 3 public static void main(String[] args) { 4 5 //新建目標對象 6 UserDaoImpl target = new UserDaoImpl(); 7 8 //創建代理對象, 並使用接口對其進行引用 9 UserDao userDao = new TransactionHandler(target); 10 11 //針對接口進行調用 12 userDao.save(); 13 14 } 15 16 }
測試結果:
總結:
總的來說靜態代理實現簡單也容易理解,但是靜態代理不能使一個代理類反復作用於多個目標對象,代理對象直接持有目標對象的引用,這導致代理對象和目標對象類型緊密耦合了在一起。如果UserDao接口下還有另一個實現類也需要進行事務控制,那么就要重新寫一個代理類,這樣就會產生許多重復的模版代碼,不能達到代碼復用的目的。而動態代理就可以很好的解決這樣的問題。
3.2 動態代理
代理接口:UserDao.java
1 public interface UserDao { 2 3 void save(); 4 5 }
目標對象:UserDaoImpl.java
1 public class UserDaoImpl implements UserDao { 2 3 @Override 4 public void save() { 5 System.out.println("保存用戶信息..."); 6 } 7 8 }
代理對象:TransactionHandler.java
1 public class TransactionHandler implements InvocationHandler { 2 3 //需要代理的目標對象 4 //這里設計為可以為任意對象添加事務控制, 所以將目標對象聲明為Object 5 private Object target; 6 7 //構造TransactionHandler時傳入目標對象 8 public TransactionHandler(Object target) { 9 this.target = target; 10 } 11 12 @Override 13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 14 //調用目標方法前的處理 15 System.out.println("開啟事務控制..."); 16 //調用目標對象的方法 17 Object result = method.invoke(target, args); 18 //調用目標方法后的處理 19 System.out.println("關閉事務控制..."); 20 //放回方法調用結果 21 return result; 22 } 23 24 }
測試類:Main.java
1 public class Main { 2 3 public static void main(String[] args) { 4 5 //新建目標對象 6 Object target = new UserDaoImpl(); 7 8 //創建事務處理器 9 TransactionHandler handler = new TransactionHandler(target); 10 11 //生成代理類並使用接口對其進行引用 12 UserDao userDao = (UserDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), 13 target.getClass().getInterfaces(), 14 handler); 15 //針對接口進行方法調用 16 userDao.save(); 17 18 } 19 20 }
測試結果:
總結:
之前我們發現了靜態代理會產生許多重復代碼,不能很好的進行代碼復用,而動態代理能夠很好的解決這個問題,代理類TransactionHandler實現了InvocationHandler接口,並且它持有的目標對象類型是Object,因此事務控制代理類TransactionHandler能夠代理任意的對象,為任意的對象添加事務控制的邏輯。因此動態代理才真正的將代碼中橫向切面的邏輯剝離了出來,起到代碼復用的目的。但是動態代理也有缺點,一是它的實現比靜態代理更加復雜也不好理解;二是它存在一定的限制,例如它要求需要代理的對象必須實現了某個接口;三是它不夠靈活,動態代理會為接口中的聲明的所有方法添加上相同的代理邏輯。當然,這只是JDK動態代理所存在的一些缺陷,動態代理還有另外的實現如使用CGLIB庫,在本文不做介紹,讀者可以自行去了解。
全文總結:
本文從概念上為大家介紹了什么是代理模式,為什么要使用代理以及代理模式有哪些實現,並使用簡單的例子為大家介紹靜態代理和JDK動態代理的實現,分析了靜態代理和動態代理各自的優缺點,使大家對代理模式有了一些大致的了解。不過到這里相信讀者對於JDK動態代理還是會感到困惑,想要進一步了解代理類是怎樣產生的。后續章節筆者會深入源碼為大家呈現整個代理類的產生過程。