代理模式:html
代理模式是經常使用的java設計模式,他的特徵是代理類與委託類有一樣的接口,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及過後處理消息等。經過代理模式,能夠延遲建立對象,限制訪問某個對象,也就是說,提供一組方法給普通用戶,特別方法給管理員用戶。java
UML圖:git
簡單結構示意圖:程序員
爲了保持行爲的一致性,代理類和委託類一般會實現相同的接口,因此在訪問者看來二者沒有絲毫的區別。github
按照代理的建立時期,代理類能夠分爲兩種:設計模式
靜態代理:dom
爲了幫助理解代理模式,來看一下靜態代理的示例代碼(代碼摘自這裏):ide
Count.java 函數
1 /** 2 * 定義一個帳戶接口 3 * @author Administrator 4 */ 5 public interface Count { 6 // 查看帳戶方法 7 public void queryCount(); 8 // 修改帳戶方法 9 public void updateCount(); 10 }
CountImpl.java 工具
1 /** 2 * 委託類(包含業務邏輯) 3 * @author Administrator 4 */ 5 public class CountImpl implements Count { 6 7 @Override 8 public void queryCount() { 9 System.out.println("查看帳戶方法..."); 10 } 11 12 @Override 13 public void updateCount() { 14 System.out.println("修改帳戶方法..."); 15 } 16 }
CountProxy.java
1 public class CountProxy implements Count { 2 private CountImpl countImpl; 3 /** 4 * 覆蓋默認構造器 5 * @param countImpl 6 */ 7 public CountProxy(CountImpl countImpl) { 8 this.countImpl = countImpl; 9 } 10 11 @Override 12 public void queryCount() { 13 System.out.println("事務處理以前"); 14 // 調用委託類的方法; 15 countImpl.queryCount(); 16 System.out.println("事務處理以後"); 17 } 18 19 @Override 20 public void updateCount() { 21 System.out.println("事務處理以前"); 22 // 調用委託類的方法; 23 countImpl.updateCount(); 24 System.out.println("事務處理以後"); 25 } 26 }
TestCount.java
1 /** 2 *測試Count類 3 * @author Administrator 4 */ 5 public class TestCount { 6 public static void main(String[] args) { 7 CountImpl countImpl = new CountImpl(); 8 CountProxy countProxy = new CountProxy(countImpl); 9 countProxy.updateCount(); 10 countProxy.queryCount(); 11 } 12 }
以上靜態代理的代碼結合前面的結構圖和UML圖,相信不難理解代理模式的基本原理。
JDK動態代理
爲了提升代理的靈活性和可擴展性,減小重複代碼,咱們可使用JDK提供的動態代理。
// InvocationHandlerImpl 實現了 InvocationHandler 接口,並能實現方法調用從代理類到委託類的分派轉發 // 其內部一般包含指向委託類實例的引用,用於真正執行分派轉發過來的方法調用 InvocationHandler handler = new InvocationHandlerImpl(..); // 經過 Proxy 爲包括 Interface 接口在內的一組接口動態建立代理類的類對象 Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 經過反射從生成的類對象得到構造函數對象 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 經過構造函數對象建立動態代理類實例 Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
實際使用過程更加簡單,由於 Proxy 的靜態方法 newProxyInstance 已經爲咱們封裝了步驟 2 到步驟 4 的過程:
// InvocationHandlerImpl 實現了 InvocationHandler 接口,並能實現方法調用從代理類到委託類的分派轉發 InvocationHandler handler = new InvocationHandlerImpl(..); // 經過 Proxy 直接建立動態代理類實例 Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
JAVA示例代碼:
public class TraceHandler implements InvocationHandler{ private Object target = null; public TraceHandler(Object t) { this.target = t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print(target); System.out.print("." + method.getName() + "("); if(args != null) { for(int i = 0; i < args.length; ++i) { System.out.print(args[i]); if(i < args.length-1) System.out.print(", "); } } System.out.println(")"); return method.invoke(target, args); } }
Test.java
public void test() { Object[] elements = new Object[1000]; for (int i = 0; i < elements.length; i++) { Integer val = i+1; TraceHandler handler = new TraceHandler(val); elements[i] = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler); } Integer key = new Random().nextInt(1000) + 1; int result = Arrays.binarySearch(elements, key); if (result > 0) { System.out.println(elements[result]); } }
Proxy 靜態方法生成動態代理類一樣須要經過類裝載器來進行裝載才能使用,它與普通類的惟一區別就是其字節碼是由 JVM 在運行時動態生成的而非預存在於任何一個 .class 文件中。每次生成動態代理類對象時都須要指定一個類裝載器對象。
動態生成的代理類自己的一些特色:
被代理的接口的特色:
異常處理的特色:
基於CGLIB的動態代理
因爲JDK的動態代理依靠接口實現,若是有些類並沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。
CGlib概述:
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現加強,但由於採用的是繼承,因此不能對final修飾的類進行代理。 很少說,直接上代碼!
有一個Manager類:
public class Manager { public void query() { System.out.println("query..."); } public void insert() { System.out.println("insert..."); } public String update() { System.out.println("update...."); return "I'm update"; } public void delete() { System.out.println("delete...."); } }
咱們想要在這個類中的每一個方法前面和後面都打印一句話,這時候咱們就可使用代理了,讓咱們來看一下這個代理能夠怎麼寫:
public class AuthProxy implements MethodInterceptor{ @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before..."); Object result = proxy.invokeSuper(arg0, args); System.out.println("After...."); return result; } }
如上,CGLIB實現的代理類必須實現MethodInterceptor接口,該接口中只有一個方法須要實現,即intercept方法。經過MethodProxy中的invokeSuper便可執行被代理類的方法。咱們繼續往下看。
public static Manager getInstace(AuthProxy auth) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Manager.class); enhancer.setCallback(auth); return (Manager) enhancer.create(); }
經過以上代碼咱們就能獲得一個Manager的代理類,被AuthProxy代理。
public void AuthProxyTest() { AuthProxy auth = new AuthProxy(); Manager manager = ManagerFactory.getInstace(auth); manager.delete(); System.out.println(); manager.query(); System.out.println(); String result = manager.update(); System.out.println("result: " + result); }
下面是一個能夠代理不一樣類的代理生成工廠:
public class ManagerFactory2 { public static Manager getInstace(Class clasz, AuthProxy auth) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clasz); enhancer.setCallback(auth); return (Manager) enhancer.create(); } }
對這個工廠進行通用化擴展:
public class ManagerFactory { public static Manager getInstace(Class clasz, AuthProxyFilter filter, AuthProxy auth, Object...args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clasz); Callback[] callback = new Callback[args.length+1]; System.out.println("length: " + callback.length); callback[0] = auth; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Callback) { callback[i+1] = (Callback) args[i]; }else { callback[i+1] = NoOp.INSTANCE; } } enhancer.setCallbacks(callback); enhancer.setCallbackFilter(filter); return (Manager) enhancer.create(); }
AuthProxyFilter.java
public class AuthProxyFilter implements CallbackFilter{ @Override public int accept(Method method) { if ("query".equals(method.getName())) { return 1; } return 0; } }
=====================華麗的分割線==================================
源碼請猛戳{ 這裏 }
===============================================================
參考資料:
http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
http://www.blogjava.net/stone2083/archive/2008/03/16/186615.html