spring代理內部方法不生效的原因

原生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使得內部方法調用代理生效

如何內部類代理生效

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章