在Spring中,可以通过@Transactional或者@Cacheable注解,通过代理的形式快速的为某个方法实现事务或者缓存。但是当在类中,存在自己调用自己的带事务的方法时,会由于调用的是原始类而非代理类从而导致注解失效。这个时候我们可以使用AopContext.currentProxy()方法直接获取当前对象的代理类对象的办法来解决这个问题。
在使用AopContext.currentProxy()时,最常出现的问题是会报如下错误:
Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
原因可以查看源码:
public static Object currentProxy() throws IllegalStateException {
Object proxy = currentProxy.get();
if (proxy == null) {
throw new IllegalStateException("Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
} else {
return proxy;
}
}
可以发现,当获取不到代理对象时,就会抛出这个异常。解决办法也很简单,百度上的解决办法无非就2种:
1、使用SpringBoot的话,在启动类上添加注解:
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
2、使用xml配置的话,在xml配置文件上添加:
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
两种办法含义是一样的,其中
proxyTargetClass这个参数与Spring使用jdk进行代理还是cglib进行代理有关,这里暂不展开。
exposeProxy这个参数为true时,相当于一个开关,允许AopContext.currentProxy()的使用形式。
通过上述的配置,即可解决这个问题,但是我碰到了另外一种情况:
在老的代码上,使用了@Cacheable注解和AopContext.currentProxy(),在一次代码改动中,将方法上的@Cacheable注解去掉了,使用其他的形式实现,但是AopContext.currentProxy()没有去掉,导致一直在报错,但是expose-proxy明明已经配置了。
经过排查,其实问题就出现在,去掉了@Cacheable注解上,在@Cacheable注解还在的时候,会有代理类的存在的,但是我们把@Cacheable注解去掉之后,刚好这个类就不需要被代理了。所以我们通过AopContext.currentProxy()是肯定拿不到代理类的,而且由于会被异常信息中的提示给带偏,导致排查方向走偏,所以在这个方法发生异常报错的时候,也可以先排查下,这个类是否真的有代理类的存在。