爲其餘對象提供一種代理以控制對這個對象的訪問。java
按照代理建立的時期來進行分類的話, 能夠分爲兩種:靜態代理、動態代理。靜態代理是由程序員建立或特定工具自動生成源代碼,在對其編譯。在程序員運行以前,代理類.class文件就已經被建立了。動態代理是在程序運行時經過反射機制動態建立的。程序員
靜態代理在使用時,須要定義接口或者父類,被代理對象與代理對象一塊兒實現相同的接口或者是繼承相同父類。緩存
實現工具
這裏假設Tom要買一套房子,而後本身沒有足夠的資金,因而就讓他的父親代理他來給他買下這套房子。性能
買房子接口:測試
public interface BuyHouse { void Buy(); }
Tom類:this
public class Tom implements BuyHouse { public void Buy() { System.out.println("Tom買到房子了..."); } }
Father類:設計
public class Father implements BuyHouse { private Tom tom; public Father(Tom tom){ this.tom = tom; } public void Buy() { System.out.println("Father給Tom買了房子..."); tom.Buy(); } }
靜態代理的優缺點:代理
動態代理日誌
下面咱們使用JDK動態代理的方式來對上面的靜態代理示例進行改寫
買房子接口和Tom類都和上面的同樣
建立代理類JdkProxy:
public class JdkProxy implements InvocationHandler { private Object target; //接收被代理的目標對象對象 public JdkProxy(Object target){ this.target = target; } //生成目標對象的代理對象 public Object getProxyInstance(){ Object instance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return instance; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開始Jdk動態代理,來給Tom買房子"); //執行目標對象的方法 Object returnVal = method.invoke(target,args); return returnVal; } }
測試類:
public class testJdkProxy { @Test public void test(){ BuyHouse tom = new Tom(); JdkProxy jdkProxy = new JdkProxy(tom); BuyHouse proxyInstance = (BuyHouse)jdkProxy.getProxyInstance(); proxyInstance.Buy(); } } /**測試結果 開始Jdk動態代理,來給Tom買房子 Tom買到房子了... */
注意Proxy.newProxyInstance()方法接受三個參數:
靜態代理和JDK代理模式都要求目標對象實現一個接口,可是有時候目標對象只是一個單獨的對象,並無實現任何對象,這個時候可使用目標對象的子類來實現代理,這就是Cglib代理。
CGLib採用了很是底層的字節碼技術,其原理是經過字節碼技術爲一個類建立子類,並在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢織入橫切邏輯。但由於採用的是繼承,因此不能對final修飾的類進行代理。JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎。
實現
建立沒有實現接口的Tom類:
public class Tom { public void Buy() { System.out.println("Tom買到房子了..."); } }
建立Cglib代理類:
public class CglibProxy implements MethodInterceptor { private Object target; public Object getProxyInstance(Object target){ this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("開始Cglib代理,來給Tom買房子"); Object returnVal = method.invoke(target, objects); return returnVal; } }
建立測試類:
public class testCglibProxy { @Test public void test(){ Tom proxyInstance = (Tom) new CglibProxy().getProxyInstance(new Tom()); proxyInstance.Buy(); } } /**測試結果 開始Cglib代理,來給Tom買房子 Tom買到房子了... */
CGLIB代理總結: CGLIB建立的動態代理對象比JDK建立的動態代理對象的性能更高,可是CGLIB建立代理對象時所花費的時間卻比JDK多得多。因此對於單例的對象,由於無需頻繁建立對象,用CGLIB合適,反之使用JDK方式要更爲合適一些。同時因爲CGLib因爲是採用動態建立子類的方法,對於final修飾的方法沒法進行代理。