如何获取Spring代理对象

作为一名面向Spring编程的开发攻城狮,Spring AOP是我们最常见、也是使用最频繁的特性之一。但是,如果使用不当就可能获取不到代理对象,从而可能会影响系统功能的使用,比如事务失效。本文将会介绍一下怎么获取Spring AOP代理对象。

示例

先看一下下面这段代码:

java 复制代码
public interface ProxyService {

    void proxy();

    void testProxy();
}
java 复制代码
@Service
public class ProxyServiceImpl implements ProxyService {

    @Override
    public void proxy() {
        System.out.println("ProxyServiceImpl.proxy 方法被调用");
    }

    @Override
    public void testProxy() {
        System.out.println("ProxyServiceImpl.testProxy 方法被调用");
        proxy();
    }
}

测试类:

java 复制代码
@SpringBootTest
public class ProxyApplicationTest {

    @Resource
    private ProxyService proxyService;

    @Test
    public void testProxy(){
        proxyService.testProxy();
    }
}

定义一个切面,用于验证获取代理对象是否生效:

java 复制代码
@Component
@Aspect
public class ProxyAspect {

    @Pointcut("execution(* site.suncodernote.proxy.ProxyService.proxy())")
    void proxyServicePointCut(){
    }

    @After("proxyServicePointCut()")
    public void jdkDivide(JoinPoint joinPoint){
        System.out.println("---------------------jdkProxyServicePointCut切面生效了----------------");
    }
}

运行测试类,你觉得结果会是什么?

结果就是这样,你没猜错。

text 复制代码
ProxyServiceImpl.testProxy 方法被调用
ProxyServiceImpl.proxy 方法被调用

为什么会这样呢?因为代理内部调用代理的其他方法时,直接调用可能会导致调用的是目标对象的方法,而不是经过增强的方法,所以也就没走切面。

获取代理对象

把当前类注入到当前类中

java 复制代码
@Service
public class ProxyServiceImpl implements ProxyService {
    
    @Resource
    private ProxyService proxyService;

这种方式也能达到目的,但是有点不太好。

使用ObjectFactory

ObjectFactory接口是一个对象工厂,其实现类通常用于延迟注入和按需获取对象实例,通过其getObject方法来创建和返回对象实例。

如果对象已经通过ObjectFactory创建了,ObjectFactory不会再次创建新的对象实例。ObjectFactory的主要功能是按需创建对象,但它会缓存已经创建的对象实例,以便后续请求时可以直接返回这些实例,而不是重新创建。

当使用ObjectFactory获取对象时,如果之前已经通过它获取并创建了对象实例,它会返回缓存的实例。这种行为确保了单例对象的唯一性和对象的重用,同时允许按需创建新的对象实例以支持延迟初始化和解决循环依赖问题。

下面看怎么使用它:

java 复制代码
@Service  
public class ProxyServiceImpl implements ProxyService {  
  
    @Resource  
    private ObjectFactory<ProxyService> objectFactory;  

    @Override  
    public void proxy() {  
        System.out.println("JDKProxyServiceImpl.proxy");  
    }  


    @Override  
    public void testProxy() {  
        System.out.println("JDKProxyServiceImpl.testProxy");  
        // proxy();  
        ProxyService proxyService = objectFactory.getObject();  
        proxyService.proxy();  
    }  
}

打印结果:

可以看到通过ObjectFactory也能获取到代理对象。

使用 AopContext.currentProxy()

AopContext.currentProxy()方法是Spring框架中的一个重要的工具方法,用于在运行时获取当前AOP代理对象。

下面具体看一下怎么使用它。 总共分2步:

  1. 在Springboot启动类或者配置类中开启暴露代理注解

默认情况下,Spring不会开启AopContext的支持,需要在配置中显式设置exposeProxy为true。

java 复制代码
@EnableAspectJAutoProxy(exposeProxy = true)

为什么默认情况下,Spring不会开启AopContext的支持呢?主要是考虑到性能问题。

下面是AopContext的源码:

java 复制代码
public final class AopContext {

   /**
    * ThreadLocal holder for AOP proxy associated with this thread.
    * Will contain {@code null} unless the "exposeProxy" property on
    * the controlling proxy configuration has been set to "true".
    * @see ProxyConfig#setExposeProxy
    */
   private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");


   private AopContext() {
   }



   /**
    * 获取当前代理对象
    */
   public static Object currentProxy() throws IllegalStateException {
      Object proxy = currentProxy.get();
      if (proxy == null) {
         throw new IllegalStateException(
               "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " +
                     "ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.");
      }
      return proxy;
   }

  
   @Nullable
   static Object setCurrentProxy(@Nullable Object proxy) {
      Object old = currentProxy.get();
      if (proxy != null) {
         currentProxy.set(proxy);
      }
      else {
         currentProxy.remove();
      }
      return old;
   }

}

可以看到,·当开启AopContext 的支持时,Spring需要在每次方法调用时维护一个当前代理对象的线程本地变量 currentProxy。这会引入额外的内存开销和执行时开销,因为需要动态地确定当前调用是否处于AOP代理的上下文中。

  1. 在方法中使用AopContext.currentProxy()

示例代码:

java 复制代码
@Override
public void testProxy() {
    System.out.println("ProxyServiceImpl.testProxy 方法被调用");
//        proxy();

    ProxyService proxyService = (ProxyService) AopContext.currentProxy();
    proxyService.proxy();
}

再次调用ProxyApplicationTest#testProxy() 单元测试方法,就可以看到控制台会打印切面中的方法了。

以上就是如何获取Spring代理对象的全部内容,谢谢观看!!!

相关推荐
xmh-sxh-131414 分钟前
jdk各个版本介绍
java
XINGTECODE27 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
天天扭码33 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶33 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺38 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序1 小时前
vue3 封装request请求
java·前端·typescript·vue
凡人的AI工具箱1 小时前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
陈王卜1 小时前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、1 小时前
Spring Boot 注解
java·spring boot
先天牛马圣体1 小时前
如何提升大型AI模型的智能水平
后端