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

相关推荐
程序员岳焱22 分钟前
Java 与 MySQL 性能优化:Java 实现百万数据分批次插入的最佳实践
后端·mysql·性能优化
FrankYoou26 分钟前
Jenkins 与 GitLab CI/CD 的核心对比
java·docker
麦兜*1 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
KK溜了溜了1 小时前
JAVA-springboot 整合Redis
java·spring boot·redis
大只鹅1 小时前
解决 Spring Boot 对 Elasticsearch 字段没有小驼峰映射的问题
spring boot·后端·elasticsearch
ai小鬼头1 小时前
AIStarter如何快速部署Stable Diffusion?**新手也能轻松上手的AI绘图
前端·后端·github
天河归来1 小时前
使用idea创建springboot单体项目
java·spring boot·intellij-idea
weixin_478689762 小时前
十大排序算法汇总
java·算法·排序算法
码荼2 小时前
学习开发之hashmap
java·python·学习·哈希算法·个人开发·小白学开发·不花钱不花时间crud
IT_10242 小时前
Spring Boot项目开发实战销售管理系统——数据库设计!
java·开发语言·数据库·spring boot·后端·oracle