Spring AOP源碼解析——專治你不會看源碼的壞毛病!

第一,要形成的習(xí)慣:

1.有空時隔一段時間要做幾道算法題,C語言和JAVA都可以,主要是訓(xùn)練思維。

2.定期閱讀spring的源碼。因為spring是框架,重設(shè)計,能夠培養(yǎng)大局觀

3.閱讀底層的書籍,如linux方面,虛擬機方面,這是內(nèi)功。越高級的語言只是招式。

4.不要忘記做了一半的東西,如搜索引擎方面,redis方面,可以過一段時間再做,因為到時候自己的境界有提升,深入程度也會有所增加。


第二,進入正題,了解原理,再看源碼。

1.看源碼看熟了,以后再遇到問題,就可以通過源碼去了解原理了。

2.spring的AOP,原理懂了,代碼相當(dāng)簡單。這也是為什么我記得我還是個菜鳥的時候,面試人家經(jīng)常問我這個。

3.先有個大局觀,畫張整體的spring結(jié)構(gòu)圖。

? ? 以下是備受吐槽的手繪時間:

(如果你覺得我左手字寫的實在是不能再難看了的話,我有空可以展示一下右手字。)

AOP面向切面編程是面向?qū)ο蟮难a充。它利用一種橫切技術(shù),將一些公共行為封裝成叫做“方面”的可重用模塊,解耦,增加可維護性。

AOP將系統(tǒng)分為核心關(guān)注點和橫切關(guān)注點兩部分。核心關(guān)注點就是主業(yè)務(wù)流程,橫切關(guān)注點就是上面提到的“方面”。那么看AOP的源碼就是要看橫切關(guān)注點是怎樣和核心關(guān)注點整合來發(fā)揮作用的。

主業(yè)務(wù)流程歸根到底是一個java方法,而且是對象的方法。

在AOP中被稱為被通知或被代理對象POJO。AOP的作用就是將核心關(guān)注點和橫切關(guān)注點組合起來,術(shù)語叫做“增強”。最后實際用的是增強后的代理對象。

對核心關(guān)注點進行增強就涉及到在哪些地方增強的問題。如方法調(diào)用或者異常拋出時做增強這些時機叫做連接點Joinpoint。一個通知將被引發(fā)的連接點集合叫做切入點,理解時就可以想正則表達(dá)式,通配符來指定多個,而不是單單一個連接點。

在連接點都做了哪些增強呢?增強的內(nèi)容AOP術(shù)語叫“通知”Advice。

Spring里定義了四種Advice:BeforeAdvice,AfterAdvice,ThrowAdvice,DynamicIntroducationAdvice。

許多AOP框架包括spring都是以攔截器作為通知模型。維護一個圍繞連接點的攔截器鏈。其中DynamicIntroducationAdvice是可以引入方法或者字段到核心關(guān)注點。

這里有個Introduction,AOP術(shù)語叫引入。將增強后的AOP代理組裝到系統(tǒng)叫做織入。

上面就是AOP的核心概念了??偨Y(jié)一下:

AOP要做的事情就是:生成代理對象,然后織入。

生成代理對象是經(jīng)常會被問到的一個問題:Spring提供了兩種方式來生成代理對象,JDKProxy和Cglib。

具體使用哪種方式由AopProxyFactory根據(jù)AdvisedSupport對象的配置來決定。

默認(rèn)的策略是如果目標(biāo)類是接口,則使用JDK動態(tài)代理技術(shù),否則使用Cglib來生成代理。

Cglib是基于字節(jié)碼技術(shù)的,使用的是ASM。asm是一個java字節(jié)碼操縱框架,它能被用來動態(tài)生成類或者增強既有類的功能。

ASM可以直接產(chǎn)生二進制class文件,也可以在類被加載入JVM之前動態(tài)改變類行為。

下面重點來看看JDK動態(tài)代理技術(shù)。這是我還是個很菜很菜的菜鳥時為數(shù)不多能看懂的源碼。因為之前看過Java設(shè)計模式,寫過類似的例子,所以會比較順暢。今天先講這一部分。

下面是調(diào)用測試類:


package?dynamic.proxy;?

import?java.lang.reflect.InvocationHandler;

import?java.lang.reflect.Method;

import?java.lang.reflect.Proxy;

/**

?*?實現(xiàn)自己的InvocationHandler

?*?@author?zyb

?*?@since?2012-8-9

?*

?*/

public?class?MyInvocationHandler?implements?InvocationHandler?{

?//?目標(biāo)對象?

?private?Object?target;

?/**

?*?構(gòu)造方法

?*?@param?target?目標(biāo)對象?

?*/

?public?MyInvocationHandler(Object?target)?{

?super();

?this.target?=?target;

?}

?/**

?*?執(zhí)行目標(biāo)對象的方法

?*/

?public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{

?//?在目標(biāo)對象的方法執(zhí)行之前簡單的打印一下

?System.out.println("------------------before------------------");

?//?執(zhí)行目標(biāo)對象的方法

?Object?result?=?method.invoke(target,?args);

?//?在目標(biāo)對象的方法執(zhí)行之后簡單的打印一下

?System.out.println("-------------------after------------------");

?return?result;

?}

?/**

?*?獲取目標(biāo)對象的代理對象

?*?@return?代理對象

?*/

?public?Object?getProxy()?{

?return?Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),?

?target.getClass().getInterfaces(),?this);

?}

}

package?dynamic.proxy;

/**

?*?目標(biāo)對象實現(xiàn)的接口,用JDK來生成代理對象一定要實現(xiàn)一個接口

?*?@author?zyb

?*?@since?2012-8-9

?*

?*/

public?interface?UserService?{

?/**

?*?目標(biāo)方法?

?*/

?public?abstract?void?add();

}

package?dynamic.proxy;?

/**

?*?目標(biāo)對象

?*?@author?zyb

?*?@since?2012-8-9

?*

?*/

public?class?UserServiceImpl?implements?UserService?{

?/*?(non-Javadoc)

?*?@see?dynamic.proxy.UserService#add()

?*/

?public?void?add()?{

?System.out.println("--------------------add---------------");

?}

}

package?dynamic.proxy;?

import?org.junit.Test;

/**

?*?動態(tài)代理測試類

?*?@author?zyb

?*?@since?2012-8-9

?*

?*/

public?class?ProxyTest?{

?@Test

?public?void?testProxy()?throws?Throwable?{

?//?實例化目標(biāo)對象

?UserService?userService?=?new?UserServiceImpl();

?//?實例化InvocationHandler

?MyInvocationHandler?invocationHandler?=?new?MyInvocationHandler(userService);

?//?根據(jù)目標(biāo)對象生成代理對象

?UserService?proxy?=?(UserService)?invocationHandler.getProxy();

?//?調(diào)用代理對象的方法

?proxy.add();

?}

}

執(zhí)行結(jié)果如下:

------------------before---------------

--------------------add---------------

-------------------after-----------------

很簡單,核心就是 invocationHandler.getProxy();這個方法調(diào)用的Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(), this); 怎么生成對象的。


/**

?*?Returns?an?instance?of?a?proxy?class?for?the?specified?interfaces

?*?that?dispatches?method?invocations?to?the?specified?invocation

?*?handler.

?*

?*?

{@code?Proxy.newProxyInstance}?throws

?*?{@code?IllegalArgumentException}?for?the?same?reasons?that

?*?{@code?Proxy.getProxyClass}?does.

?*

?*?@param?loader?the?class?loader?to?define?the?proxy?class

?*?@param?interfaces?the?list?of?interfaces?for?the?proxy?class

?*?to?implement

?*?@param?h?the?invocation?handler?to?dispatch?method?invocations?to

?*?@return?a?proxy?instance?with?the?specified?invocation?handler?of?a

?*?proxy?class?that?is?defined?by?the?specified?class?loader

?*?and?that?implements?the?specified?interfaces

?*?@throws?IllegalArgumentException?if?any?of?the?restrictions?on?the

?*?parameters?that?may?be?passed?to?{@code?getProxyClass}

?*?are?violated

?*?@throws?SecurityException?if?a?security?manager,?s,?is?present

?*?and?any?of?the?following?conditions?is?met:

?*?

?*?

?the?given?{@code?loader}?is?{@code?null}?and

?*?the?caller's?class?loader?is?not?{@code?null}?and?the

?*?invocation?of?{@link?SecurityManager#checkPermission

?*?s.checkPermission}?with

?*?{@code?RuntimePermission("getClassLoader")}?permission

?*?denies?access;

?*?

?for?each?proxy?interface,?{@code?intf},

?*?the?caller's?class?loader?is?not?the?same?as?or?an

?*?ancestor?of?the?class?loader?for?{@code?intf}?and

?*?invocation?of?{@link?SecurityManager#checkPackageAccess

?*?s.checkPackageAccess()}?denies?access?to?{@code?intf};

?*?

?any?of?the?given?proxy?interfaces?is?non-public?and?the

?*?caller?class?is?not?in?the?same?{@linkplain?Package?runtime?package}

?*?as?the?non-public?interface?and?the?invocation?of

?*?{@link?SecurityManager#checkPermission?s.checkPermission}?with

?*?{@code?ReflectPermission("newProxyInPackage.{package?name}")}

?*?permission?denies?access.

?*?

?*?@throws?NullPointerException?if?the?{@code?interfaces}?array

?*?argument?or?any?of?its?elements?are?{@code?null},?or

?*?if?the?invocation?handler,?{@code?h},?is

?*?{@code?null}

?*/

?@CallerSensitive

?public?static?Object?newProxyInstance(ClassLoader?loader,

?Class[]?interfaces,

?InvocationHandler?h)

?throws?IllegalArgumentException

?{

?Objects.requireNonNull(h);

?final?Class[]?intfs?=?interfaces.clone();

?final?SecurityManager?sm?=?System.getSecurityManager();

?if?(sm?!=?null)?{

?checkProxyAccess(Reflection.getCallerClass(),?loader,?intfs);

?}

?/*

?*?Look?up?or?generate?the?designated?proxy?class.

?*/

?Class?cl?=?getProxyClass0(loader,?intfs);

?/*

?*?Invoke?its?constructor?with?the?designated?invocation?handler.

?*/

?try?{

?if?(sm?!=?null)?{

?checkNewProxyPermission(Reflection.getCallerClass(),?cl);

?}

?final?Constructor?cons?=?cl.getConstructor(constructorParams);

?final?InvocationHandler?ih?=?h;

?if?(!Modifier.isPublic(cl.getModifiers()))?{

?AccessController.doPrivileged(new?PrivilegedAction()?{

?public?Void?run()?{

?cons.setAccessible(true);

?return?null;

?}

?});

?}

?return?cons.newInstance(new?Object[]{h});

?}?catch?(IllegalAccessException|InstantiationException?e)?{

?throw?new?InternalError(e.toString(),?e);

?}?catch?(InvocationTargetException?e)?{

?Throwable?t?=?e.getCause();

?if?(t?instanceof?RuntimeException)?{

?throw?(RuntimeException)?t;

?}?else?{

?throw?new?InternalError(t.toString(),?t);

?}

?}?catch?(NoSuchMethodException?e)?{

?throw?new?InternalError(e.toString(),?e);

?}

?}


這個代碼是JDK1.8中的,里面用到了1.8的一些語法,如果不太了解,建議先看看這本書。

代碼看著不少,實際上都在進行一些安全校驗,包裝之類的,真正有用的就兩句:

Class cl = getProxyClass0(loader, intfs);這句話查找或者生成代理類。跟進去:


/**

?*?Generate?a?proxy?class.?Must?call?the?checkProxyAccess?method

?*?to?perform?permission?checks?before?calling?this.

?*/

?private?static?Class?getProxyClass0(ClassLoader?loader,

?Class...?interfaces)?{

?if?(interfaces.length?>?65535)?{

?throw?new?IllegalArgumentException("interface?limit?exceeded");

?}

?//?If?the?proxy?class?defined?by?the?given?loader?implementing

?//?the?given?interfaces?exists,?this?will?simply?return?the?cached?copy;

?//?otherwise,?it?will?create?the?proxy?class?via?the?ProxyClassFactory

?return?proxyClassCache.get(loader,?interfaces);

?}


對,就是從緩存里把接口拿將出來。然后用return cons.newInstance(new Object[]{h}) 這一句將接口用invocationHandler進行包裝。

具體源碼可以跟進去看,不詳述。想必看到這里,JDK動態(tài)代理的原理都已經(jīng)很明白了。

這里要說一點理論性的東西:

AOP解決的問題往往可以用代理模式來解決。Java開發(fā)中常說動態(tài)代理和靜態(tài)代理,而AOP就是動態(tài)代理,因為代理的類是在運行時才生成的。

而一般說的代理模式寫成的代碼是編譯期就已經(jīng)生成的,叫靜態(tài)代理。

想繼續(xù)了解JAVA知識的,記得關(guān)注+關(guān)注+關(guān)注!


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容