当前位置: 首页 >Java技术 > spring aop 执行两次

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的代理类!!!

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

观察sayHello方法

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

m3方法就是MyService的sayHello方法

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

此处的h是什么呢?是JDKDynamicAopProxy,它实现了InvocationHandler

$Proxy14与$Proxy13基本一样,这里不再展示。

通过debug查看线程栈再次验证了上述论证,线程中依次执行了Proxy14、Proxy13的sayHello方法

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

至此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

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

Proxy13代理了MyService

spring aop 执行两次 _ JavaClub全栈架构师技术笔记

综上所述,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>

更深层次的原因

一个切面产生两个代理更深层次的原因是因为有两个代理创建器,AspectJAwareAdvisorAutoProxyCreatorDefaultAdvisorAutoProxyCreator
<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),我们将第一时间核实后及时予以删除。





本文链接:https://www.javaclub.cn/java/112614.html

标签:SpringAOP
分享给朋友:

“spring aop 执行两次” 的相关文章

SpringBoot之整合Mybatis篇 2022年05月15日 21:51:04
浅谈高性能web程序解决方案 2022年05月15日 21:54:15
常用锁原理的介绍(上) 2022年05月16日 18:33:06
Spring Cloud Feign 如何使用对象参数 2022年05月16日 18:38:07
SpringCloud简单实例 2022年05月16日 20:38:34
Java实现阶乘运算 2022年05月21日 11:37:18