一、代理模式
代理的概念來自於設計模式中的代理模式,先了解一下代理模式
1、結構圖
2、參與者
Subject:接口,定義代理類和實際類的共用接口
RealSubject:實際類,實現Subject這個接口
Proxy:代理類,實現Subject這個接口,內部引用一個RealSubject實際類
3、描述
Proxy實現了Subject接口,內部引用一個RealSubject實際類,RealSubject能做的Proxy都會
於是Proxy代替了RealSubject,實例化到Subject里交給客戶端調用,而客戶端不知道自己實際用的是誰
客戶端調用了Subject接口中的Request()方法,實際上訪問的是Proxy
Proxy類提供了一個客戶端對RealSubject的間接性訪問的過程,在這個過程中可以做很多的控制
4、意圖
控制對象的訪問
5、應用場景
虛代理:根據需要來創建開銷很大的對象,該對象只有在需要的時候才會被真正創建
遠程代理:用來在不同的地址空間上代表同一個對象,這個不同的地址空間可以是在本機,也可以在其他機器上,在java里面最典型的就是RMI技術
copy-on-write代理:在客戶端操作的時候,只有對象確實改變了,才會真的拷貝(或克隆)一個目標對象,算是虛代理的一個分支
保護代理:控制對原始對象的訪問,如果有需要,可以給不同的用戶提供不同的訪問權限,以控制他們對原始對象的訪問
Cache代理:為那些昂貴操作的結果提供臨時的存儲空間,以便多個客戶端可以共享這些結果
防火牆代理:保護對象不被惡意用戶訪問和操作
同步代理:使多個用戶能夠同時訪問目標對象而沒有沖突
二、靜態代理
java中的靜態代理就是代理模式最簡單的實現,下面是示例代碼:
//接口 public interface Subject { void Request(); } //實際類 public class RealSubject implements Subject { public void Request() { System.out.println("執行具體的功能"); } } //代理類 public class Proxy implements Subject { private Subject realSubject = null; public Proxy(Subject subject) { realSubject = subject; } public void Request() { System.out.println("靜態代理開始"); realSubject.Request(); System.out.println("靜態代理結束"); } } //客戶端 public class Client { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); realSubject.Request(); Subject subject = new Proxy(realSubject); subject.Request(); } }
執行的結果是:
靜態代理的局限:
一個代理類Proxy只能服務Subject一種接口,如果有10個不同類型的對象,需要創建10個代理類,類的數量激增、也增加了代碼的冗余
如果只用一個代理類,就能完成全部的代理功能就好了,於是就有了動態代理
三、動態代理
靜態代理的代碼,在編譯后就生成了代理類的.class字節碼文件,所以在程序運行以前,代理類和實際類的關系就確定了
而動態代理不同,代理類的字節碼是在程序運行期間,由JVM根據反射等機制動態生成的,代理類和實際類的關系是在程序運行時確定的
依托反射等機制,動態代理可以動態生成任意類型的代理類,一個代理的處理程序就可以處理多種對象,程序更加的靈活,也更容易擴展
spring的AOP、RMI遠程代理就是依靠動態代理來實現的
java中的動態代理有兩種,一種是java自帶的jdk動態代理,一種是基於cglib的動態代理
1、jdk動態代理
jdk1.3開始,java語言提供了對動態代理的支持,主要會使用到java.lang.reflect包下的Proxy和InvocationHandler類
(1)、Proxy
Proxy類提供了用於創建動態代理類和實例對象的方法,它是所創建的動態代理類的父類
其中newProxyInstance()方法用來獲得動態生成的代理類實例,第一個參數是實際類的類加載器,第二個參數是實際類的接口列表,第三個參數是代理處理程序類
(2)、InvocationHandler
InvocationHandler接口是代理處理程序類的實現接口,每一個被生成的代理類實例都可以提供一個相關的代理處理程序類,代理類實例的方法被調用時會回調invoke()方法
invoke()方法用來處理代理類實例的方法調用並返回結果,實現自己的代理邏輯,第一個參數是代理類實例,第二個參數是需要代理的方法,第三個參數是方法的參數數組
//接口 public interface Subject { void Request(); } //實際類 public class RealSubject implements Subject { public void Request() { System.out.println("執行具體的功能"); } } import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; //代理處理程序類 public class ProxyHandler implements InvocationHandler { private Object target; public ProxyHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] param) throws Throwable { System.out.println("jdk動態代理開始"); Object result = method.invoke(target, param); System.out.println("jdk動態代理結束"); return result; } } import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; //客戶端 public class Client { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); InvocationHandler handler = new ProxyHandler(realSubject); Subject subject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); subject.Request(); } }
執行的結果是:
通過"realSubject.getClass().getInterfaces()"這句可以看到,jdk動態代理的實現是依賴接口的
如果是抽象類或者一般的類需要進行代理,jdk動態代理就無法滿足了,只有使用cglib動態代理
2、cglib動態代理
cglib是一個強大的高性能的代碼生成包,廣泛的被許多AOP的框架使用,它的開源地址在https://github.com/cglib/cglib
通過它實現動態代理,主要用到import net.sf.cglib.proxy包下的MethodInterceptor、MethodProxy和Enhancer類
(1)、MethodInterceptor
MethodInterceptor類是方法攔截器,代理類實例的方法被調用時會回調MethodInterceptor的intercept()方法攔截,用來實現自己的代理邏輯,類似於jdk動態代理的InvocationHandler接口
(2)、MethodProxy
MethodProxy是intercept()方法中的第四個參數的類型,它是實際類方法的代理引用,使用methodProxy比使用jdk自身的method在效率上會有提升
(3)、Enhancer
Enhancer用來動態創建實際類子類的代理類實例,setSuperclass()方法設置實際類為父類,setCallback()方法建立方法回調,create()方法創建一個代理類實例
//實際類 public class RealSubject { public void Request() { System.out.println("執行具體的功能"); } public final void RequestFinal() { System.out.println("執行具體的功能(final)"); } } import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; //方法攔截器 public class ProxyInterceptor implements MethodInterceptor { public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable { System.out.println("cglib動態代理開始"); Object result = methodProxy.invokeSuper(proxy, params); System.out.println("cglib動態代理開始"); return result; } //創建代理類實例 public Object newProxy(Object target) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } } //客戶端 public class Client { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); ProxyInterceptor proxyInterceptor = new ProxyInterceptor(); RealSubject subject = (RealSubject)proxyInterceptor.newProxy(realSubject); subject.Request(); subject.RequestFinal(); } }
執行的結果是:
通過輸出的結果可以發現final的方法cglib是無法處理的,因為cglib是基於繼承的代理,final的方法無法被重寫
3、jdk、cglib動態代理的區別
jdk動態代理是由java內部的反射機制生成一個實現代理接口的匿名類,被代理的類必須要實現一個接口,如果某個類沒有實現接口則不能生成代理對象,實際使用中會有一些局限性
cglib動態代理底層是借助asm來實現的,加載實際類的.class文件,修改其字節碼生成子類來處理,比java反射效率要高一些,不過生成子類的方式也帶來小問題:目標類不能聲明成final,因為final類不能被繼承,無法生成代理;目標方法也不能聲明成final,final方法不能被重寫,無法得到處理
在實際使用中cglib的應用更加廣泛,效率更有優勢
實例代碼地址:https://github.com/ctxsdhy/cnblogs-example