如何获取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代理对象的全部内容,谢谢观看!!!

相关推荐
尚学教辅学习资料2 分钟前
基于SpringBoot的医药管理系统+LW示例参考
java·spring boot·后端·java毕业设计·医药管理
雷神乐乐18 分钟前
File.separator与File.separatorChar的区别
java·路径分隔符
小刘|23 分钟前
《Java 实现希尔排序:原理剖析与代码详解》
java·算法·排序算法
逊嘘42 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
morris1311 小时前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
monkey_meng1 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马1 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng1 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员1 小时前
java导出word文件(手绘)
java·开发语言·word