原生CGLib内部方法互相调用时可以代理,但基于CGLib的Spring AOP却代理失效

这个问题是在工程中遇到的,想着写一个切面日志,然后用内部类调用了,结果发现没有生效,发现原因有2个:

第一:我的service 类没有实现接口,由于不是接口的实现的类,导致spring默认使用cglib代理

第二:cglib在spring实现中会保留proxybean和实际bean类,因此,内部方法调用的时候是使用了实际bean类的实例

首先我们先把cglib的代码写一下

public class CglibProxy implements MethodInterceptor
{
    // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
    public Object getProxy(Class<?> clazz)
    {
        Enhancer enhancer = new Enhancer();
        
        enhancer.setSuperclass(clazz);
        
        enhancer.setCallback(this);
        
        return enhancer.create();
    }

    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
    {
        // 这里增强
        System.out.println("收钱");

       // arg0是代理包装类 proxybean
        return arg3.invokeSuper(arg0, arg2);  

        //return arg3.invoke(arg0, arg2);
    }
}

 

如果 使用 return arg3.invoke(new Zhoujielun(), arg2); 就会发现内部调用的方法它没有实现代理,因为我们传入的是一个实际的bean类,自然调用的也就是bean类的方法,而不是proxybean的代理方法
而如果你使用下面的 return arg3.invoke(arg0, arg2); 作为intercept方法的返回值的话,你运行就会发现栈溢出,也就递归调用方法本身,为什么会这样呢,因为这里是arg0是 proxybean ,也就相当于 intercept-》 arg3.invoke(arg0, arg2);-》proxybean.welcome-》intercept,这样也就递归调用自己了,必然也就OOM

具体问题可以看spring的 cglib源码

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init();
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throw var4.getTargetException();
    }
}

public Object invoke(Object obj, Object[] args) throws Throwable {
    try {
        this.init();
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f1.invoke(fci.i1, obj, args);
    } catch (InvocationTargetException var4) {
        throw var4.getTargetException();
    } catch (IllegalArgumentException var5) {
        if (this.fastClassInfo.i1 < 0) {
            throw new IllegalArgumentException("Protected method: " + this.sig1);
        } else {
            throw var5;
        }
    }
}

这里可以看出invokeSuper方法 直接使用cglib的ci.f2.invoke(fci.i2, obj, args);这里 obj 就是proxybean 类代理对象,

而invoke 方法则是  return fci.f1.invoke(fci.i1, obj, args);   这里  fci.i1就是实际的bean 类,

具体递归OOM的问题看这篇博客https://blog.csdn.net/makecontral/article/details/79593732

而降到代理,spring有两种,动态代理和CGLIB代理,

经测试,jdk创建对象的速度远快于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略快于jdk, 所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。 如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。 pring默认使用jdk动态代理,如果类没有接口,则使用cglib。

原生CGLib内部方法互相调用时可以代理,但基于CGLib的Spring AOP却代理失效
原生CGLib代理类,相当于重写原生类方法,且只保留代理类的对象proxyBean,所有调用都走proxyBean,所以可以被代理。
Spring AOP 是 proxybean -> bean 所以无法拦截内部方法调用,Spring会保留原生类的对象bean以及代理类的对象proxyBean,
这样处理会导致内部方法调用时代理失效,传入的是原生类的对象bean,所以内部方法调用不可以被代理。

但是我们可以配置 spring aop使得内部方法调用代理生效

如何内部类代理生效

 

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐