• Spring AOP 中,被代理的对象一定是单例吗?
  • 发布于 2个月前
  • 203 热度
    0 评论
  • 小熊
  • 0 粉丝 30 篇博客
  •   
今天我们来思考这样一个问题:在 Spring AOP 中,被代理的对象是单例的吗?当我们每次获取到代理对象的时候,都会重新获取一个新的被代理对象吗?还是被代理的对象始终是同一个?

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

一. 问题呈现
假设我有如下一个计算器接口:
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) {
        // 堆代码 duidaima.com
        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(3, 4);
这里几个方法应该都好理解:
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(2, 3);
我们分别获取了 calculator 和 calculator2 两个代理对象,但是实际上,这两个代理对象中持有的是同一个被代理对象,如下图:

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

二. 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 TargetSource, Serializable {
    @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 TargetSource, BeanFactoryAware, Serializable {@
    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 容器返回的对象到底是不是单例!

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) {
 }

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