Spring AOP 中被代理的对象一定是单例吗?

发布时间:2025-05-19 00:19:24 作者:益华网络 来源:undefined 浏览量(1) 点赞(1)
摘要:来源:江南一点雨 今天我们来思考这样一个问题:在 Spring AOP 中,被代理的对象是单例的吗?当我们每次获取到代理对象的时候,都会重新获取一个新的被代理对象吗?还是被代理的对象始终是同一个? 为什么要思考这个问题,因为在松哥接下来

来源:江南一点雨

今天我们来思考这样一个问题:在 Spring AOP 中,被代理的对象是单例的吗?当我们每次获取到代理对象的时候,都会重新获取一个新的被代理对象吗?还是被代理的对象始终是同一个?

为什么要思考这个问题,因为在松哥接下来要讲的 @Scope 注解高级用法中涉及到这个知识点。

1. 问题呈现

假设我有如下一个计算器接口:

public interface ICalculator 

{

    void add(int a, int b)

;

    int minus(int a, int b)

;

}

然后给这个接口提供一个实现类:

public class CalculatorImpl implements ICalculator 

{

    @Override    public void add(int a, int b) 

{

        System.out.println(a + "+" + b + "="

 + (a + b));

    }

    @Override    public int minus(int a, int b) 

{

        return

 a - b;

    }

}

现在假设我要生成一个代理对象,利用编程式的方式,代码如下:

ProxyFactory proxyFactory = new

 ProxyFactory();

proxyFactory.setTarget(new

 CalculatorImpl());

proxyFactory.addInterface(ICalculator.class)

;

proxyFactory.addAdvice(new

 MethodInterceptor() {

    @Override    public Object invoke(MethodInvocation invocation) throws Throwable 

{

        Method method = invocation.getMethod();

        String name = method.getName();

        System.out.println(name+" 方法开始执行了。。。"

);

        Object proceed = invocation.proceed();

        System.out.println(name+" 方法执行结束了。。。"

);

        return

 proceed;

    }

});

ICalculator calculator = (ICalculator) proxyFactory.getProxy();

calculator.add(34

);

这里几个方法应该都好理解:

setTarget 方法是设置真正的被代理对象。这个在我们之前的 @Lazy 注解为啥就能破解死循环?一文中大家已经接触过了。addInterface,基于 JDK 的动态代理是需要有接口的,这个方法就是设置代理对象的接口。addAdvice 方法就是添加增强/通知。最后通过 getProxy 方法获取到一个代理对象然后去执行。

最终打印结果如下:

这是一个简单的 AOP 案例。

现在我们的问题在于 setTarget 方法上。

我们点进来到 setTarget 方法上看一下这个方法做了什么:

public void setTarget(Object target) 

{

 setTargetSource(new

 SingletonTargetSource(target));

}

小伙伴们看到,setTarget 方法内部调用了 setTargetSource 方法,这个方法设置了一个 SingletonTargetSource 来作为 targetSource,从名字上就能看出来,这个 SingletonTargetSource 是一个单例的 targetSource。

因此,对于上面的代码,我们可以推断,多个不同的代理对象中持有的相同的被代理对象,例如下面这段代码:

ProxyFactory proxyFactory = new

 ProxyFactory();

proxyFactory.setTarget(new

 CalculatorImpl());

proxyFactory.addInterface(ICalculator.class)

;

proxyFactory.addAdvice(new

 MethodInterceptor() {

    @Override    public Object invoke(MethodInvocation invocation) throws Throwable 

{

        Method method = invocation.getMethod();

        String name = method.getName();

        System.out.println(name+" 方法开始执行了。。。"

);

        Object proceed = invocation.proceed();

        System.out.println(name+" 方法执行结束了。。。"

);

        return

 proceed;

    }

});

ICalculator calculator = (ICalculator) proxyFactory.getProxy();

ICalculator calculator2 = (ICalculator) proxyFactory.getProxy();

calculator2.add(23

);

我们分别获取了 calculator 和 calculator2 两个代理对象,但是实际上,这两个代理对象中持有的是同一个被代理对象,如下图:

从这张图可以看出,代理对象不是同一个,但是被代理对象其实是同一个。

2. TargetSource

在 Spring AOP 中,否则处理代理对象的接口是 TargetSource,TargetSource 有诸多实现类,不同实现类具备不同的能力:

很多实现类单纯从名字上就能看出来其特点了。

我们先来看下 TargetSource 接口:

public interface TargetSource extends TargetClassAware 

{

 @Override @Nullable

 Class  getTargetClass();

 boolean isStatic()

;

 @Nullable Object getTarget() throws Exception

;

 void releaseTarget(Object target) throws Exception

;

}

这个接口一共是四个方法:

getTargetClass:这个是返回被代理对象的类型。isStatic:这个方法判断被代理对象是否是不变的,也可以理解为返回被代理对象是否是单例的,不过这个方法并不控制单例的实现,这个方法存在意义在于,如果该方法返回 true,表示被代理的对象是单例的,那么将来就不用调用 releaseTarget 方法去释放对象,反之,如果这个方法返回 false,表示被代理的对象不是单例的,那么就需要在使用完被代理的对象之后,调用 releaseTarget 方法将之释放掉。getTarget:这个方法就是返回被代理对象。releaseTarget:释放被代理的对象。

TargetSource 的实现类比较多,我们来看几个典型的实现类。

2.1 SingletonTargetSource

先来看这个类的定义:

public class SingletonTargetSource implements TargetSourceSerializable 

{

    @SuppressWarnings("serial"

)

 private final

 Object target;

 public SingletonTargetSource(Object target) 

{

  Assert.notNull(target, "Target object must not be null"

);

  this

.target = target;

 }

 @Override public

 Class  getTargetClass() {

  return this

.target.getClass();

 }

 @Override public Object getTarget() 

{

  return this

.target;

 }

 @Override public void releaseTarget(Object target) 

{

  // nothing to do

 }

 @Override public boolean isStatic() 

{

  return true

;

 }

}

如果被代理的对象是单例的,那么我们就会选择使用 SingletonTargetSource,被代理的对象总是在 getTarget 方法中被调用,然而这个方法返回的总是同一个对象,所以最终被代理的对象就是单例的。

同时,由于被代理对象是单例的,因此 isStatic 方法返回 true,releaseTarget 中不需要额外操作。

2.2 SimpleBeanTargetSource

SimpleBeanTargetSource 比较典型,这个是每当需要的时候,就去 Spring 容器中查找相应的被代理的 Bean,至于这个被代理的 Bean 是否为单例,就由 Spring 容器来控制了:

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource 

{

 @Override public Object getTarget() throws Exception 

{

  return

 getBeanFactory().getBean(getTargetBeanName());

 }

}

public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSourceBeanFactoryAwareSerializable 

{

 @Nullable private

 String targetBeanName;

 @Nullable private volatile

 Class  targetClass;

 @Nullable private

 BeanFactory beanFactory;

 public void setTargetBeanName(String targetBeanName) 

{

  this

.targetBeanName = targetBeanName;

 }

 public String getTargetBeanName() 

{

  Assert.state(this.targetBeanName != null"Target bean name not set"

);

  return this

.targetBeanName;

 }

 public void setTargetClass(Class<?> targetClass) 

{

  this

.targetClass = targetClass;

 }

 @Override public void setBeanFactory(BeanFactory beanFactory) 

{

  this

.beanFactory = beanFactory;

 }

 public BeanFactory getBeanFactory() 

{

  Assert.state(this.beanFactory != null"BeanFactory not set"

);

  return this

.beanFactory;

 }

 @Override @Nullable public

 Class  getTargetClass() {

  Class<?> targetClass = this

.targetClass;

  if (targetClass != null

) {

   return

 targetClass;

  }

  synchronized (this

) {

   targetClass = this

.targetClass;

   if (targetClass == null && this.beanFactory != null && this.targetBeanName != null

) {

    targetClass = this.beanFactory.getType(this

.targetBeanName);

    if (targetClass == null

) {

     Object beanInstance = this.beanFactory.getBean(this

.targetBeanName);

     targetClass = beanInstance.getClass();

    }

    this

.targetClass = targetClass;

   }

   return

 targetClass;

  }

 }

 @Override public boolean isStatic() 

{

  return false

;

 }

 @Override public void releaseTarget(Object target) throws Exception 

{

  // Nothing to do here.

 }

}

从上面这段源码中大家可以看到,SimpleBeanTargetSource 在使用的时候,需要传入 targetBeanName,也就是被代理的 bean 名称,还需要传入 Spring 容器 BeanFactory,这样,在每次需要被代理对象的时候去调用 getTarget 方法的时候,就直接从容器中查询出来目标 Bean。因此,被代理的对象到底是不是单例,就要看 Spring 容器返回的对象到底是不是单例!

小伙伴们要记着 SimpleBeanTargetSource 的特点,因为在下一篇文章中,松哥要和大家聊的 @Scope 注解的高级用法,就涉及到这一点了。

2.3 LazyInitTargetSource

LazyInitTargetSource 有点类似于 SimpleBeanTargetSource,也是从 Spring 容器中查找被代理的 Bean,不同的是,LazyInitTargetSource 具备延迟初始化的能力,也就是在第一次进行调用的时候才会去获取被代理对象:

public class LazyInitTargetSource extends AbstractBeanFactoryBasedTargetSource 

{

 @Nullable private

 Object target;

 @Override public synchronized Object getTarget() throws BeansException 

{

  if (this.target == null

) {

   this

.target = getBeanFactory().getBean(getTargetBeanName());

   postProcessTargetObject(this

.target);

  }

  return this

.target;

 }

 protected void postProcessTargetObject(Object targetObject) 

{

 }

}

好啦,其他的类我就不挨个说了,感兴趣的小伙伴可以自行查看,这一块的源码还是比较好理解的~

二维码

扫一扫,关注我们

声明:本文由【益华网络】编辑上传发布,转载此文章须经作者同意,并请附上出处【益华网络】及本页链接。如内容、图片有任何版权问题,请联系我们进行处理。

感兴趣吗?

欢迎联系我们,我们愿意为您解答任何有关网站疑难问题!

您身边的【网站建设专家】

搜索千万次不如咨询1次

主营项目:网站建设,手机网站,响应式网站,SEO优化,小程序开发,公众号系统,软件开发等

立即咨询 15368564009
在线客服
嘿,我来帮您!