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