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(3, 4);

這裏幾個方法應該都好理解:

  1. setTarget 方法是設置真正的被代理對象。這個在我們之前的 [@Lazy 註解爲啥就能破解死循環?](https://mp.weixin.qq.com/s/GjI4xPGXscCGQtcalwnJKQ)一文中大家已經接觸過了。
  2. addInterface,基於 JDK 的動態代理是需要有接口的,這個方法就是設置代理對象的接口。
  3. addAdvice 方法就是添加增強/通知。
  4. 最後通過 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 兩個代理對象,但是實際上,這兩個代理對象中持有的是同一個被代理對象,如下圖:

從這張圖可以看出,代理對象不是同一個,但是被代理對象其實是同一個。

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;
}

這個接口一共是四個方法:

  1. getTargetClass:這個是返回被代理對象的類型。
  2. isStatic:這個方法判斷被代理對象是否是不變的,也可以理解爲返回被代理對象是否是單例的,不過這個方法並不控制單例的實現,這個方法存在意義在於,如果該方法返回 true,表示被代理的對象是單例的,那麼將來就不用調用 releaseTarget 方法去釋放對象,反之,如果這個方法返回 false,表示被代理的對象不是單例的,那麼就需要在使用完被代理的對象之後,調用 releaseTarget 方法將之釋放掉。
  3. getTarget:這個方法就是返回被代理對象。
  4. 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 &amp;&amp; this.beanFactory != null &amp;&amp; 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) {
	}

}

好啦,其他的類我就不挨個說了,感興趣的小夥伴可以自行查看,這一塊的源碼還是比較好理解的~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章