Spring AOP源碼剖析——跟進(jìn)中......

之前就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è)方法

  1. 將Advice包裝成一個(gè)PointCutAdvisor;
  2. 然后在添加增強(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)建代理通常有兩種方式:

  1. JDK動態(tài)代理;
  2. 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);
        }
    }
}

上述方法步驟:

  1. 如果攔截鏈不為空敦姻,則創(chuàng)建一個(gè)ReflectiveMethodInvocation;
  2. 調(diào)用其proceed方法;3
  3. proceed方法的調(diào)用會遞歸調(diào)用瘾境,直到所有的MethodInterceptor調(diào)用完
  4. 如果攔截鏈為空,直接調(diào)用目標(biāo)對象的方法镰惦。

未完跟進(jìn)中......

參考

https://juejin.im/post/591d8c8ba22b9d00585007dd

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迷守,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子旺入,更是在濱河造成了極大的恐慌盒犹,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眨业,死亡現(xiàn)場離奇詭異急膀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)龄捡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門卓嫂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人聘殖,你說我怎么就攤上這事晨雳。” “怎么了奸腺?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵餐禁,是天一觀的道長。 經(jīng)常有香客問我突照,道長帮非,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任讹蘑,我火速辦了婚禮末盔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘座慰。我一直安慰自己陨舱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布版仔。 她就那樣靜靜地躺著游盲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛮粮。 梳的紋絲不亂的頭發(fā)上益缎,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機(jī)與錄音蝉揍,去河邊找鬼链峭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛又沾,可吹牛的內(nèi)容都是我干的弊仪。 我是一名探鬼主播熙卡,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼励饵!你這毒婦竟也來了驳癌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤役听,失蹤者是張志新(化名)和其女友劉穎颓鲜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體典予,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甜滨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瘤袖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衣摩。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捂敌,靈堂內(nèi)的尸體忽然破棺而出艾扮,到底是詐尸還是另有隱情,我是刑警寧澤占婉,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布泡嘴,位于F島的核電站,受9級特大地震影響逆济,放射性物質(zhì)發(fā)生泄漏酌予。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一纹腌、第九天 我趴在偏房一處隱蔽的房頂上張望霎终。 院中可真熱鬧滞磺,春花似錦升薯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阅茶,卻和暖如春蛛枚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脸哀。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工蹦浦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撞蜂。 一個(gè)月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓盲镶,卻偏偏與公主長得像侥袜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子溉贿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記枫吧,整理的知識點(diǎn),也是為了防止忘記宇色,尊重勞動成果九杂,轉(zhuǎn)載注明出處哦!如果你也喜歡宣蠕,那...
    波波波先森閱讀 12,285評論 6 86
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理例隆,服務(wù)發(fā)現(xiàn),斷路器抢蚀,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,838評論 25 707
  • 等待是一種沒有限度的愛裳擎,等待著你的問候,等待著你的信息思币,等待著你的電話鹿响,等待也是一種煎熬…… 等待承載了太多的思念...
    榮耀啟程閱讀 288評論 0 2
  • 每天早出晚歸,跟孩子相處的時(shí)間僅有幾個(gè)小時(shí)谷饿,這兩天不斷使用林老師的方法惶我,讓我懂得和孩子的相處要學(xué)會接受同時(shí)也要不斷...
    英才日記閱讀 176評論 0 0