代理模式是常見設計模式的一種,代理模式的定義是:爲其餘對象提供一種代理以控制對這個對象的訪問。html
在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。java
理解設計模式是比較枯燥的,因此仍是以舉例子的方式來進行理解,spring
例如:公司開年會想找個明星來表演,那麼並不會直接聯繫明星(主要仍是聯繫不上),而是會聯繫明星的經紀人,明星就是被代理的對象,而經紀人就是代理對象。明星只須要準備來參加年會時應該表演什麼節目就能夠,其餘的出場費之類的事情就交給經紀人來處理就行了。代理對象能夠理解爲被代理對象的擴展,能作被代理對象不能作的事情,也能夠調用代理對象作事情。設計模式
那麼用代碼實現這個場景是什麼樣子的呢?框架
執行合做方法的接口ide
/** * @Description: 經紀公司接口,代理對象和被代理對象都須要實現的接口 */ public interface Company { /** 合做 */ void cooperation(); }
被代理對象函數
/** * @Description: 目標對象-明星(被代理對象) */ public class Start implements Company { @Override public void cooperation() { System.out.println("is show time"); } }
代理對象工具
/** * @Description: 經紀人(代理對象) */ public class Agent implements Company { private Company company; public Agent(Company company) { this.company = company; } @Override public void cooperation() { System.out.println("收出場費,化妝等等"); company.cooperation(); System.out.println("收拾行李,打道回府"); } }
測試類oop
import org.junit.Test; /** * @Description: 測試類 */ public class ProxyTest { @Test public void AnnualMeeting() { //目標對象 Start start = new Start(); //構建代理對象,生成代理關係 Agent agent = new Agent(start); //用代理對象執行被代理對象的動做 agent.cooperation(); } }
輸出結果:性能
收出場費,化妝等等
is show time
收拾行李,打道回府
靜態代理的特色是:能夠在目標對象實現的基礎上,加強額外的功能操做,即擴展目標對象的功能。有時候不方便修改別人的代碼或者是引入的一個功能,須要進行功能擴展一下才能適用於本身的業務實現,可使用代理模式來進行設計。
可是靜態代理的實現基礎是一個目標對象對應一個代理對象,而且在編譯時就已經維護好了代理關係,若是目標對象是多個那麼就會須要多個代理對象,這樣在更新目標的對象的時候還須要更新代理對象,當代理對象持續增長時維護成本就變得很是困難。
針對於這種狀況,動態代理應運而生。
動態代理的代理對象不須要和目標對象共同實現接口,而是利用JDK的API,動態的在內存中構建代理對象。
動態生成代理對象須要調用JDK中的java.lang.reflect.Proxy類的newProxyInstance方法,這個方法須要三個參數:
@CallerSensitive public static Object newProxyInstance(ClassLoader loader,]Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader:類加載器,用來加載目標對象類,由於是在運行時得到目標對象,因此確定須要用到反射。
Class<?>[] interfaces:目標對象類實現的接口集合,這些接口中定義目標對象能夠執行的方法。
InvocationHandler h:這個參數表明的是動態代理對象在調用方法的時候,會將方法轉發到哪個invocationHandler對象身上,InvocationHandler是個接口,
須要本身實現它,而後定義本身的動態代理執行方法。
建立包含動態代理對象具體執行方法的實現類。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * @Description: 包含動態代理對象具體執行方法的實現類 */ public class MyInvocationHandler implements InvocationHandler { private Company company; public MyInvocationHandler(Company company) { this.company = company; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("收出廠費,化妝等"); //具體執行方法 Object result = method.invoke(company,args); System.out.println("收拾現場,卸妝,打道回府"); return result; } }
測試類
import org.junit.Test; import java.lang.reflect.Proxy; /** * @Description: 測試類 */ public class DynamicProxyTest { @Test public void AnnualMeeting() { //建立目標對象 Company start = new Start(); //建立代理對象須要執行的方法處理對象 MyInvocationHandler myInvocationHandler = new MyInvocationHandler(start); //得到目標對象的類加載器 ClassLoader classLoader = start.getClass().getClassLoader(); //建立動態代理對象 Company proxy = (Company) Proxy.newProxyInstance(classLoader,start.getClass().getInterfaces(),myInvocationHandler); //用動態代理對象執行目標對象的方法 proxy.cooperation(); } }
輸出結果:
收出廠費,化妝等
is show time
收拾現場,卸妝,打道回府
JDK動態代理的特色:代理對象不須要實現接口,可是目標對象必須實現接口。
那麼若是在實際的業務中目標對象確實沒有實現接口,怎麼辦呢?
遇到這種狀況的時候就須要時cglib動態代理了。
Cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.
在實現cglib代理時須要引入cglib的jar包,可是spring核心功能已經包含了cglib的功能,因此引入spring-core的jar包就能夠了。
須要注意的是:代理的類不能爲final,不然報錯,目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法。
沒有實現接口的目標對象類
/** * @Description: 沒有經紀公司的明星,就行像最近以我的練習生出道的蔡徐坤 */ public class AloneStart { /** 合做 */ public void cooperation() { System.out.println("is show time"); } }
生成Cglib代理對象的類
import org.mockito.cglib.proxy.Enhancer; import org.mockito.cglib.proxy.MethodInterceptor; import org.mockito.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @Description: 生成代理對象的類 */ public class CglibProxy implements MethodInterceptor { private AloneStart aloneStart; public CglibProxy(AloneStart aloneStart) { this.aloneStart = aloneStart; } /** * 建立代理對象 * @return */ public Object getProxyInstance() { //動態代理工具類 Enhancer enhancer = new Enhancer(); //設置父類 enhancer.setSuperclass(aloneStart.getClass()); //設置回調函數調用對象 enhancer.setCallback(this); //返回代理對象 return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("收出廠費,化妝等"); //執行代理方法 methodProxy.invokeSuper(obj,objects); System.out.println("卸妝,回家"); return null; } }
測試類
import org.junit.Test; /** * @Description: 測試類 */ public class CglibProxyTest { @Test public void cglibTest() { //建立目標對象 AloneStart aloneStart = new AloneStart(); //建立代理對象 AloneStart startProxy = (AloneStart) new CglibProxy(aloneStart).getProxyInstance(); //用代理對象執行目標對象的方法 startProxy.cooperation(); } }
輸出結果:
收出廠費,化妝等
is show time
卸妝,回家
jdk採用反射機制調用委託類的方法,而cglib採用相似索引的方式直接調用委託類方法;
還有須要注意的是:
在Spring的AOP中
若是加入容器的目標對象有實現接口,用JDK代理
若是目標對象沒有實現接口,用Cglib代理
參考:
Java的三種代理模式: https://www.cnblogs.com/cenyu/p/6289209.html
說說代理模式:http://www.importnew.com/26116.html