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);
這裡幾個方法應該都好理解:
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 兩個代理物件,但是實際上,這兩個代理物件中持有的是同一個被代理物件,如下圖:
從這張圖可以看出,代理物件不是同一個,但是被代理物件其實是同一個。
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 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 容器返回的物件到底是不是單例!
小夥伴們要記著 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) {
}
}
好啦,其他的類我就不挨個說了,感興趣的小夥伴可以自行檢視,這一塊的原始碼還是比較好理解的~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2987754/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring事務管理與AOP代理的原理,預設的單例物件和代理物件銷燬的時機Spring單例物件
- 死磕Spring之AOP篇 - Spring AOP自動代理(三)建立代理物件Spring物件
- 死磕Spring之AOP篇 - Spring AOP兩種代理物件的攔截處理Spring物件
- 死磕Spring之AOP篇 - Spring AOP自動代理(一)入口Spring
- Spring AOP 自動建立代理Spring
- Spring AOP代理執行解析Spring
- Spring框架系列(10) - Spring AOP實現原理詳解之AOP代理的建立Spring框架
- 【Spring AOP】AOP 底層實現原理 —— 動態代理類的建立(JDK、CGlib)、工廠如何加工原始物件SpringJDKCGLib物件
- 從代理機制到Spring AOPSpring
- 3種代理模式-理解Spring Aop模式Spring
- Spring AOP --JDK動態代理方式SpringJDK
- Spring IoC 中的(Singleton)單例物件建立過程探索Spring單例物件
- 從動態代理到Spring AOP(中)Spring
- 從動態代理到Spring AOP(上)Spring
- 在spring中獲取代理物件代理的目標物件工具類Spring物件
- 死磕Spring之AOP篇 - Spring AOP自動代理(二)篩選合適的通知器Spring
- 您的單例模式,真的單例嗎?單例模式
- spring aop原理 JDK動態代理和CGLIB動態代理SpringJDKCGLib
- 【Spring】AOP的代理預設是Jdk還是Cglib?SpringJDKCGLib
- iOS單例物件iOS單例物件
- 淺析Spring中AOP的實現原理——動態代理Spring
- Spring AOP裡的靜態代理和動態代理,你真的瞭解嘛?Spring
- spring之AOP的概念及簡單案例Spring
- JAVA-Spring AOP基礎 - 代理設計模式JavaSpring設計模式
- AOP詳解之三-建立AOP代理後記,建立AOP代理
- Spring Aop 詳解一Spring
- Spring 的 AOPSpring
- 這一次搞懂Spring代理建立及AOP鏈式呼叫過程Spring
- GDPR正式生效!合規就一定是安全的嗎?
- spring中如何向一個單例bean中注入非單例beanSpring單例Bean
- MySQL 聚簇索引一定是主鍵嗎MySql索引
- Spring AOP就是這麼簡單啦Spring
- Spring 的Controller 是單例or多例SpringController單例
- 談談Spring中的物件跟Bean,你知道Spring怎麼建立物件的嗎?Spring物件Bean
- 你會單例嗎?單例
- Spring Boot實際專案用簡單的AOPSpring Boot
- 單例模式 – 單例登錄檔與 Spring 實現單例剖析單例模式Spring
- SimpleDateFormat一定是執行緒不安全嗎?ORM執行緒