spring aop 执行两次
问题
系统整合了shiro框架后,发现方法本体执行一次,aop执行两次!
经过研究,是因为系统中有两个代理创建器,对一个通知器(通知器包含切点和通知)生成两个代理类导致的。以下是研究过程。
基本配置
spring 基本配置如下
<context:component-scan base-package="testmaven.service"></context:component-scan><bean id="myinterceptor" class="testmaven.interceptor.MyInterceptor"></bean><aop:config proxy-target-class="false"><aop:pointcut expression="execution(* testmaven.service.*.*(..))" id="mypointcut"/><aop:advisor advice-ref="myinterceptor" pointcut-ref="mypointcut"/></aop:config><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
因为proxy-target-class=”false”,采用的是JDK代理,所以,打算将代理类生成到磁盘上,一探究竟。
为什么aop执行两次?
设置jvm参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
生成代理类后(com.sun.proxy、org.springframework.core文件夹需要手动创建),反编译观察
发现
$Proxy13与$Proxy14都是MyService的代理类!!!
观察sayHello方法
m3方法就是MyService的sayHello方法
此处的h是什么呢?是JDKDynamicAopProxy,它实现了InvocationHandler
$Proxy14与$Proxy13基本一样,这里不再展示。
通过debug查看线程栈再次验证了上述论证,线程中依次执行了Proxy14、Proxy13的sayHello方法
至此aop执行两次的原因找到了,因为产生了两个代理。
为什么方法本体执行了一次?
按照常理,有两个代理,方法本体会执行两次,为什么本体却只执行了一次呢?
这是因为
Proxy13代理了MyService,而Proxy14又代理了Proxy13!
这也解释了为什么proxy14比proxy13多实现一个serializable接口,因为java.lang.reflect.Proxy类实现了serializable接口,所以proxy14代理proxy13时,就需要实现serializable接口了。
等同于代码
proxy14.javapublic void sayHello(){super.h.invoke(target,args);//target就是$Proxy13}
proxy13.javapublic void sayHello(){super.h.invoke(target,args);//target就是MyServiceImpl}
JDKAopDynamicProxy.javapublic void invoke(){System.out.println("method before.....");//第一次,打印一次method before,此时target是$Proxy13,所以执行$Proxy13的sayHello//第二次,打印一次method before,此时target是MyserviceImpl,执行MyserviceImpl的sayHellomethod.invoke(target,args);}
proxy14的target是proxy13
Proxy13代理了MyService
综上所述,aop执行两次,方法本体执行一次
解决办法
去掉DefaultAdvisorAutoProxyCreator,让系统中,只存在一个代理创建器。修改后
<context:component-scan base-package="testmaven.service"></context:component-scan><bean id="myinterceptor" class="testmaven.interceptor.MyInterceptor"></bean><aop:config proxy-target-class="false"><aop:pointcut expression="execution(* testmaven.service.*.*(..))" id="mypointcut"/><aop:advisor advice-ref="myinterceptor" pointcut-ref="mypointcut"/></aop:config>
更深层次的原因
一个切面产生两个代理更深层次的原因是因为有两个代理创建器,AspectJAwareAdvisorAutoProxyCreator
、DefaultAdvisorAutoProxyCreator
<aop:config/>
产生了AspectJAwareAdvisorAutoProxyCreator
<aop:config/>
由AopNamespaceHandler处理
public class AopNamespaceHandler extends NamespaceHandlerSupport {/** * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}' * and '{@code scoped-proxy}' tags. */@Overridepublic void init() {// In 2.0 XSD as well as in 2.1 XSD.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());// Only in 2.0 XSD: moved to context namespace as of 2.1registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());}}
aop:config
是由ConfigBeanDefinitionParser处理的,一路追踪,最终到了
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {retu registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);}
可以看到<aop:config/>
创建了AspectJAwareAdvisorAutoProxyCreator
!
再加上配置文件中的DefaultAdvisorAutoProxyCreator
,就存在两个代理创建器,代理创建器会扫描配置文件中的advisor创建代理。所以对同一个切面,创建了两个代理。
总结
aop执行多次的原因是配置了多个代理创建器,多个代理创建器,产生了多个代理,代理2代理了代理1,代理1代理了本体,所以就产生了aop执行两次,本体方法执行一次的现象。
所以,系统中最好只有一个代理创建器,避免同一个通知器被创建多个代理的问题。
作者:wangjun5159
来源链接:https://blog.csdn.net/wangjun5159/article/details/51824171
版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。
2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。