美文网首页spring framework
AOP失效了...分析

AOP失效了...分析

作者: 名字是乱打的 | 来源:发表于2021-07-30 11:39 被阅读0次

技术来自于需求
组内在做性能测试,要知道每个方法调用时候,外层方法以及内部调用的每个子方法的耗时时长,第一时间呢就想到了在方法前后打印时间,然后做差值打印
但是里面的调用链比较长,然后写了好多好多差值打印代码...

基于上述情况呢,我就想着做个annotation给方法去使用,想打印方法执行时间的方法呢就把我的annotation加上去就行了
先说下额外的一个小选型废弃

  • 摒弃threadlocal记录时间,本来想直接用threadlocal记录,然后在全局返回体内拦截的适合去除时间即可,但是由于我们方法内许多方法都是可以异步进行提高性能的,用到了多线程,所以这个方案就废弃了
  • 最终方案呢 是用annotation增强我们的方法,将方法执行时间打印到mdc里,然后在全局拦截器(一个对方法返回值再封装的拦截器形如 m,d,e)里加了一个t (map结构),将我们的mdc关于时间打印的都放进去了

AOP失效了啥情况?

同一类里调用别的AOP方法,写下伪代码,类似下面代码

@PostMapping("/timeRecode")
    @TimeRecord
    public String timeRecode() throws InterruptedException {
        time1();
        TimeUnit.SECONDS.sleep(1);
        time2();
        return "ok";
    }

    @TimeRecord
    public void time1() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
    }

    @TimeRecord
    public void time2() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
    }

按照我的预期应该是三个方法都被拦截到的,但是实验证明只有timeRecode被拦截了,time1().time2()没被拦截,分析下,两者不同的是前者是外部方法,而后面两者是内部调用的.

原因分析:

Spring AOP采用代理的方式实现AOP,我们编写的横切逻辑被添加到动态生成的代理对象中,只要我们调用的是代理对象,则可以保证调用的是被增强的代理方法。而在代理对象中,不管你的横切逻辑是怎样的,也不管你增加了多少层的横切逻辑,有一点可以确定的是,你终归会调用目标对象的同一方法来调用原始的业务逻辑。
如果目标对象中的原始方法依赖于其他对象,那么Spring会注入所依赖对象的代理对象,从而保证依赖的对象的横切逻辑能够被正常织入。而一旦目标对象调用的是自身的其他方法时,问题就来了,这种情况下,目标对象调用的并不是代理对象的方法,故被调用的方法无法织入横切逻辑。

我们这里方法 A 被调用,是基于 AOP 生成的 代理对象 进行的调用;方法 B 调用方法 A ,是 this 目标对象 直接调用,并不是代理对象进行调用

解决方案

通过代理对象调用~

    1. 同一个类内方法互相调用可以拿到Spring给我们创建的代理,用代理调用就可以解决,解决如下:
final TestController proxy = (TestController) AopContext.currentProxy();
        proxy.time2();
    1. 采用注入的方式把要调用的bean先注入进来,再通过bean去调用,这也适用于同一个类,其实我们也可以把自己的bean注入到自己方法内使用.
//构造器注入其他bean
private final CacheManager cacheManager;
//annotation注入其他bean
@Autowired
private  Temp temp;
@Service
public class SomeService {
    //注入自己
    @Autowired
    private SomeService self
}

ps:

另在查阅资料时候发现有人的aop失效是因为没开启cglib,这里也提一下啊,想要使用aop,要做以下配置

  • 启动类上加@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true),开启cglib代理,为啥呢?
    Spring AOP通过动态代理实现的,不同场景下代理的支持

      1. 如果要切入的目标对象实现了接口,默认情况下Spring会直接采用JDK的动态代理的方式去实现AOP
    
      2. 如果要切入的目标对象实现了接口,也可以通过代码控制强制要求Spring使用CGLIB代理的方式去实现AOP
    
      3. 如果要切入的目标对象没有实现任何接口,那么此时Spring会自动采用CGLIB库,它会根据实际切入目标的情况,自动在JDK动态代理和CGLIB之间做切换,但是必须在项目中加入CGLIB需要的jar包,否则当Spring判定需要使用CGLIB的时候,而你没有引入CGLIB的jar包,此时就会报错,而当你引入CGLIB的jar包后,不需要额外做任何关于CGLIB配置,Spring直接就可以根据规则自动转换代理模式。
    

其他失效

  • (1) 在一个类内部调用时,被调用方法的 AOP 声明将不起作用。

  • (2) 对于基于接口动态代理的 AOP 事务增强来说,由于接口的方法都必然是 public ,这就要求实现类的实现方法也必须是 public 的(不能是 protected、private 等),同时不能使用 static 的修饰符。 所以,可以实施接口动态代理的方法只能是使用 public 或 public final 修饰符的方法,其他方法不可能被动态代理,相应的也就不能实施 AOP 增强,换句话说,即不能进行 Spring 事务增强了。

  • (3) 基于 CGLib 字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行 AOP 增强植入的
    由于使用 final、static、private 修饰符的方法都不能被子类覆盖,这些方法将无法实施 AOP 增强。所以方法签名必须特别注意这些修饰符的使用,以免使方法不小心成为事务管理的漏网之鱼。

相关文章

  • AOP失效了...分析

    技术来自于需求组内在做性能测试,要知道每个方法调用时候,外层方法以及内部调用的每个子方法的耗时时长,第一时间呢就想...

  • aop

    使用: spring aop使用简单示例 开启单个bean的代理,ProxyFactoryBean aop失效 a...

  • 内部调用引起Spring声明式事务@Transactional失

    失效的原因 Spring声明式事务是基于AOP生成的代理类来实现的,而AOP无法拦截内部调用,导致事务失效。 解决...

  • Spring事务什么时候会失效

    Spring事务什么时候会失效? Spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不...

  • K820-可靠性-失效分析

    15.失效分析 15.1 失效分析的概念 失效分析是 【1】判断产品失效的模式 【2】查找失效原因和机理 【3】提...

  • AOP失效问题

    AOP失效问题 前言 AOP我们知道,可以做成一个切面,我们可以把类里一些公共的行为放到AOP中。例如在AOP里...

  • kotlin aop 失效的问题

    当我们从java的aop转换到kotlin的aop时,会出现失效的问题。 java 的 Controller类 @...

  • Spring事务

    Spring AOP原理分析介绍了AOP的一些细节,有了这篇文章的基础,我们再来分析Spring中声明式事务的实现...

  • Spring 高级编程(第五版)-第五章-关于Spring的AO

    第五章 Spring AOP 概览 本章首先是脱离Spring之外讲了AOP的基础,其次分析了AOP的两种类型:...

  • spring 事务管理

    基于aop, 实现事务管理 service层的第一个非事务方法调用事务方法, 会导致事务失效. 由于aop 的实现...

网友评论

    本文标题:AOP失效了...分析

    本文链接:https://www.haomeiwen.com/subject/crffvltx.html