SpringAOP中,使用this调用方法的问题

场景问题

假设我们有一个核心支付类,其中有pay()支付功能,同时会通过record()方法记录都有哪些用户访问过这个核心功能。

随着业务的不断扩大,我们需要统计一下保存访问日志这个动作的耗时情况,看看是否会对核心支付功能有较大的影响。

所以,我们使用SpringAOP进行了切面处理。

切面类很简单,通过@Around方法对record方法进行切入切出,并记录该方法的执行时间。

看起来没什么问题

我们新建一个controller,看看我们的切面类有没有生效。

 

 启动服务,访问http://localhost:8080/demo/pay。

问题出现了。

按照上面的代码,在打印完业务日志之后,应该打印一行记录日志耗时的日志。然而控制台却空空如也,说明我们的切面类并没有生效。

问题就出现在pay()方法中的this调用上。

我们可以看到,图中的this指向的是一个普通的PayService对象,而不是被Spring增强后的bean。

而SpringAOP起作用的原理是什么:Spring通过JDK动态代理和CGlib代理对目标类生成一个代理类,在代理类中做功能增强。

我们看一下在controller中的PayService:

可以看到,在controller中的payService是一个被SpringCLlib增强后的代理类,而我们通过this引用到的,对于Spring来说只是一个普通的bean对象,自然无法实现AOP的功能。

那Spring在什么时候会对一个对象进行代理呢?

Spring会在一个bean创建的时候判断是否要进行代理,核心类是AnnotationAwareAspectJAutoProxyCreator,其本质是一个BeanPostProcessor。当需要使用到AOP时,它会把创建的原始的Bean对象wrap成代理对象作为Bean返回。

所以,最终结论是:只有被动态代理出来的对象,才可以被Spring增强,具备AOP的能力

解决办法

有两种办法。

一,自己引用自己

直接在当前类中注入自己,这样Spring会对类中的属性进行代理,生成一个payService代理类。

需要注意的是,这样其实是人为的制造了循环依赖。在高版本的Springboot中,循环依赖是默认关闭的。如果想开启循环依赖,需要配置spring.main.allow-circular-references=true。

二,通过AopContext

AopContext内部维护了一个保存proxy的ThreadLocal,简单说就是通过一个ThreadLocal将proxy和当前线程绑定起来,这样就可以随时拿出当前线程绑定的 Proxy。

如果使用这样的方式,需要在@EnableAspectJAutoProxy 里加一个配置项 exposeProxy = true。

/**
 * @author 戴着假发的程序员
 * 
 * @description
 */
@Component
public class MessageService {
    public String showMessage(String info) {
        System.out.println("OtherInfoServcie-showMessage展示信息:"+info);
        //使用AopContext的静态方法获取当前的代理对象
        ((MessageService)AopContext.currentProxy()).formartMsg(info);
        return null;
    }
    public String formartMsg(String info){
        System.out.println("OtherInfoServcie-formartMsg对象消息"+info+"进行格式化");
        return  info;
    }
}

总结

SpringAOP实际上会自动为我们创建一个Proxy,使得调用者能无感知地调用指定方法,本质上就是一个动态代理。我们只有访问这些代理对象的方法,才能获得AOP实现的功能,所以通过this引用是无法去正确使用 AOP 功能的。

猜你喜欢

转载自blog.csdn.net/qq_30436011/article/details/129654691