代理模式
代理模式是常用的 Java 設計模式,它的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。
代理的分類
-
靜態代理
由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的
.class
文件就已經存在了。 -
動態代理
在程序運行時,運用反射機制動態創建而成。
靜態代理
-
Count.java
package com.yangruihan.dao; /** * 定義一個賬戶接口 */ public interface Count { // 查看賬戶方法 public void queryCount(); // 修改賬戶方法 public void updateCount(); }
-
CountImpl.java
package com.yangruihan.dao.impl; import com.yangruihan.dao.Count; /** * 委托類(包含業務邏輯) */ public class CountImpl implements Count { @Override public void queryCount() { System.out.println("查看用戶..."); } @Override public void updateCount() { System.out.println("修改賬戶..."); } }
-
CountProxy.java
package com.yangruihan.dao.impl; import com.yangruihan.dao.Count; /** * 這是一個代理類(增強 CountImpl 實現類) */ public class CountProxy implements Count { private CountImpl countImpl; /** * 覆蓋默認構造器 */ public CountProxy(CountImpl countImpl) { this.CountImpl = countImpl; } @Override public void queryCount() { System.out.println("事務處理之前..."); // 調用委托類的方法 countImpl.queryCount(); System.out.println("事務處理之后..."); } @Override public void updateCount() { System.out.println("事務處理之前..."); // 調用委托類的方法 countImpl.updateCount(); System.out.println("事務處理之后..."); } }
-
TestCount.java
package com.yangruihan.test; import com.yangruihan.dao.impl.CountImpl; import com.yangruihan.dao.impl.CountProxy; /** * 測試類 */ public class TestCount { public static void main(String[] args) { CountImpl countImpl = new CountImpl(); CountProxy countProxy = new CountProxy(countImpl); countProxy.updateCount(); countProxy.queryCount(); } }
動態代理
觀察上面代碼可以發現每一個代理類只能為一個接口服務,這樣一來程序開發中必然會產生過多的代理類,而且,所有的代理操作除了調用的方法不一樣之外,其他的操作都一樣,則此時肯定是有重復代碼的。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那么此時就必須使用動態代理完成。
-
JDK 動態代理(包含一個類和一個接口):
-
InvocationHandler
接口:public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
其中參數說明:
- Object proxy:指被代理的對象
- Method method:要調用的方法
- Object[] args:方法調用時所需要的參數
可以將
InvocationHandler
接口的子類想象成一個代理的最終操作類,替換掉ProxySubject
。 -
Proxy
類:Proxy
類是專門完成代理的操作類,可以通過此類為一個或多個接口動態地生成實現類,此類提供如下的操作方法:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
其中參數說明:
- ClassLoader loader:類加載器
- Class<?>[] interfaces:得到全部的接口
- InvocationHandler h:得到
InvocationHandler
接口的子類實例
Ps:類加載器:
*在`Proxy`類中的`newProxyInstance()`方法中需要一個`ClassLoader`類的實例,`ClassLoader`實際上對應的是類加載器,在Java中主要有一下三種類加載器:* - *Booststrap ClassLoader:此加載器采用 C++ 編寫,一般開發中是看不到的* - *Extendsion ClassLoader:用來進行擴展類的加載,一般對應的是`jre\lib\ext`目錄中的類* - *AppClassLoader:(默認)加載`classpath`指定的類,是最常用的一種加載器*
與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由 Java 反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為 Java 反射機制可以生成任意類型的動態代理類。
java.lang.reflect
包中的Proxy
類和InvocationHandler
接口提供了生成動態代理類的能力。程序演示:
-
BookFacade.java
package com.yangruihan.dao; public interface BookFacade { public void addBook(); }
-
BookFacadeImpl.java
package com.yangruihan.dao.impl; import com.yangruihan.dao.BookFacade; public class BookFacadeImpl implements BookFacade { @Override public void addBook() { System.out.println("增加圖書方法..."); } }
-
BookFacadeProxy.java
package com.yangruihan.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK 動態代理代理類 */ public class BookFacadeProxy implements InvocationHandler { private Object target; /** * 綁定委托對象并返回一個代理類 */ public Object bind(Object target) { this.target = target; // 取得代理對象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); // 該方法需要綁定接口(這是一個缺陷,cglib 彌補了這一缺陷) } /** * 調用方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("事務開始..."); // 執行方法 result = method.invoke(target, args); System.out.println("事務結束..."); return result; } }
-
TestProxy.java
package com.yangruihan.test; import com.yangruihan.dao.BookFacade; import com.yangruihan.dao.impl.BookFacadeImpl; import com.yangruihan.proxy.BookFacadeProxy; public class TestProxy { public static void main(String[] args) { BookFacadeProxy proxy = new BookFacadeProxy(); BookFacade bookProxy = (BookFacade)proxy.bind(new BookFacadeImpl()); bookProxy.addBook(); } }
-
-
cglib 代理
JDK 的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現 JDK 動態代理,cglib 是針對類來實現代理的,它的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對
final
修飾的類進行代理。程序演示:
-
BookFacade.java
package com.yangruihan.dao; public interface BookFacade { public void addBook(); }
-
BookFacadeImpl.java
package com.yangruihan.dao.impl; /** * 這是沒有實現接口的實現類 */ public class BookFacadeImpl { public void addBook() { System.out.println("增加圖書的普通方法..."); } }
-
BookFacadeProxy.java
package com.yangruihan.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 使用 cglib 動態代理 */ public class BookFacadeCglib implements MethodInterceptor { private Object target; /** * 創建代理對象 */ public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperClass(this.target.getClass()); // 回調方法 enhancer.setCallBack(this); // 創建代理對象 return enhancer.create(); } /** * 回調方法 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("事務開始..."); proxy.invokeSuper(obj, args); System.out.println("事務結束..."); return null; } }
-
TestCglib.java
package com.yangruihan.test; import com.yangruihan.dao.BookFacadeImpl; import com.yangruihan.proxy.BookFacadeCglib; public class TestCglib { public static void main(String[] args) { BookFacadeCglib cglib = new BookFacadeCglib(); BookFacadeImpl bookCglib = (BookFacadeImpl)cglib.getInstance(new BookFacadeImpl()); bookCglib.addBook(); } }
-