大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
cglib 动态代理详解
我们都知道jdk的动态代理内部调用切面无效的问题,而cglib则不会出现这种情况,这是为什么?cglib就一定不会出现内部调用切面无效的问题吗?cglib针对每一个类只创建了一个代理类吗?为什么cglib的效率要比jdk的动态代理低呢?
首先我们看一下通常我们是如何使用cglib增强一个类的
public class Main {
static class Test{
public void test(){
System.out.println("test");
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
innerTest();
}
public void innerTest(){
System.out.println("inner test");
}
}
public static void main(String[] args) throws Exception {
Enhancer enhancer = new Enhancer();
// 设置被代理的类
enhancer.setSuperclass(Test.class);
// 创建MethodInterceptor对象作为回调方法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
long cost = System.currentTimeMillis();
// 调用实际方法,也就是被代理类原本的方法
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println("method " + method.getName() + " cost time :" + (System.currentTimeMillis() - cost));
return result;
}
});
// 生成代理类,并创建代理类对象
Test test = (Test)enhancer.create();
// 调用代理类对象的方法
test.test();
}
}
输出结果
test
inner test
method innerTest cost time :0
method test cost time :1012
结果完全符合我们的预期,内部调用的函数也被增强了,那我们接下来就一步步的看一下,cglib究竟是如何实现动态代理的。
1. cglib生成的代理类长什么样?
我们先看一下cglib是如何创建一个代理类的,从我们代码中调用的net.sf.cglib.proxy.Enhancer#create()方法开始一层层的往下看
// net.sf.cglib.proxy.Enhancer#create
// 我们创建代理类的入口
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}
// net.sf.cglib.proxy.Enhancer#createHelper
private Object createHelper() {
// 预处理,验证是否有明确的回调信息
preValidate();
// 存储类信息、回调方法信息等,cglib使用这些信息对象作为缓存key;之后可以根据key直接去缓存中查找创建过的代理类
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
// 生成代理类信息,并返回代理对象
Object result = super.create(key);
return result;
}
// 父类的create方法,创建代理类并获取代理类对象
// net.sf.cglib.core.AbstractClassGenerator#create
protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
/**
* 省略从缓存中获取对象的代码
*/
this.key = key;
// 从缓存中获取代理类的Class对象
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
// 使用代理类的Class对象进行实例化
return firstInstance((Class) obj);
}
return nextInstance(obj);
} catch (RuntimeException e) {
// 省略异常处理代码
......
}
}
// 如果使用缓存,则从缓存中取,若不存在则先生成代理类;如果不使用缓存则直接创建
// net.sf.cglib.core.AbstractClassGenerator.ClassLoaderData#get
public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
// 创建代理类并生成对象
return gen.generate(ClassLoaderData.this);
} else {
Object cachedValue = generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
// 生成代理类并创建代理对象的核心代码;cglib所有的代理类都从这里创建
// net.sf.cglib.core.AbstractClassGenerator#generate
protected Class generate(ClassLoaderData data) {
Class gen;
Object save = CURRENT.get();
CURRENT.set(this);
try {
// 获取类加载器
ClassLoader classLoader = data.getClassLoader();
....
synchronized (classLoader) {
// 生成代理类的类名
String name = generateClassName(data.getUniqueNamePredicate());
data.reserveName(name);
this.setClassName(name);
}
// 生成代理类的字节码;
// 若想获取生成的代理类的字节码信息可以在此打断点,将字节数据输出到本地文件中,之后所有获取的代理类字节码信息均采用此方法
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) {
// 将字节码转换成Class对象
if (protectionDomain == null) {
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
return gen;
} catch (RuntimeException e) {
....
// 省略异常处理
}
}
从代码中我们可以看到,最终所有代理类都是通过调用net.sf.cglib.core.AbstractClassGenerator#generate创建的,这也是。
我们开启debug,在生成字节码的代码之后打断点,将字节码信息输出到本地文件中,再经过反编译就能拿到生成的代理类的代码,我们看一下Test的代理代码,以下省略了所有的hashCode、equals等从Object继承而来的方法增强的代码以及其他不是很重要的初始化方法的代码。
// 代理类是Test的子类
public class Main$Test$$EnhancerByCGLIB$$a07c0bc4 extends Test implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$test$0$Method;
private static final MethodProxy CGLIB$test$0$Proxy;
private static final Method CGLIB$innerTest$1$Method;
private static final MethodProxy CGLIB$innerTest$1$Proxy;
static void CGLIB$STATICHOOK1() {
// 代理类的Class对象
Class var0 = Class.forName("com.netease.com.Main$Test$$EnhancerByCGLIB$$a07c0bc4");
Class var1;
// 获取test和innerTest的Method对象,同时这里给var1变量赋值,指向了未被代理的Class对象
var10000 = ReflectUtils.findMethods(new String[]{"test", "()V", "innerTest", "()V"}, (var1 = Class.forName("com.netease.com.Main$Test")).getDeclaredMethods());
CGLIB$test$0$Method = var10000[0];
CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
CGLIB$innerTest$1$Method = var10000[1];
CGLIB$innerTest$1$Proxy = MethodProxy.create(var1, var0, "()V", "innerTest", "CGLIB$innerTest$1");
}
// 父类的test方法
final void CGLIB$test$0() {
super.test();
}
// 代理后的test方法,实际有效内容为我们编码时的MethodInterceptor对象
public final void test() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy);
} else {
super.test();
}
}
// 父类的innerTest方法
final void CGLIB$innerTest$1() {
super.innerTest();
}
// 被代理的innerTest方法,实际有效内容为我们编码时创建的MethodInterceptor对象
public final void innerTest() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
// 调用编码时的intercept方法
var10000.intercept(this, CGLIB$innerTest$1$Method, CGLIB$emptyArgs, CGLIB$innerTest$1$Proxy);
} else {
super.innerTest();
}
}
// 获取方法代理对象
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1422510685:
if (var10000.equals("test()V")) {
return CGLIB$test$0$Proxy;
}
break;
case -379268499:
if (var10000.equals("innerTest()V")) {
return CGLIB$innerTest$1$Proxy;
}
break;
}
return null;
}
}
从生成的代理对象文件中我们能看到每一个被代理的方法都会创建一个net.sf.cglib.proxy.MethodProxy对象与之绑定,同时代理方法的实际执行方法为我们编码时创建的net.sf.cglib.proxy.MethodInterceptor接口对象。
其中我们关注的四个方法我在这里重点标注一下,正常签名的方法是代理后的方法,增加CGLIB$前缀的方法则是原始类的方法
方法名 | 功能 |
---|---|
CGLIB$test$0() | 父类(原始类)的test方法 |
test() | 代理后的test方法 |
CGLIB$innerTest$1() | 父类(原始类)的innerTest方法 |
innerTest() | 代理后的innerTest方法 |
2. 代理类是如何调用父类方法的
从生成的代理类的代码中我们可以看到,我们最终调用代理对象的方法时,实际上都是执行的编码时创建的net.sf.cglib.proxy.MethodInterceptor接口对象,这个接口只有一个intercept方法。
/**
* 所有被代理的方法实际调用时都会调用改方法,
* @param obj 代理对象,千万注意这个参数是代理后的对象,从生成的代理类中我们也可以看到,每次传入的this
* @param method 被代理的方法
* @param args 方法参数
* @param proxy 每个被代理的方法都会创建一个MethodProxy对象与之绑定,传入的就是与之绑定的MethodProxy对象,可以用它调用未被代理的方法(官方建议)
* @return 方法返回结果
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
net.sf.cglib.proxy.MethodInterceptor接口类似于jdk动态代理中的java.lang.reflect.InvocationHandler接口,所有代理方法的执行都交予该接口,从intercept方法的参数中我们可以看到调用未被代理类(即代理类的父类)的方法可以使用MethodProxy。
我们在编码的时候使用了methodProxy.invokeSuper()方法,我们接下来看下invokeSuper方法是如何调用父类方法的。
// net.sf.cglib.proxy.MethodProxy#invokeSuper
// 调用父类(未被代理的原始类)方法
// 官方限制了传入的obj必须是生成的代理对象
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
// 初始化FastClassInfo
init();
FastClassInfo fci = fastClassInfo;
// 实际调用方法
// f2绑定的是代理类,i2绑定的原始方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
我们首先看一下init()方法都做了哪些事情,重要的参数来源我都已经在注释上标注了。
// net.sf.cglib.proxy.MethodProxy#init
// 初始化FastClassInfo
private void init()
{
// 忽略double-check代码
......
// 获取类信息,其中createInfo是在MethodProxy对象创建时赋值的, c1是原类,c2是代理类
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
// 创建FastClass代理类, f1针对原类, f2针对代理类
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
// sig1和sig2也是创建MethodProxy对象时创建的,sig1是代理方法的签名,sig2是原方法的签名
// 这里千万要注意,代理方法的签名才是test(), 原方法的签名已被改成CGLIB$test$0()
// 所以i1是代理方法的索引,i2是原方法的索引
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
// net.sf.cglib.proxy.MethodProxy#create
// 创建MethodProxy对象的唯一入口,构造函数是私有的
// 由此可见createInfo的两个变量c1和c2是创建的时候传入的,回顾我们代理类中创建MethodProxy对象时传入的参数,我们能看到c1是原类,c2是代理类
// sig1是代理方法的签名,sig2是原方法的签名
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
// net.sf.cglib.proxy.MethodProxy#helper
// 生成FastClass类
private static FastClass helper(CreateInfo ci, Class type) {
FastClass.Generator g = new FastClass.Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
// 实际上内部调用了net.sf.cglib.core.AbstractClassGenerator#generate生成了一个代理类
return g.create();
}
从源码中我们可以看到,init()方法创建了FastClassInfo对象,同时生成了两个FastClass代理类,f1与原始类绑定,f2与代理类绑定。接着我们看一下fci.f2.invoke(fci.i2, obj, args);这行代码究竟执行了什么。FastClass的invoke方法本是一个abstract方法:abstract public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException;
,但是我们生成的两个代理类中,均对这个函数的方法体进行了补全,这里我们先把f2的字节码反编译查看,其中我们使用的是两个方法getIndex()和invoke()方法,分别是根据方法签名获取索引,以及是调用实际方法拿到结果
public class Main$Test$$EnhancerByCGLIB$$a07c0bc4$$FastClassByCGLIB$$c6aa9344 extends FastClass {
// 根据方法签名查询索引
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1659809612:
if (var10000.equals("CGLIB$test$0()V")) {
return 16;
}
break;
case -1422510685:
if (var10000.equals("test()V")) {
return 7;
}
break;
case -379268499:
if (var10000.equals("innerTest()V")) {
return 9;
}
break;
case -216640445:
if (var10000.equals("CGLIB$innerTest$1()V")) {
return 17;
}
break;
return -1;
}
// 根据索引找到对应的方法执行,然后返回执行结果
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
// 代理类对象
// a07c0bc4是我们生成的代理类的类名,所以这就是为什么当初调用invokeSuper()的时候传入的obj必须是生成的代理对象,传入其他的对象会因为强制转换失败而报错
a07c0bc4 var10000 = (a07c0bc4)var2;
// 索引
int var10001 = var1;
try {
switch(var10001) {
case 7:
// test代理方法
var10000.test();
return null;
case 9:
// innerTest代理方法
var10000.innerTest();
return null;
case 16:
// test原方法
var10000.CGLIB$test$0();
return null;
case 17:
// innerTest原方法
var10000.CGLIB$innerTest$1();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
}
我们再来回头看一下我们编码时的调用Object result = methodProxy.invokeSuper(proxy, args);
, 此时invokeSuper方法内部使用FastClass对象是与代理类绑定的f2,方法签名索引使用的原始方法,即CGLIB$test$0()V
,所以实际上的结果也就是调用了原始方法。但是这个原始方法仍然是在代理对象内部执行的,所以如果存在内部调用的话,调用的仍然是代理对象的内部方法,所以切面依然生效!
3. 调用原方法只有这一种方式吗?
当然不是!!
MethodProxy.invoke()
我们看一下MethodProxy类的方法,就会发现其中有一个方法叫invoke方法。
// net.sf.cglib.proxy.MethodProxy#invoke
// 调用未被代理类对象的方法
// obj绝对不能使用代理对象,否则会因为无限递归导致栈溢出
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
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;
}
}
我们看一下这个方法的代码就会发现它跟invokeSuper()大同小异,无非就是使用绑定原始类的FastClass对象f1,方法签名是代理方法签名test()。接下来我们看下f1的源码
public class Main$Test$$FastClassByCGLIB$$e2977a20 extends FastClass {
// 根据方法签名获取索引
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1422510685:
if (var10000.equals("test()V")) {
return 0;
}
break;
case -379268499:
if (var10000.equals("innerTest()V")) {
return 1;
}
break;
return -1;
}
// 调用方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
// 传入的对象只要是任何一个Test的子类即可
Test var10000 = (Test)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.test();
return null;
case 1:
var10000.innerTest();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
}
从f1的源码中我们可以看出,这里没有涉及到任何因为代理而生成的方法,例如使用CGLIB$
前缀的一系列方法,invoke方法传入的对象只需要是Test的任何一个子类对象即可,但是绝对不能传入我们生成的代理类,假如我们传入生成的代理类proxy的话,proxy的test()方法实际是被代理过的,最终执行又会使用MethodProxy.invoke()方法从而形成无限递归造成Stack over flow。正确的方式应该是传入一个Test对象或Test子类对象,如下。
// 创建正常test对象
Test originalTest = new Test();
new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
long cost = System.currentTimeMillis();
// 调用时使用正常test对象
Object result = methodProxy.invoke(originalTest, args);
System.out.println("method " + method.getName() + " cost time :" + (System.currentTimeMillis() - cost));
return result;
}
}
Method.invoke
我们仔细观察下intercept方法,里面传入的参数中有原始方法的Method对象,那我们同样只需要构造一个正常的Test对象就能调用了。
// 创建正常test对象
Test originalTest = new Test();
new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
long cost = System.currentTimeMillis();
// 调用时使用正常test对象
Object result = method.invoke(originalTest);
System.out.println("method " + method.getName() + " cost time :" + (System.currentTimeMillis() - cost));
return result;
}
}
总结
除了使用MethodProxy.invokeSuper()方法可以不创建任何对象的调用原始类方法之外,还可以通过MethodProxy.invoke()和传入的原始方法Method.invoke()来调用原始方法,但是后两种调用方式就类似于jdk的动态代理了,都需要将原始方法的执行托管给一个没有经过代理的对象,也就同样都存在这jdk动态代理的内部调用切面无法生效的问题,以上两种方法的执行结果均是:
test
inner test
method test cost time :1002
总结
至此我们已经了解了cglib如何实现动态代理的,总结来说就是以下步骤
- 通过生成字节码创建原始类的一个子类作为代理类,原来父类中所有方法的实现均托管给net.sf.cglib.proxy.MethodInterceptor对象
- 在net.sf.cglib.proxy.MethodInterceptor对象中我们可以通过以下三种方式调用原始类的方法
- 使用MethodProxy.invokeSuper()方法,该方法会将原始方法的执行重新托管给代理类,所以即使是内部调用,内部方法的切面增强代码依然生效
- 使用MethodProxy.invoke()方法,该方法将原始方法的执行托管给未被代理的对象,若托管给代理类则会造成栈溢出,仍然存在内部调用切面失效问题
- 使用Method.invoke()方法,与2相同
接着我们来回答一下开头的几个问题。
- 首先cglib的动态代理一定不会出现内部调用失效的问题吗?不一定,这取决于调用原始方法时是采用哪种方式,实际上spring-aop中使用cglib生成代理类时调用的就是MethodProxy.invoke()方法,spring中类即使没有实现接口(没有实现接口spring会选择由cglib进行代理),也仍然是会存在内部调用切面失效问题。为什么spring这么设计,我猜大概是为了跟jdk动态代理的效果保持一致吧,不能同样是spring生成的代理类,一部分存在内部调用问题,一部分不存在吧,不太合理。
// org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)
// 使用cglib获取代理类的方法入口
public Object getProxy(@Nullable ClassLoader classLoader) {
// 省略一大堆代码
......
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
// 设置回调函数
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return createProxyClassAndInstance(enhancer, callbacks);
}
// 方法org.springframework.aop.framework.CglibAopProxy#getCallbacks中返回的一系列回调函数,
// 随便挑一两个看就能看到其中调用的MethodProxy.invoke()方法
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object target = null;
// 获取目标对象,这个对象并没有被代理
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 调用invoke方法,使用未被代理对象作为目标对象
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
}
- 第二个问题,cglib动态代理,一个原始类只会生成一个代理类(这里把cglib直接生成字节码然后再加载到JVM中的都叫做代理类)吗?不是,首先代理类一旦被创建时需要被缓存的,而生成缓存key对象实际上也是生成了一个代理类对象,还记得我们一开始的createHelper()方法中的这段代码吗?这段代码实际上生成了net.sf.cglib.proxy.Enhancer.EnhancerKey接口的一个代理类对象。
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
其次,代理类调用原始类方法时如果采用的是MethodProxy的方法,则初次调用时会生成两个FastClass抽象类的代理对象,一个绑定原始类,一个绑定代理类。而每个被代理的方法实际上都会创建一个MethodProxy对象,所以一个代理类有n个方法被调用过,就会生成2*n个FastClass的代理对象。
所以使用cglib进行动态代理,生成的代理类绝不止一个,而jdk的动态代理,则确确实实只会生成一个代理类。使用cglib进行动态代理会生成大量的代理类,Class对象都是存储在JVM的方法区的,Class对象的卸载也比较严格,所以使用cglib进行动态代理会增大方法区的存储压力。
- 同时最后一个问题,为什么cglib比jdk动态代理要慢?方法初次调用都会生成两个FastClass的代理类,然后加载到JVM中并实例化,这些开销加上自然要比Jdk动态代理要高。
cglib动态代理与jdk动态代理有相同的地方,也有不同的地方,cglib生成更多的代理类,但是也实现了jdk动态代理无法实现的对类进行代理的功能,二者并无孰优孰劣,也许像spring-aop那样将二者的优势结合在一起的应用才是他们二者发挥最大价值的途径吧。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/179589.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...