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


所有评论(0)