Spring Aop失效的情況及解决办法

本文详细介绍了Spring AOP在实际应用中遇到的四种常见问题:类或方法未被Spring管理、重复扫描、内部方法调用未被代理及注解方式未开启支持,并提供了相应的解决方案。强调了容器管理、方法调用路径、避免重复扫描和启用注解支持的重要性。同时,解释了AopContext.currentProxy()的工作原理,用于在代理对象内部获取当前代理引用。
摘要由CSDN通过智能技术生成

第一种(要增强的类或方法没被spring管理)

对于传统的web项目来说,通常使用spring和springmvc,因此对于这种项目来讲,他是有两个容器的,一个是spring容器,一般我们会把Service层的东西注入到spring容器中,另一个是springmvc的容器,通常这个容器里注入的是Controller层的东西,这里我们认为spring容器是父容器,springmvc是子容器的概念,然后我们大家都知道通过父子继承关系可知,子容器是可以读取到父容器中的东西,但是父容器是无法读到子容器中的内容,因此基于这个场景,有的同学,把Aop的实现类注入到了spring容器中,并且将Aop的切点表达式配置<aop:config> <aop:pointcut的execution也配到了spring容器的xml,而巧了这位同学要切的类方法,正好是Controller,也就是springmvc容器中的东西,那么这时候问题就来了,aop在初始化时会在自己的容器中寻找能够匹配的类方法,然后给他套上一层代理,此时他在自己能够访问到的spring容器中根本找不到与之匹配的类和方法,因为这些类和方法是在springmvc容器中管理的,因此就没有代理成功
解决那么对于上述问题要怎么修改呢?只需要确保你要Aop切的类和方法与你Aop配置切点aop:config <aop:pointcut的execution表达式声明是在同一个容器中即可,此时只需要将这个配置移到springmvc容器的xml中即可

第二种(要被增强的类或方法同时被spring和springMVC扫描)

第二种情况与第一种情况有些许的类似,但并不相同,是关于重复扫描的,比如你在spring容器中配置了一个Aop,并且把他托管给spring容器管理,而且execution表达式切的也是spring容器中管理的类和方法,理论上这个时候是好用的,这批execution切到的类都被加了代理,但是巧了,springmvc容器中由于配置的是包路径扫描,恰好把execution表达式切的这一批对象又扫了一遍,又都托管给了springmvc容器,而此时扫到的这批对象,是重新new出来交给springmvc管理的,因此并没有被aop代理,所以在使用时,注入进来的可能是springmvc容器管理的这批对象,因此使用时发现Aop代理失效了
解决:这个问题的解决方案,就是避免两个容器重复扫描。

第三种(要增强的方法被同类的方法调用)

第三个问题就比较简单了,他的现象是有些方法被Aop代理成功了,但有个别方法没有代理成功,究其原因发现这部分没有代理成功的方法并不是通过代理对象调用的,而是自身调用的,故被调用的方法没有被Aop代理,无法织入横切逻辑。
解决:举个栗子,比如A.a(),A.b()是被代理的类和方法,那么当我调用A.a()时,此时a被代理了,成功执行代理类的内容,但还没有完,a()方法中调用了自身的方法b(),此时我们以为b也会被代理类代理,但实际上并没有,因为他是自身方法调用了并不是通过代理类A调用的,在a方法中可以通过((A) AopContext.currentProxy()).b()调用,不过前提是配置上<aop:config expose-proxy="true">expose-proxy="true"是重点

AopContext原理

AopContext.currentProxy() 是 Spring AOP(面向切面编程)框架中的一个工具方法,它允许你在一个由 Spring AOP 创建的代理类中获取对当前代理对象的引用。这对于在代理对象内部需要调用其他被增强(或说被代理)的方法时特别有用,因为直接通过 this 引用调用方法可能会绕过 Spring AOP 的增强逻辑。

以下是 AopContext.currentProxy() 获取当前代理对象的原理概述:

  1. 代理对象的创建:当 Spring 容器启动并发现某个 bean 需要被 AOP 增强时,它会为这个 bean 创建一个代理对象。这个代理对象是一个实现了与原始 bean 相同接口的类,并且它会在调用方法时执行额外的 AOP 逻辑(如前置通知、后置通知、环绕通知等)。
  2. ThreadLocal 存储:在创建代理对象的过程中,Spring AOP 会将当前创建的代理对象存储在 ThreadLocal 变量中。ThreadLocal 是一个特殊的变量,它使得每个线程都有自己的一个独立的变量副本。在 AOP 的场景中,这是非常有用的,因为不同的线程可能会调用同一个代理对象的方法,而 ThreadLocal 可以确保每个线程都能够访问到自己的代理对象副本。
  3. AopContext.currentProxy():当你在代理对象内部调用 AopContext.currentProxy() 时,这个方法会检查当前线程的 ThreadLocal 变量,并返回存储在该变量中的代理对象。这样,你就可以在代理对象内部通过 AopContext.currentProxy() 获取到对自身的引用,从而调用其他被增强的方法而不绕过 AOP 的增强逻辑。

需要注意的是,AopContext.currentProxy() 只在由 Spring AOP 创建的代理对象内部有效,如如果由外部调用了被AOP注解的方法就会是代理对象。如果你在一个非代理对象或者由其他方式创建的代理对象内部调用这个方法,它可能会返回 null 或者不可预期的结果。

另外,为了使用 AopContext.currentProxy(),你需要在 Spring 的配置中启用对 AopContext 的支持。这通常是通过在启动类上添加 @EnableAspectJAutoProxy(exposeProxy = true) 注解来实现的。这个注解会告诉 Spring AOP 在创建代理对象时也将代理对象存储在 ThreadLocal 中,以便 AopContext.currentProxy() 能够访问到它

第四种(用的注解方式没有开启注解支持)

这种就更好理解了,只需要开启注解支持就行了。
xml方式开启在spring配置文件添加上<aop:aspectj-autoproxy/>即可
注解方式开启在启动类上添加@EnableAspectJAutoProxy即可

总结

第一种和第二种是因为MVC分层没有用好,接收数据和业务逻辑要分开,
第三种是因为代理模式限制
第四种就是使用不当了

参考: https://blog.csdn.net/sun5769675/article/details/95187532

  • 10
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring AOP 失效的原因可能有很多,以下是一些可能的原因: 1. 未配置正确的切面表达式:切面表达式定义了应该在哪些方法上应用切面。如果切面表达式不正确,就无法正确地匹配到目标方法,并且切面将无法生效。 2. 目标方法没有被代理:Spring AOP 使用动态代理来实现切面,在运行时生成代理对象,并将切面应用于代理对象上的目标方法。如果目标方法没有被代理,切面将无法生效。 3. 目标方法被直接调用而不是通过代理调用:如果目标对象的方法被其他部分直接调用,而不是通过生成的代理对象调用,那么切面将无法生效。 4. 切面的顺序问题:在一个应用中可能存在多个切面,每个切面都可以定义相同或不同的切面表达式。如果切面的顺序不正确,可能会导致切面失效,例如一个切面将目标方法拦截之后,另一个切面再次拦截该方法。 5. 使用错误的切面类型:Spring AOP 支持多种类型的切面,如前置通知、后置通知、异常通知等。如果使用了错误的切面类型,或者切面类型不匹配目标方法,切面将无法生效。 6. 目标方法没有被正确地扫描到:Spring AOP 使用自动扫描来发现目标方法,并将切面应用于这些方法上。如果目标方法没有被正确地扫描到,切面将无法生效。 总结来说,Spring AOP 失效的原因可能是由于切面表达式的问题、代理问题、调用方式问题、切面顺序问题、切面类型问题、以及扫描问题。为了确保切面生效,我们需要仔细检查配置,并理解Spring AOP的工作原理。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值