JDK動態代理與CGLIB動態代理應用及原始碼解析
代理模式
代理模式:為其他物件提供一種代理以控制對這個物件的訪問。
代理模式中有三種角色:Subject抽象主題角色、RealSubject真實主題角色、Proxy代理主題角色。Subject描述了業務行為,RealSubject執行具體的業務邏輯,Proxy代理會攔截對RealSubject物件方法的呼叫,並在方法呼叫前後做預處理以及一些善後工作。
代理模式可以很好地在不侵入原始碼的情況下,拓展原來的功能。
下圖為Proxy模式的靜態類圖:
下圖為Proxy模式的呼叫關係:
動態代理
靜態代理由於硬編碼,難以應對真實物件和呼叫方法靈活多變的情況,動態代理則對這些場景應付自如。
動態代理主要有兩種實現方式:1、JDK自帶的Proxy 2、CGLIB位元組碼生成庫
基於JDK的動態代理
基於JDK的動態代理關鍵在於兩個類:InvocationHandler和Proxy。
其主要實現邏輯是,由InvocationHandler定義方法執行前後的增強邏輯,由Proxy類去生成一個繼承自Proxy並且實現了真實物件介面的新物件–代理物件,對該代理物件的方法呼叫經由InvocationHandler攔截,執行增強邏輯和呼叫真實物件執行業務邏輯。
下面我們先看一個例子:
定義介面:
public interface UserService {
public String getName(int id);
public Integer getAge(int id);
}
定義真實物件:
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
}
@Override
public String getName(int id) {
System.out.println("---getName---");
return "John";
}
@Override
public Integer getAge(int id) {
System.out.println("---getAge---");
return 10;
}
}
定義InvocationHandler:
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getName")) {
System.out.println("+++before get name+++");
Object res = method.invoke(target, args);
System.out.println("+++after get name+++");
return res;
} else {
Object res = method.invoke(target, args);
return res;
}
}
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
UserService us = new UserServiceImpl();
InvocationHandler ih = new MyInvocationHandler(us);
UserService usProxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(),
us.getClass().getInterfaces(), ih);
System.out.println(usProxy.getName(1));
System.out.println(usProxy.getAge(1));
System.out.println(usProxy.getClass());
}
}
測試結果:
可以看到,對於getName方法的增強邏輯執行了。
下面我們從原始碼角度分析一下這個過程的實現原理,先看InvocationHandler:
InvocationHandler是一個介面,只有一個方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
在定義增強邏輯類時,需要實現該介面,並在invoke方法裡實現增強邏輯和對真實物件方法的呼叫。對於invoke方法的三個引數,proxy表示代理物件,method表示真實物件的方法,args表示真實物件方法的引數,這裡也可以看出對真實物件方法的呼叫是通過反射來實現的。
增強邏輯定義好了以後,我們需要一個代理物件來執行,先看如何產生這個代理物件。代理物件的產生是通過呼叫Proxy類的靜態方法:newProxyInstance,以下原始碼分析部分篇幅原因僅擷取部分程式碼片段。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
//該方法需要三個引數:ClassLoader確保返回的代理物件和真實物件由同一個類
載入器載入,interfaces用於定義代理類應該實現的方法,
invocationHandler用於定義代理類的增強邏輯
{
if (h == null) {
throw new NullPointerException();
}
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//這一行程式碼很關鍵,這裡是獲取代理物件Class物件,Proxy類內部維護了代理
//物件的快取,如果快取裡有則直接返回,如果沒有,則生成它
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
//由Class物件獲取構造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
//最後由構造器返回新的代理物件例項
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
接下來我們看代理物件的Class物件是如何生成的,快取邏輯略去不表,直接看沒有快取的情況,代理類Class物件由ProxyClassFactory工廠生成:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
//驗證程式碼略去
/*
* Generate the specified proxy class.
*/
//這裡生成了代理物件二進位制位元組碼流
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//通過二進位制位元組碼流載入代理類
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
最終生成二進位制位元組碼流用到了Sun的ProxyGenerator類,反編譯:
public static byte[] generateProxyClass(String s, Class aclass[])
{
ProxyGenerator proxygenerator = new ProxyGenerator(s, aclass);
//該方法生成代理物件的二進位制位元組碼流
byte abyte0[] = proxygenerator.generateClassFile();
//如果要儲存該位元組碼檔案,可以將其寫到硬碟上,我們稍後分析
if(saveGeneratedFiles)
//儲存檔案部分略去
return abyte0;
}
具體生成原始碼如下:
private byte[] generateClassFile() {
addProxyMethod(hashCodeMethod, java / lang / Object);
addProxyMethod(equalsMethod, java / lang / Object);
addProxyMethod(toStringMethod, java / lang / Object);
for (int i = 0; i < interfaces.length; i++) {
Method amethod[] = interfaces[i].getMethods();
for (int k = 0; k < amethod.length; k++)
addProxyMethod(amethod[k], interfaces[i]);
}
List list;
for (Iterator iterator = proxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
list = (List) iterator.next();
try {
methods.add(generateConstructor());
for (Iterator iterator1 = proxyMethods.values().iterator(); iterator1.hasNext();) {
List list1 = (List) iterator1.next();
Iterator iterator2 = list1.iterator();
while (iterator2.hasNext()) {
ProxyMethod proxymethod = (ProxyMethod) iterator2.next();
fields.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
methods.add(proxymethod.generateMethod());
}
}
methods.add(generateStaticInitializer());
} catch (IOException ioexception) {
throw new InternalError("unexpected I/O Exception");
}
if (methods.size() > 65535)
throw new IllegalArgumentException("method limit exceeded");
if (fields.size() > 65535)
throw new IllegalArgumentException("field limit exceeded");
cp.getClass(dotToSlash(className));
cp.getClass("java/lang/reflect/Proxy");
for (int j = 0; j < interfaces.length; j++)
cp.getClass(dotToSlash(interfaces[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
try {
dataoutputstream.writeInt(-889275714);
dataoutputstream.writeShort(0);
dataoutputstream.writeShort(49);
cp.write(dataoutputstream);
dataoutputstream.writeShort(49);
dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
dataoutputstream.writeShort(interfaces.length);
for (int l = 0; l < interfaces.length; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));
dataoutputstream.writeShort(fields.size());
FieldInfo fieldinfo;
for (Iterator iterator3 = fields.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
fieldinfo = (FieldInfo) iterator3.next();
dataoutputstream.writeShort(methods.size());
MethodInfo methodinfo;
for (Iterator iterator4 = methods.iterator(); iterator4.hasNext(); methodinfo.write(dataoutputstream))
methodinfo = (MethodInfo) iterator4.next();
dataoutputstream.writeShort(0);
} catch (IOException ioexception1) {
throw new InternalError("unexpected I/O Exception");
}
return bytearrayoutputstream.toByteArray();
}
至此,總結一下,Proxy類通過ProxyClassFactory生成了繼承Proxy類並實現了真實物件介面的 $ProxyX代理物件的二進位制位元組碼流,並載入該位元組碼返回代理物件Class物件,由該Class物件經反射得到構造器構造了代理物件的例項。
在原始碼中我們可以通過saveGeneratedFiles變數儲存生成的class檔案,我們反編譯上面的示例生成的class檔案:
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m4;
private static Method m0;
private static Method m3;
private static Method m2;
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[]{obj})).booleanValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
//這裡是UserService介面的getAge方法,在代理物件$Proxy0上的呼叫被invocationHandler攔截,
//經由其invoke方法呼叫(即我們之前定義的MyinvocationHandler的invoke方法),
//留意invoke方法的引數,我們在MyinvocationHandler裡定義invoke方法時並沒有使用proxy引數,
//這裡proxy引數的位置傳入了this變數,即代理物件本身。
public final Integer getAge(int i) {
try {
return (Integer) super.h.invoke(this, m4, new Object[]{Integer.valueOf(i)});
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String getName(int i) {
try {
return (String) super.h.invoke(this, m3, new Object[]{Integer.valueOf(i)});
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m4 = Class.forName("cn.john.test.dynamicProxy.UserService").getMethod("getAge", new Class[]{Integer.TYPE});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("cn.john.test.dynamicProxy.UserService").getMethod("getName", new Class[]{Integer.TYPE});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
基於CGLIB的動態代理
使用示例
public class UserServiceB {
public String getName(int id) {
System.out.println("---getName---");
return "John";
}
public Integer getAge(int id) {
System.out.println("---getAge---");
return 10;
}
public static void main(String[] args) throws InterruptedException, IOException {
//將cglib生成的Class物件寫成檔案存入硬碟,後面反編譯出來用以分析
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
UserServiceB us = new UserServiceB();
// 定義增強器
Enhancer en = new Enhancer();
// 定義要代理的物件
en.setSuperclass(us.getClass());
// 定義回撥函式
en.setCallback(new MethodInterceptor() {
//這裡要理解intercept方法的幾個引數代表的意思
//obj指的是代理類物件
//Method指的是 目標類中被攔截的方法
//args指的是 呼叫攔截方法所需的引數
//MethodProxy指的是用來呼叫目標類被攔截方法的方法,這個方法比反射更快
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("-----before-------");
//這裡只能用invokeSuper,原因稍後解釋
methodProxy.invokeSuper(obj, args);
System.out.println("-----after--------");
return null;
}
});
// 生成代理物件
UserServiceB usb = (UserServiceB) en.create();
// 在代理物件上呼叫方法
usb.getName(1);
}
}
執行結果:
同時生成了三個class檔案:一個是代理類,一個是代理類的FastClass,一個是目標類的FastClass
原始碼分析
代理類原始碼概覽
從上面的示例程式碼中,我們知道通過cglib生成代理類只需要一個目標類和一個回撥函式(增強邏輯),下面我們從在代理物件上呼叫getName()方法出發,一步一步分析cglib動態代理的實現原理。
先來看生成的代理物件Class檔案反編譯後的原始碼(程式碼很長,代理了Object中的finalize,equals, toString,hashCode,clone方法,這裡僅留下getName()方法):
public class UserServiceB$$EnhancerByCGLIB$$a33459ad extends UserServiceB implements Factory {
//cglib基於繼承,可以看到代理類繼承了目標類,並實現了Factory介面,Factory介面可以簡化回撥函式的變更
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;//回撥函式
private static final Method CGLIB$getName$0$Method;//目標類的getName方法
private static final MethodProxy CGLIB$getName$0$Proxy;//getName方法的代理方法
//初始化變數
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
//代理類
Class class_ = Class.forName("cn.john.test.dynamicProxy.UserServiceB$$EnhancerByCGLIB$$a33459ad");
//目標類
Class class_2 = Class.forName("cn.john.test.dynamicProxy.UserServiceB");
Method[] arrmethod = ReflectUtils.findMethods(
(String[]) new String[]{"getName", "(I)Ljava/lang/String;", "getAge", "(I)Ljava/lang/Integer;"},
(Method[]) class_2.getDeclaredMethods());
//getName方法
CGLIB$getName$0$Method = arrmethod[0];
//建立更快的方法索引訪問方式,這裡通過目標類和代理類的Class物件和方法簽名為這兩個物件
//的所有方法都建立了索引,並提供了通過索引呼叫的方法。
//這裡CGLIB$getName$0$Proxy提供在目標類(或者相容類)上快速呼叫getName和在代理類上
//快速呼叫CGLIB$getName$0的功能
CGLIB$getName$0$Proxy = MethodProxy.create(class_2, class_, (String) "(I)Ljava/lang/String;",
(String) "getName", (String) "CGLIB$getName$0");
}
//直接呼叫了目標類的方法,這個方法即用來執行目標類真實邏輯,通過索引呼叫來快速訪問該方法
final String CGLIB$getName$0(int n) {
return super.getName(n);
}
//代理方法getName入口
public final String getName(int n) {
//先獲取回撥函式
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BIND_CALLBACKS((Object) this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
//獲取了回撥函式之後
if (methodInterceptor != null) {
//呼叫回撥函式的攔截方法。
//注意這裡傳入的引數,代理類傳入的this,被攔截的方法傳入的是目標類物件的
//被攔截的方法,methodProxy傳入的是getName方法的快速訪問代理
return (String) methodInterceptor.intercept((Object) this, CGLIB$getName$0$Method,
new Object[]{new Integer(n)}, CGLIB$getName$0$Proxy);
}
return super.getName(n);
}
public UserServiceB$$EnhancerByCGLIB$$a33459ad() {
UserServiceB$$EnhancerByCGLIB$$a33459ad userServiceB$$EnhancerByCGLIB$$a33459ad = this;
UserServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BIND_CALLBACKS((Object) userServiceB$$EnhancerByCGLIB$$a33459ad);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] arrcallback) {
CGLIB$THREAD_CALLBACKS.set(arrcallback);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] arrcallback) {
CGLIB$STATIC_CALLBACKS = arrcallback;
}
private static final void CGLIB$BIND_CALLBACKS(Object object) {
UserServiceB$$EnhancerByCGLIB$$a33459ad userServiceB$$EnhancerByCGLIB$$a33459ad = (UserServiceB$$EnhancerByCGLIB$$a33459ad) ((Object) object);
if (!userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BOUND) {
userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BOUND = true;
Object t = CGLIB$THREAD_CALLBACKS.get();
if (t != null || (v13465 = CGLIB$STATIC_CALLBACKS) != null) {
userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$CALLBACK_0 = (MethodInterceptor) ((Callback[]) t)[0];
}
}
}
static {
UserServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$STATICHOOK1();
}
}
進入回撥函式後,跟JDK動態代理InvocationHandler一樣,先執行前置增強邏輯,然後將目標類的真實邏輯。注意此處目標類的真實邏輯執行cglib的實現方式與JDK實現方式不同:JDK使用的是反射技術,而cglib則使用了FastClass構建方法索引+繼承的方式訪問目標類的方法。
建立getName方法和CGLIB$getName0的索引
CGLIB$getName$0$Proxy = MethodProxy.create(class_2, class_, (String) "(I)Ljava/lang/String;",
(String) "getName", (String) "CGLIB$getName$0");
//注意這裡class_2是目標類,class_是代理類
我們擷取MethodProxy類的部分程式碼:
private static class CreateInfo {
Class c1;
Class c2;
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;
public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
this.namingPolicy = fromEnhancer.getNamingPolicy();
this.strategy = fromEnhancer.getStrategy();
this.attemptLoad = fromEnhancer.getAttemptLoad();
}
}
}
//注意在MethodProxy類內部變數命名:這裡目標類為1,而代理類變為了2
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
//準備資料階段,目標類和代理類的Class物件,需要對映的方法簽名,這裡並不會
//觸發實際上的建立方法索引
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
實際建立方法索引發生在回撥函式中方法呼叫時(Lazyinit):
//MethodProxy類中的InvokeSuper
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();//實際的方法索引在此時建立
FastClassInfo fci = this.fastClassInfo;
//在代理類FastClass物件上呼叫索引為i2的方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
//內部類FastClassInfo
private static class FastClassInfo {
FastClass f1;//目標類fc
FastClass f2;//代理類fc
int i1;//目標類getName方法索引
int i2;//代理類CGLIB$getName$0方法索引
private FastClassInfo() {
}
}
private void init() {
if (this.fastClassInfo == null) {
Object object = this.initLock;
synchronized (object) {
if (this.fastClassInfo == null) {
CreateInfo ci = this.createInfo;
FastClassInfo fci = new FastClassInfo();
//這裡通過位元組碼技術生成目標類和代理類的FastClass類的例項
//FastClass關鍵有兩類方法:
//一類是getIndex方法,通過方法簽名獲取某方法的索引
//一類是invoke方法,通過方法的索引來找到方法並呼叫
fci.f1 = MethodProxy.helper(ci, ci.c1);//生成目標類的FastClass物件
fci.f2 = MethodProxy.helper(ci, ci.c2);//生成代理類的FastClass物件
//獲取目標類和代理類的方法索引
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;//至此,getName方法的索引建立、獲取完畢
this.createInfo = null;
}
}
}
}
回到示例上層程式碼:methodProxy.invokeSuper(obj, args);
這裡obj是代理物件,再看invokeSuper:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();//建立和獲取索引
FastClassInfo fci = fastClassInfo;
//在obj即代理物件上呼叫索引為i2的方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
現在我們反編譯代理類的FastClass類看一下,fci.f2.invoke(fci.i2, obj, args);
到底意味著什麼。
代理類FastClass(部分程式碼):
public class UserServiceB$$EnhancerByCGLIB$$a33459ad$$FastClassByCGLIB$$9e4fc4c5 extends FastClass {
public UserServiceB$$EnhancerByCGLIB$$a33459ad$$FastClassByCGLIB$$9e4fc4c5(Class class_) {
super(class_);
}
public int getIndex(Signature signature) {
String string = signature.toString();
switch (string.hashCode()) {
case -2024387448 : {
//在上面的Init方法中我們已經獲取了CGLIB$getName$0的索引,值為18
if (!string.equals("CGLIB$getName$0(I)Ljava/lang/String;"))
break;
return 18;
}
case 206620625 : {
if (!string.equals("getName(I)Ljava/lang/String;"))
break;
return 3;
}
}
return -1;
}
//在代理物件上呼叫索引為18的方法
public Object invoke(int n, Object object, Object[] arrobject) throws InvocationTargetException {
UserServiceB$$EnhancerByCGLIB$$a33459ad userServiceB$$EnhancerByCGLIB$$a33459ad = (UserServiceB$$EnhancerByCGLIB$$a33459ad) ((Object) object);
try {
switch (n) {
case 3 : {
return userServiceB$$EnhancerByCGLIB$$a33459ad.getName(((Number) arrobject[0]).intValue());
}
//在代理物件上呼叫了CGLIB$getName$0方法,回到代理類原始碼,它呼叫super.getName,即目標類的真實邏輯
case 18 : {
return userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$getName$0(((Number) arrobject[0]).intValue());
}
}
} catch (Throwable v1) {
throw new InvocationTargetException(v1);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
}
現在我們來回答一下為什麼示例程式碼中不能呼叫invoke,而是隻能呼叫invokeSuper。invoke程式碼:
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
//因為在回撥函式中obj傳入的代理物件,這裡實際上是在代理物件上呼叫
//getName方法,將陷入無限遞迴,直至棧溢位
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (IllegalArgumentException e) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw e;
}
}
總結
型別 | 機制 | 回撥方式 | 適用場景 | 效率 |
---|---|---|---|---|
JDK動態代理 | 委託機制,代理類和目標類都實現了同樣的介面,InvocationHandler持有目標類,代理類委託InvocationHandler去呼叫目標類的原始方法 | 反射 | 目標類是介面類 | 效率瓶頸在反射呼叫稍慢 |
CGLIB動態代理 | 繼承機制,代理類繼承了目標類並重寫了目標方法,通過回撥函式MethodInterceptor呼叫父類方法執行原始邏輯 | 通過FastClass方法索引呼叫 | 非介面類,非final類,非final方法 | 第一次呼叫因為要生成多個Class物件較JDK方式慢,多次呼叫因為有方法索引較反射方式快,如果方法過多switch case過多其效率還需測試 |