spring aop 执行两次 您所在的位置:网站首页 一次两次的怎么写 spring aop 执行两次

spring aop 执行两次

2024-03-16 17:27| 来源: 网络整理| 查看: 265

问题

系统整合了shiro框架后,发现方法本体执行一次,aop执行两次! 经过研究,是因为系统中有两个代理创建器,对一个通知器(通知器包含切点和通知)生成两个代理类导致的。以下是研究过程。

基本配置

spring 基本配置如下

因为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.java public void sayHello(){ super.h.invoke(target,args);//target就是$Proxy13 } proxy13.java public void sayHello(){ super.h.invoke(target,args);//target就是MyServiceImpl } JDKAopDynamicProxy.java public void invoke(){ System.out.println("method before....."); //第一次,打印一次method before,此时target是$Proxy13,所以执行$Proxy13的sayHello //第二次,打印一次method before,此时target是MyserviceImpl,执行MyserviceImpl的sayHello method.invoke(target,args); }

proxy14的target是proxy13

这里写图片描述

Proxy13代理了MyService

这里写图片描述

综上所述,aop执行两次,方法本体执行一次

解决办法

去掉DefaultAdvisorAutoProxyCreator,让系统中,只存在一个代理创建器。修改后

更深层次的原因

一个切面产生两个代理更深层次的原因是因为有两个代理创建器,AspectJAwareAdvisorAutoProxyCreator、DefaultAdvisorAutoProxyCreator 产生了AspectJAwareAdvisorAutoProxyCreator 由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. */ @Override public 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.1 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); } }

aop:config是由ConfigBeanDefinitionParser处理的,一路追踪,最终到了

public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) { return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source); }

可以看到创建了AspectJAwareAdvisorAutoProxyCreator! 再加上配置文件中的DefaultAdvisorAutoProxyCreator,就存在两个代理创建器,代理创建器会扫描配置文件中的advisor创建代理。所以对同一个切面,创建了两个代理。

总结

aop执行多次的原因是配置了多个代理创建器,多个代理创建器,产生了多个代理,代理2代理了代理1,代理1代理了本体,所以就产生了aop执行两次,本体方法执行一次的现象。 所以,系统中最好只有一个代理创建器,避免同一个通知器被创建多个代理的问题。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有