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

相关推荐
路在脚下@21 分钟前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
啦啦右一22 分钟前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
森屿Serien24 分钟前
Spring Boot常用注解
java·spring boot·后端
苹果醋31 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader2 小时前
深入解析 Apache APISIX
java·apache
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
菠萝蚊鸭2 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0072 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl