新聞中心
今天我們來(lái)思考這樣一個(gè)問(wèn)題:在 Spring AOP 中,被代理的對(duì)象是單例的嗎?當(dāng)我們每次獲取到代理對(duì)象的時(shí)候,都會(huì)重新獲取一個(gè)新的被代理對(duì)象嗎?還是被代理的對(duì)象始終是同一個(gè)?

10余年專(zhuān)注成都網(wǎng)站制作,企業(yè)網(wǎng)站設(shè)計(jì),個(gè)人網(wǎng)站制作服務(wù),為大家分享網(wǎng)站制作知識(shí)、方案,網(wǎng)站設(shè)計(jì)流程、步驟,成功服務(wù)上千家企業(yè)。為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì)及定制高端網(wǎng)站建設(shè)服務(wù),專(zhuān)注于企業(yè)網(wǎng)站設(shè)計(jì),高端網(wǎng)頁(yè)制作,對(duì)火鍋店設(shè)計(jì)等多個(gè)方面,擁有豐富的營(yíng)銷(xiāo)推廣經(jīng)驗(yàn)。
為什么要思考這個(gè)問(wèn)題,因?yàn)樵谒筛缃酉聛?lái)要講的 @Scope 注解高級(jí)用法中涉及到這個(gè)知識(shí)點(diǎn)。
1. 問(wèn)題呈現(xiàn)
假設(shè)我有如下一個(gè)計(jì)算器接口:
public interface ICalculator {
void add(int a, int b);
int minus(int a, int b);
}然后給這個(gè)接口提供一個(gè)實(shí)現(xiàn)類(lèi):
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;
}
}現(xiàn)在假設(shè)我要生成一個(gè)代理對(duì)象,利用編程式的方式,代碼如下:
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+" 方法開(kāi)始執(zhí)行了。。。");
Object proceed = invocation.proceed();
System.out.println(name+" 方法執(zhí)行結(jié)束了。。。");
return proceed;
}
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.add(3, 4);這里幾個(gè)方法應(yīng)該都好理解:
- setTarget 方法是設(shè)置真正的被代理對(duì)象。這個(gè)在我們之前的 @Lazy 注解為啥就能破解死循環(huán)?一文中大家已經(jīng)接觸過(guò)了。
- addInterface,基于 JDK 的動(dòng)態(tài)代理是需要有接口的,這個(gè)方法就是設(shè)置代理對(duì)象的接口。
- addAdvice 方法就是添加增強(qiáng)/通知。
- 最后通過(guò) getProxy 方法獲取到一個(gè)代理對(duì)象然后去執(zhí)行。
最終打印結(jié)果如下:
圖片
這是一個(gè)簡(jiǎn)單的 AOP 案例。
現(xiàn)在我們的問(wèn)題在于 setTarget 方法上。
我們點(diǎn)進(jìn)來(lái)到 setTarget 方法上看一下這個(gè)方法做了什么:
public void setTarget(Object target) {
setTargetSource(new SingletonTargetSource(target));
}小伙伴們看到,setTarget 方法內(nèi)部調(diào)用了 setTargetSource 方法,這個(gè)方法設(shè)置了一個(gè) SingletonTargetSource 來(lái)作為 targetSource,從名字上就能看出來(lái),這個(gè) SingletonTargetSource 是一個(gè)單例的 targetSource。
因此,對(duì)于上面的代碼,我們可以推斷,多個(gè)不同的代理對(duì)象中持有的相同的被代理對(duì)象,例如下面這段代碼:
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+" 方法開(kāi)始執(zhí)行了。。。");
Object proceed = invocation.proceed();
System.out.println(name+" 方法執(zhí)行結(jié)束了。。。");
return proceed;
}
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
ICalculator calculator2 = (ICalculator) proxyFactory.getProxy();
calculator2.add(2, 3);我們分別獲取了 calculator 和 calculator2 兩個(gè)代理對(duì)象,但是實(shí)際上,這兩個(gè)代理對(duì)象中持有的是同一個(gè)被代理對(duì)象,如下圖:
圖片
從這張圖可以看出,代理對(duì)象不是同一個(gè),但是被代理對(duì)象其實(shí)是同一個(gè)。
2. TargetSource
在 Spring AOP 中,否則處理代理對(duì)象的接口是 TargetSource,TargetSource 有諸多實(shí)現(xiàn)類(lèi),不同實(shí)現(xiàn)類(lèi)具備不同的能力:
圖片
很多實(shí)現(xiàn)類(lèi)單純從名字上就能看出來(lái)其特點(diǎn)了。
我們先來(lái)看下 TargetSource 接口:
public interface TargetSource extends TargetClassAware {
@Override
@Nullable
Class> getTargetClass();
boolean isStatic();
@Nullable
Object getTarget() throws Exception;
void releaseTarget(Object target) throws Exception;
}這個(gè)接口一共是四個(gè)方法:
- getTargetClass:這個(gè)是返回被代理對(duì)象的類(lèi)型。
- isStatic:這個(gè)方法判斷被代理對(duì)象是否是不變的,也可以理解為返回被代理對(duì)象是否是單例的,不過(guò)這個(gè)方法并不控制單例的實(shí)現(xiàn),這個(gè)方法存在意義在于,如果該方法返回 true,表示被代理的對(duì)象是單例的,那么將來(lái)就不用調(diào)用 releaseTarget 方法去釋放對(duì)象,反之,如果這個(gè)方法返回 false,表示被代理的對(duì)象不是單例的,那么就需要在使用完被代理的對(duì)象之后,調(diào)用 releaseTarget 方法將之釋放掉。
- getTarget:這個(gè)方法就是返回被代理對(duì)象。
- releaseTarget:釋放被代理的對(duì)象。
TargetSource 的實(shí)現(xiàn)類(lèi)比較多,我們來(lái)看幾個(gè)典型的實(shí)現(xiàn)類(lèi)。
2.1 SingletonTargetSource
先來(lái)看這個(gè)類(lèi)的定義:
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;
}
}如果被代理的對(duì)象是單例的,那么我們就會(huì)選擇使用 SingletonTargetSource,被代理的對(duì)象總是在 getTarget 方法中被調(diào)用,然而這個(gè)方法返回的總是同一個(gè)對(duì)象,所以最終被代理的對(duì)象就是單例的。
同時(shí),由于被代理對(duì)象是單例的,因此 isStatic 方法返回 true,releaseTarget 中不需要額外操作。
2.2 SimpleBeanTargetSource
SimpleBeanTargetSource 比較典型,這個(gè)是每當(dāng)需要的時(shí)候,就去 Spring 容器中查找相應(yīng)的被代理的 Bean,至于這個(gè)被代理的 Bean 是否為單例,就由 Spring 容器來(lái)控制了:
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 在使用的時(shí)候,需要傳入 targetBeanName,也就是被代理的 bean 名稱(chēng),還需要傳入 Spring 容器 BeanFactory,這樣,在每次需要被代理對(duì)象的時(shí)候去調(diào)用 getTarget 方法的時(shí)候,就直接從容器中查詢(xún)出來(lái)目標(biāo) Bean。因此,被代理的對(duì)象到底是不是單例,就要看 Spring 容器返回的對(duì)象到底是不是單例!
小伙伴們要記著 SimpleBeanTargetSource 的特點(diǎn),因?yàn)樵谙乱黄恼轮?,松哥要和大家聊?@Scope 注解的高級(jí)用法,就涉及到這一點(diǎn)了。
2.3 LazyInitTargetSource
LazyInitTargetSource 有點(diǎn)類(lèi)似于 SimpleBeanTargetSource,也是從 Spring 容器中查找被代理的 Bean,不同的是,LazyInitTargetSource 具備延遲初始化的能力,也就是在第一次進(jìn)行調(diào)用的時(shí)候才會(huì)去獲取被代理對(duì)象:
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) {
}
}好啦,其他的類(lèi)我就不挨個(gè)說(shuō)了,感興趣的小伙伴可以自行查看,這一塊的源碼還是比較好理解的~
分享標(biāo)題:SpringAOP中被代理的對(duì)象一定是單例嗎?
當(dāng)前地址:http://www.fisionsoft.com.cn/article/djpcjhj.html


咨詢(xún)
建站咨詢(xún)
