Java代理3:二刷代理
- 2020 年 1 月 22 日
- 筆記
靜態代理

- Subject:抽象角色。可以是介面,也可以是抽象類。聲明真實對象(Real Subject)和代理對象(Proxy Subject)的共同介面。
- Real Subject:真實角色。定義代理角色所代表的真實實體,即最終要引用的對象。
- Proxy Subjcet:代理角色。包含真實對象的引用,負責對真實對象的調用,並在真實對象處理前後做預處理與善後工作;提供與真是對象(Real Subject)相同的介面以便任何時候都可以代替真實實體。
為保持行為的一致性,代理角色與真實角色通常會實現相同的介面。
詳細程式碼實例請見:Java代理1 代理和動態代理的基礎與使用 中的靜態代理部分。
優點
- 職責清晰:真是角色只需關注業務邏輯的實現。非業務邏輯的部分交給後期的代理角色即可。
- 高擴展:可以在不修改真實對象的前提下,對目標功能擴展。
缺點
- 代理角色的一個介面對應一種類型的對象(即代理角色與真實角色通常會實現相同的介面),如果要代理的過多,就會產生大量的類,從而導致類的急速膨脹。
- 介面添加一個方法後,所有實現類都要實現該方法,所有的代理類也需要實現該方法。增加了程式碼維護的複雜度。
JDK Proxy動態代理
在使用動態代理時,需要定義一個位於代理類與委託類之間的中介類,也叫動態代理類,該類被要求實現InvocationHandler介面。
當調用代理類對象的方法時,這個「調用」會轉送到中介類的invoke方法中。參數proxy為調用method的代理實例(即動態生成的那個代理類),method標識了我們具體調用的是代理類的哪個方法,args為這個方法的參數。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
一個典型的動態代理可分為以下四個步驟:
- 創建抽象角色(如:Animal)
- 創建真實角色(如:Dog)
- 通過實現InvocationHandler介面創建中介類(如:DynamicProxyHandler)
- 通過場景類,動態生成代理類(如:SimpleDynamicProxy)
詳細程式碼實例請見:Java代理2 動態代理的實現原理分析
在場景類中通過如下程式碼動態產生了一個代理類,並返回了其實例:
Animal prox = (Animal) Proxy.newProxyInstance( Animal.class.getClassLoader(), new Class[]{Animal.class}, new DynamicProxyHandler(dog) );Proxy的newProxyInstance主要業務邏輯為:
// 1 獲得與傳入的指定類裝載器(loader)和介面列表(intfs)相關的代理類類型對象,亦既生成代理類class,並載入到JVM中 Class<?> cl = getProxyClass0(loader, intfs); // 2 通過反射獲取該代理類的構造函數,亦既獲取代理類的參數類型為InvocationHandler的構造函數 final Constructor<?> cons = cl.getConstructor(constructorParams); // 3 返回這個新的代理類的一個實例,亦既生成並返回代理類實例 return cons.newInstance(new Object[]{h});具體做的事有:
- 根據傳入的參數interfaces動態生成一個類,它實現interfaces中的介面,該例中即Animal介面的doBark方法和somethingElse方法。假設動態生成的類為$Proxy0。
- 通過傳入的classloder(該例中即Animal.class.getClassLoader()),將剛生成的$Proxy0類載入到jvm中。
- 利用中介類,調用$Proxy0的$Proxy0(InvocationHandler)構造函數,創建$Proxy0類的實例,其InvocationHandler屬性,為我們創建的中介類。
其核心為getProxyClass0方法。
/** * Generate a proxy class. Must call the checkProxyAccess method * to perform permission checks before calling this. */ private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { // 校驗介面數量 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 從快取中獲取,如果沒有就通過ProxyClassFactory創建 // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }Proxy類中有一屬性proxyClassCache,這是一個java.lang.reflect.WeakCache類型的靜態變數,其指示了類載入器和代理類之間的映射。proxyClassCache的get方法用於根據類載入器來獲取Proxy類,如果已經存在則直接從cache中返回,如果沒有則創建一個映射並更新cache表。
proxyClassCache的get方法supplier.get() 當是創建代理類時,supplier為是Factory,這個類定義在WeakCach的內部。Factory中get方法又通過 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); 調用了 java.lang.reflect.Proxy.ProxyClassFactory 的apply方法(valueFactory即ProxyClassFactory)。
最終通過apply中的如下語句生成代理類。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
ProxyGenerator.generateProxyClass方法可以不填寫最後一個參數,從而我們可以通過如下程式碼手動生成代理類。
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Dog.class.getInterfaces());JDK的動態生成的代理類均會繼承Proxy類,由於java的單繼承特性,故之後只能跟隨實現介面,從而只能代理實現了介面的類。
CGLIB代理
JDK動態代理只能代理實現了介面的類。
CGLIB動態代理針對類實現代理,原理是對目標類生成一個子類,並覆蓋其中方法實現增強,由於是繼承,無法對final修飾的類進行代理。
實現步驟
- 創建目標對象(如:DogCGlib)
- 通過實現MethodInterceptor介面創建中介類(如:CGlibProxy)
- 通過場景類,動態生成代理類(如:CGLIBDemo)
詳細程式碼實例請見:Java代理1 代理和動態代理的基礎與使用 中的CGLIB動態代理部分。
在場景類中通過如下程式碼動態產生了一個代理類,並返回了其實例:
// proxy為CGlibProxy實例,即CGlibProxy proxy = new CGlibProxy(); DogCGlib dog = (DogCGlib)proxy.getInstance(DogCGlib.class);
getInstance要做的事有:
- 創建Enhancer實例
- 通過setSuperclass設置目標類
- 通過setCallback設置攔截對象
- create方法生成Target的代理類,並返回代理類的實例
Enhancer是CGLib的位元組碼增強器,可以方便的對類進行擴展,內部調用GeneratorStrategy.generate方法生成代理類的位元組碼,通過以下方式可以生成class文件:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./tmp/");
具體生成如下:

其中DogCGlib$$EnhancerByCGLIB$$556eb8b5.class為生成的代理類,進去可見
public class DogCGlib$$EnhancerByCGLIB$$556eb8b5 extends DogCGlib implements Factory { ... static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("Others.base.SimpleProxy.CGLibDemo.DogCGlib$$EnhancerByCGLIB$$556eb8b5"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$2$Method = var10000[0]; CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); CGLIB$toString$3$Method = var10000[1]; CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3"); CGLIB$hashCode$4$Method = var10000[2]; CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4"); CGLIB$clone$5$Method = var10000[3]; CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5"); var10000 = ReflectUtils.findMethods(new String[]{"somethingElse", "(Ljava/lang/String;)V", "doBark", "()V"}, (var1 = Class.forName("Others.base.SimpleProxy.CGLibDemo.DogCGlib")).getDeclaredMethods()); CGLIB$somethingElse$0$Method = var10000[0]; CGLIB$somethingElse$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "somethingElse", "CGLIB$somethingElse$0"); CGLIB$doBark$1$Method = var10000[1]; CGLIB$doBark$1$Proxy = MethodProxy.create(var1, var0, "()V", "doBark", "CGLIB$doBark$1"); } private MethodInterceptor CGLIB$CALLBACK_0; ... final void CGLIB$doBark$1() { super.doBark(); } public final void doBark() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$doBark$1$Method, CGLIB$emptyArgs, CGLIB$doBark$1$Proxy); } else { super.doBark(); } } ... }每個被代理的方法都對應一個MethodProxy對象,methodProxy.invokeSuper方法最終調用委託類的doBark方法。
JDK代理通過反射,CGLIB通過MethodProxy+FastClass方式(類似索引的方式直接調用委託類方法)實現方法調用。
FastClass其實就是對Class對象進行特殊處理,提出下標概念index,通過索引保存方法的引用資訊,將原先的反射調用,轉化為方法的直接調用,從而體現所謂的fast。
CGLIB部分暫時跟不下去了,網上搜到的內容大量的同質化,擴展裡面的前三篇相對來說同質化的內容少一些。由於有些內容還不明白,導致不能像JDK動態代理那樣流暢。
問題如:
- 怎樣通過生成的動態代理類調到intercept。
- 動態代理類如何與FastClass相結合的。
核心其實就是:
dog.doBark();
如何調到MethodInterceptor.intercept方法的,這個留給日後單獨開一篇探索吧。

