之前就Spring AOP的基本術(shù)語和原理及使用有了一定的認(rèn)識舰攒,作為Spring核心特性之一,AOP同樣很有必要重點(diǎn)掌握悔醋。這次沉淀將會開啟AOP源碼閱讀的序幕摩窃。
對于源碼的理解,以注釋添加在對應(yīng)代碼塊上方
一芬骄、AOP概念回顧
為什么會有面向切面編程(AOP)猾愿?眾所周知,Java是一個(gè)面向?qū)ο?OOP)的語言账阻,但它有一些弊端蒂秘,例如:當(dāng)我們需要為多個(gè)不具有繼承關(guān)系的對象引入一個(gè)公共行為,例如日志淘太、權(quán)限姻僧、事務(wù)规丽、性能監(jiān)測等功能時(shí),只能在在每個(gè)對象里引用公共行為撇贺,這樣做不便于維護(hù)赌莺,而且有大量重復(fù)代碼,通常我們更希望的是這些模塊可以實(shí)現(xiàn)熱插拔特性而且無需把外圍的代碼入侵到核心模塊中松嘶。
為了能夠更好地將系統(tǒng)級別的代碼抽離出來艘狭,去掉與對象的耦合,就產(chǎn)生了面向AOP(面向切面)翠订。
如上圖所示巢音,OOP屬于一種橫向擴(kuò)展,AOP是一種縱向擴(kuò)展蕴轨。AOP依托于OOP港谊,進(jìn)一步將系統(tǒng)級別的代碼抽象出來骇吭,進(jìn)行縱向排列橙弱,實(shí)現(xiàn)低耦合。
AOP的出現(xiàn)彌補(bǔ)了OOP的這點(diǎn)不足燥狰。假設(shè)現(xiàn)在我們把日志棘脐、權(quán)限、事務(wù)龙致、性能監(jiān)測等外圍業(yè)務(wù)看作單獨(dú)的關(guān)注點(diǎn)(也可以理解為單獨(dú)的模塊)蛀缝,每個(gè)關(guān)注點(diǎn)都可以在需要它們的時(shí)刻及時(shí)被運(yùn)用而且無需提前整合到核心模塊中。
二目代、源碼剖析
1. Demo
public static void main(String[] args) {
//創(chuàng)建代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
//設(shè)置目標(biāo)對象
proxyFactory.setTarget(new MyLogServiceImpl());
//前置增強(qiáng)
proxyFactory.addAdvice(new MyLogBefore());
//后置增強(qiáng)
proxyFactory.addAdvice(new MyLogAfter());
//從代理工廠中獲取代理
MyLogService myLogService = (MyLogService) proxyFactory.getProxy();
myLogService.log("x");
}
2. 源碼剖析
在demo中屈梁,直接獲取的是一個(gè)代理,不是要使用的實(shí)現(xiàn)類榛了,這是因?yàn)锳OP其實(shí)就是代理模式在讶,在編譯期或者運(yùn)行期,給我們原來的代碼增加一些功能霜大,成為一個(gè)代理构哺。當(dāng)我們調(diào)用的時(shí)候,實(shí)際就是調(diào)用的代理類战坤。
代碼中首先創(chuàng)建一個(gè)代理工廠實(shí)例:
ProxyFactory proxyFactory = new ProxyFactory();
代理工廠的作用就是使用編程的方式創(chuàng)建AOP代理曙强。ProxyFactory繼承自AdvisedSupport,AdvicedSupport是AOP代理的配置管理器途茫。
需要明白的是碟嘴,Spring中實(shí)現(xiàn)AOP,就是生成一個(gè)代理囊卜,然后在使用的時(shí)候調(diào)用代理臀防。
首先從方法proxyFactory.setTarget(new LoginServiceImpl())開始剖析源碼:
public void setTarget(Object target) {
/**首先根據(jù)給定的目標(biāo)實(shí)現(xiàn)類眠菇,創(chuàng)建一個(gè)單例的TargetSource
**然后設(shè)置TargetSource
*/
setTargetSource(new SingletonTargetSource(target));
}
類SingletonTargetSource實(shí)現(xiàn)接口TargetSource :
public interface TargetSource {
//返回目標(biāo)類的類型
Class getTargetClass();
/**查看TargetSource是否是static的
*靜態(tài)的TargetSource每次都返回同一個(gè)Target
*/
boolean isStatic();
//獲取目標(biāo)類的實(shí)例
Object getTarget() throws Exception;
//釋放目標(biāo)類
void releaseTarget(Object target) throws Exception;
}
類SingletonTargetSource定義如下:
public final class SingletonTargetSource implements TargetSource, Serializable {
//用來保存目標(biāo)類
private final Object target;
//構(gòu)造方法
public SingletonTargetSource(Object target) {
this.target = target;
}
//直接返回目標(biāo)類的類型
public Class getTargetClass() {
return target.getClass();
}
//返回目標(biāo)類
public Object getTarget() {
return this.target;
}
//釋放目標(biāo)類,代碼邏輯為空
public void releaseTarget(Object o) {
// Nothing to do
}
//是否為靜態(tài)袱衷,這里直接返回true
public boolean isStatic() {
return true;
}
//重寫equals方法
public boolean equals(Object other) {
//相等捎废,返回true
if (this == other) {
return true;
}
//如果不是SingletonTargetSource類型的返回false
if (!(other instanceof SingletonTargetSource)) {
return false;
}
SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
//判斷目標(biāo)類是否相等
return ObjectUtils.nullSafeEquals(this.target, otherTargetSource.target);
}
//重寫toString方法
public String toString() {
return "SingletonTargetSource: target=(" + target + ")";
}
}
在AdvisedSupport類中的設(shè)置目標(biāo)類setTargetSource方法:
public void setTargetSource(TargetSource targetSource) {
if (isActive() && getOptimize()) {
throw new AopConfigException("Can't change target with an optimized CGLIB proxy: it has its own target");
}
//將構(gòu)建的TargetSource緩存起來
this.targetSource = targetSource;
}
設(shè)置了要代理的目標(biāo)類之后,接下來就是添加通知致燥,即添加增強(qiáng)類登疗,proxyFactory.addAdvice()方法是添加增強(qiáng)類的方法。對應(yīng)demo中是:
//前置增強(qiáng)
proxyFactory.addAdvice(new MyLogBefore());
//后置增強(qiáng)
proxyFactory.addAdvice(new MyLogAfter());
addAdvice方法的參數(shù)是一個(gè)Advice類型的類嫌蚤,也就是通知或者叫增強(qiáng)辐益,這里先介紹有關(guān)通知Advice的代碼。
Advice接口
Advice不屬于Spring脱吱,是AOP聯(lián)盟定義的接口智政。Advice接口并沒有定義任何方法,是一個(gè)空的接口箱蝠,用來做標(biāo)記续捂,實(shí)現(xiàn)了此接口的的類是一個(gè)通知類。Advice有幾個(gè)子接口:
- BeforeAdvice宦搬,前置增強(qiáng)牙瓢,即在我們的目標(biāo)類之前調(diào)用的增強(qiáng),未定義任何方法间校;
- AfterReturningAdvice矾克,方法正常返回前的增強(qiáng),該增強(qiáng)可以看到方法的返回值憔足,但不能更改返回值胁附;
- ThrowsAdvice,拋出異常時(shí)候的增強(qiáng)滓彰,是一個(gè)標(biāo)志接口控妻,未定義任何方法;
- Interceptor找蜜,攔截器饼暑,未定義任何方法,表示一個(gè)通用的攔截器洗做。不屬于Spring弓叛;
- DynamicIntroductionAdvice,動態(tài)引介增強(qiáng)诚纸,有一個(gè)方法implementsInterface撰筷。
MethodBeforeAdvice接口,是BeforeAdvice的子接口畦徘,表示在方法前調(diào)用的增強(qiáng)毕籽。
public interface MethodBeforeAdvice extends BeforeAdvice {
/**在給定的方法調(diào)用前抬闯,調(diào)用該方法
*參數(shù)method是被代理的方法
*參數(shù)args是被代理方法的參數(shù)
*參數(shù)target是方法調(diào)用的目標(biāo),可能為null
*/
void before(Method m, Object[] args, Object target) throws Throwable;
}
我們來看看向代理工廠中添加增強(qiáng)的addAdvice方法关筒,addAdvice方法在AdvisedSupport類中:
public void addAdvice(Advice advice) throws AopConfigException {
/**advisors是Advice列表溶握,是一個(gè)LinkedList
*如果被添加進(jìn)來的是一個(gè)Interceptor,會先被包裝成一個(gè)Advice
*添加之前現(xiàn)獲取advisor的大小蒸播,當(dāng)做添加的Advice的位置
*/
int pos = (this.advisors != null) ? this.advisors.size() : 0;
//添加Advice
addAdvice(pos, advice);
}
未完跟進(jìn)中......(addAdvice(pos, advice)方法)
方法addAdvice(pos, advice)源碼:
//添加Advice
public void addAdvice(int pos, Advice advice) throws AopConfigException {
//只能處理已經(jīng)實(shí)現(xiàn)了AOP聯(lián)盟的接口的攔截器
if (advice instanceof Interceptor && !(advice instanceof MethodInterceptor)) {
throw new AopConfigException(getClass().getName() + " only handles AOP Alliance MethodInterceptors");
}
/**如果advice是IntroductionInfo接口類型睡榆,
*不需要IntroductionAdvisor
*/
if (advice instanceof IntroductionInfo) {
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
}
/**如果advice是動態(tài)引介增強(qiáng),
*需要IntroductionAdvisor
*/
else if (advice instanceof DynamicIntroductionAdvice) {
throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
}
else {
//添加增強(qiáng)器袍榆,需要先將增強(qiáng)包裝成增強(qiáng)器胀屿,然后進(jìn)行添加
addAdvisor(pos, new DefaultPointcutAdvisor(advice));
}
}
添加增強(qiáng)的過程:實(shí)際調(diào)用添加增強(qiáng)器這個(gè)方法
- 將Advice包裝成一個(gè)PointCutAdvisor;
- 然后在添加增強(qiáng)器包雀。
Advisor接口
Advisor宿崭,增強(qiáng)器,它持有一個(gè)增強(qiáng)Advice和一個(gè)過濾器才写,用來決定Advice的所使用的位置葡兑。
public interface Advisor {
//判斷Advice是否存在于每個(gè)實(shí)例中
boolean isPerInstance();
//返回當(dāng)下持有的Advice
Advice getAdvice();
}
PointcutAdvisor
PointcutAdvisor是一個(gè)持有Pointcut切點(diǎn)的增強(qiáng)器,擁有一個(gè)Advice和一個(gè)Pointcut琅摩。
public interface PointcutAdvisor extends Advisor {
//獲取Pointcut
Pointcut
}
Pointcut接口
即切入點(diǎn)铁孵,定義了哪些連接點(diǎn)需要被織入橫切邏輯锭硼。
public interface Pointcut {
//類過濾器房资,用于明確需要攔截的類
ClassFilter getClassFilter();
//方法匹配器,用于明確需要攔截的方法
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
ClassFilter接口
public interface ClassFilter {
//判斷所給的類是否需要攔截
boolean matches(Class clazz);
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
MethodMatcher接口
public interface MethodMatcher {
//靜態(tài)方法匹配
boolean matches(Method m, Class targetClass);
//是否是運(yùn)行時(shí)動態(tài)匹配
boolean isRuntime();
//運(yùn)行時(shí)動態(tài)匹配
boolean matches(Method m, Class targetClass, Object[] args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
了解了這些相關(guān)定義之后檀头,回到源碼邏輯的剖析轰异,讓我們來看看添加增強(qiáng)器方法addAdvisor的具體實(shí)現(xiàn):
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
//如果增強(qiáng)器是引介增強(qiáng)器
if (advisor instanceof IntroductionAdvisor) {
addAdvisor(pos, (IntroductionAdvisor) advisor);
}
else {
//其他的增強(qiáng)器處理
addAdvisorInternal(pos, advisor);
}
}
方法 addAdvisorInternal(pos, advisor)的源碼:
private void addAdvisorInternal(int pos, Advisor advice) throws AopConfigException {
if (isFrozen()) {
throw new AopConfigException("Cannot add advisor: config is frozen");
}
//把Advice添加到LinkedList中指定位置pos
this.advisors.add(pos, advice);
//同時(shí)更新Advisors數(shù)組
updateAdvisorArray();
//通知監(jiān)聽器
adviceChanged();
}
獲取代理
上述源碼是在組裝代理工廠,接下類我們繼而剖析代理的生成暑始,方法proxyFactory.getProxy()這一步就是獲取代理的過程:
public Object getProxy() {
//創(chuàng)建AOP代理
AopProxy proxy = createAopProxy();
//返回代理
return proxy.getProxy();
}
Spring中搭独,創(chuàng)建代理通常有兩種方式:
- JDK動態(tài)代理;
- CGLIB動態(tài)代理廊镜。
而這里的創(chuàng)建AOP代理就是生成這兩種代理中的一種牙肝。
protected synchronized AopProxy createAopProxy() {
if (!this.isActive) {
activate();
}
//獲取AOP代理工廠,然后創(chuàng)建代理
return getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
//判斷使用ObjenesisCglibAopProxy還是JdkDynamicAopProxy
/**如果代理的是類嗤朴,
*就使用CGLIB的方式來創(chuàng)建代理
*/
boolean useCglib = advisedSupport.getOptimize() || advisedSupport.getProxyTargetClass() || advisedSupport.getProxiedInterfaces().length == 0;
if (useCglib) {
return CglibProxyFactory.createCglibProxy(advisedSupport);
}
else {
/**如果代理的是接口配椭,
*就使用JDK動態(tài)代理來創(chuàng)建代理
*/
return new JdkDynamicAopProxy(advisedSupport);
}
}
JDK動態(tài)代理
看下JDK動態(tài)代理的方式,對于方法的調(diào)用雹姊,調(diào)用的是代理類的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation = null;
Object oldProxy = null;
boolean setProxyContext = false;
//所代理的目標(biāo)對象
TargetSource targetSource = advisedSupport.targetSource;
Class targetClass = null;
Object target = null;
try {
//equal股缸、hashCode 等方法
if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
}
else if (Advised.class == method.getDeclaringClass()) {
return AopProxyUtils.invokeJoinpointUsingReflection(this.advisedSupport, method, args);
}
Object retVal = null;
//代理目標(biāo)對象
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
if (this.advisedSupport.exposeProxy) {
// Make invocation available if necessary
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//獲取此方法的攔截鏈
List chain = this.advisedSupport.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this.advisedSupport, proxy, method, targetClass);
//如果沒有配置通知
if (chain.isEmpty()) {
//直接調(diào)用目標(biāo)對象的方法
retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
//如果配置了通知,創(chuàng)建一個(gè)方法調(diào)用
invocation = new ReflectiveMethodInvocation(proxy, target,
method, args, targetClass, chain);
//執(zhí)行通知鏈吱雏,沿著通知器鏈調(diào)用所有的通知
retVal = invocation.proceed();
}
if (retVal != null && retVal == target) {
//返回值為自己
retVal = proxy;
}
//返回
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
上述方法步驟:
- 如果攔截鏈不為空敦姻,則創(chuàng)建一個(gè)ReflectiveMethodInvocation;
- 調(diào)用其proceed方法;3
- proceed方法的調(diào)用會遞歸調(diào)用瘾境,直到所有的MethodInterceptor調(diào)用完
- 如果攔截鏈為空,直接調(diào)用目標(biāo)對象的方法镰惦。