今天我们来思考这样一个问题:在 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) { 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);
这里几个方法应该都好理解:
最终打印结果如下:
图片
这是一个简单的 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 两个代理对象,但是实际上,这两个代理对象中持有的是同一个被代理对象,如下图:
图片
从这张图可以看出,代理对象不是同一个,但是被代理对象其实是同一个。
在 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;}
这个接口一共是四个方法:
TargetSource 的实现类比较多,我们来看几个典型的实现类。
先来看这个类的定义:
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 中不需要额外操作。
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 容器返回的对象到底是不是单例!
小伙伴们要记着 SimpleBeanTargetSource 的特点,因为在下一篇文章中,松哥要和大家聊的 @Scope 注解的高级用法,就涉及到这一点了。
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) { }}
好啦,其他的类我就不挨个说了,感兴趣的小伙伴可以自行查看,这一块的源码还是比较好理解的~
本文链接:http://www.28at.com/showinfo-26-12767-0.htmlSpring AOP 中被代理的对象一定是单例吗?
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com