如何获取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面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处】即可免费获取

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

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot获取当前代理对象可以通过使用AOP的方式来实现。在Spring Boot中,可以使用@Aspect注解来定义切面类,使用@Around注解来定义切入点和执行逻辑。 首先,在一个类上使用@Aspect注解来标识这个类是一个切面类,然后在这个类中定义一个方法,使用@Around注解来标识这个方法是一个切入点。 在这个方法中,可以通过JoinPoint参数来获取当前执行的目标对象,然后可以通过ProxyUtils工具类的getTargetClass方法来获取目标对象的类名。然后可以根据需要对目标对象进行处理。 例如,定义一个切面类ProxyAspect: ``` @Aspect @Component public class ProxyAspect { @Around("@annotation(com.example.demo.Proxy)") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { // 获取当前执行的目标对象 Object target = joinPoint.getTarget(); // 获取目标对象的类名 String className = ProxyUtils.getTargetClass(target).getSimpleName(); // 处理目标对象 // 执行目标方法 return joinPoint.proceed(); } } ``` 然后在需要获取当前代理对象的地方,可以使用@Proxy注解来标识需要切入的方法,例如: ``` @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Proxy { } ``` 在目标类中的方法上使用@Proxy注解来标识需要切入的方法,例如: ``` @Service public class MyService { @Proxy public void doSomething() { // 业务逻辑 } } ``` 这样,在执行doSomething方法的时候,会触发切面类ProxyAspect中的around方法,通过JoinPoint参数获取当前被代理的目标对象,并对其进行处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值