手撕Spring AOP步氏,讓你成為完全理解AOP的作用

AOP基本概念

面向切面編程 [官網(wǎng)介紹]: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop
白話(huà)文:增強(qiáng)/加強(qiáng)一系列的類(lèi)/方法

定義幾個(gè)簡(jiǎn)單的接口

public interface ServiceForKTV {

    void kty();
}
public interface ServiceForSPA {

    void spa();
}

具體實(shí)現(xiàn)邏輯可以自己寫(xiě)

定義代理邏輯

public interface Advice {

    //定義一個(gè)方法
    //用戶(hù)提供增加邏輯
    /**
     *
     * @param target
     * @param method
     * @param args
     * @return
     */
    public Object invoke(Object target, Method method,Object[] args) throws Exception;
}
public class TimeCsAdvice implements Advice {
    @Override
    public Object invoke(Object target, Method method, Object[] args) throws Exception {
        long startTime = System.currentTimeMillis();
        Object ret = method.invoke(target,args);
        long endTime = System.currentTimeMillis();
        System.out.println("類(lèi)名【"+target.getClass().getName()+"】");
        System.out.println("方法名【"+method.getName()+"】");
        System.out.println("耗時(shí)【"+(endTime - startTime)+"】");
        return null;
    }
}

這里是統(tǒng)計(jì)接口耗時(shí)和打印一些參數(shù)

到這里的話(huà),基本上用戶(hù)的在邏輯都寫(xiě)完了,接下來(lái)就要完成一些框架需要干的事了

定義切入點(diǎn)和切入方法

@Data
public class Pointcut {

    private String classPattern;

    private String methodPattern;
}

java是能識(shí)別正則表達(dá)式的,這里我們用正則撒汉。

定義一個(gè)類(lèi)來(lái)封裝切點(diǎn)和Advice

public class Aspect {

    private Advice advice;

    private Pointcut pointcut;
}

定義AOP核心處理類(lèi)InvocationHandler

public class AopInvocationHandler implements InvocationHandler {

    private Aspect aspect;

    private Object bean;

    public AopInvocationHandler(Aspect aspect, Object bean) {
        this.aspect = aspect;
        this.bean = bean;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (bean.getClass().getName().matches(aspect.getPointcut().getMethodPattern())){
            return aspect.getAdvice().invoke(bean,method,args);
        }
        return method.invoke(bean,args);
    }
}

增強(qiáng)需要被代理的類(lèi)

構(gòu)造簡(jiǎn)易的Spring上下文對(duì)象

public interface IApplicationContext {

    Object getBean(String beanName) throws Exception;

    void registerBeanDefinition(String beanName,Class<?> beanClass) throws Exception;

    void setAspect(Aspect aspect);
}
public class MyApplicationContext implements IApplicationContext {

    private Map<String,Class<?>> beanMap = new ConcurrentHashMap<>();

    private Aspect aspect;

    @Override
    public Object getBean(String beanName) throws Exception {
        Object bean = createInstance(beanName);
        //返回一個(gè)增加的方法
        bean = proxyEnhance(bean);
        return bean;
    }

    private Object proxyEnhance(Object bean) {
        if (aspect != null && (bean.getClass().getName()).matches(aspect.getPointcut().getClassPattern()) ){
            return Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    new AopInvocationHandler(aspect,bean));
        }
        return bean;
    }

    private Object createInstance(String beanName) throws Exception {
        return beanMap.get(beanName).newInstance();
    }

    @Override
    public void registerBeanDefinition(String beanName, Class<?> beanClass) throws Exception{
        this.beanMap.put(beanName,beanClass);
    }

    @Override
    public void setAspect(Aspect aspect) {
        this.aspect = aspect;
    }

}

解釋&說(shuō)明

  • beanMap 模擬Spring容器,存放實(shí)例化的Bean對(duì)象

  • getBean(String beanName) 獲取Bean實(shí)例涕滋。如果對(duì)象都被增強(qiáng)睬辐,則返回增強(qiáng)過(guò)后的類(lèi),在實(shí)例化出來(lái)的時(shí)候,已經(jīng)是被增強(qiáng)過(guò)的溯饵,可以參考Spring 提供的源碼侵俗。

  • proxyEnhance(Object bean) 簡(jiǎn)易的增強(qiáng)邏輯,通過(guò)正則匹配丰刊,是否需要被增強(qiáng)隘谣。這里有使用到動(dòng)態(tài)代理。java給我們提供了兩種便捷的API啄巧,分別是JDK提供的動(dòng)態(tài)代理CGLIB提供的寻歧。

  • registerBeanDefinition(String beanName, Class<?> beanClass) 簡(jiǎn)易的加入邏輯,將Bean放入到Spring容器中棵帽∠ㄇ螅可以通過(guò)XML或是注解的形式將Bean放入到Spring中渣玲。這里簡(jiǎn)化了IOC處理的流程逗概,具體IOC相關(guān)的知識(shí),可以參考

  • createInstance(String beanName) Bean 的實(shí)例化

到這里的話(huà)忘衍,已經(jīng)是大功告成逾苫。

功成身退

public class AopMain {

    public static void main(String[] args) throws Exception{
        Advice advice = new TimeCsAdvice();
        Pointcut pointcut = new Pointcut();
        pointcut.setClassPattern("com\\.lynn\\.pay\\.imooc\\.demo\\.aop\\.service\\..*");
        pointcut.setMethodPattern(".*impl");
        Aspect aspect = new Aspect();
        aspect.setAdvice(advice);
        aspect.setPointcut(pointcut);

        //創(chuàng)建容器
        IApplicationContext applicationContext = new MyApplicationContext();
        applicationContext.setAspect(aspect);
        applicationContext.registerBeanDefinition("ktv", KTVimpl.class);
        applicationContext.registerBeanDefinition("spa", SPAimpl.class);

        ServiceForSPA spa = (ServiceForSPA)applicationContext.getBean("spa");
        ServiceForKTV ktv = (ServiceForKTV)applicationContext.getBean("ktv");

        spa.spa();
        ktv.kty();
    }
}

運(yùn)行結(jié)果如下圖,只對(duì)KTV服務(wù)進(jìn)行了匹配處理枚钓。

image.png

總結(jié)

Sping已經(jīng)為我們封裝好了AOP铅搓,我們只需要定義好以下幾個(gè)點(diǎn)就可以使用了

  1. Pointcut(切入點(diǎn))
  2. Advice (通知)
  3. invoke(增強(qiáng)邏輯)

當(dāng)然,在實(shí)際生產(chǎn)中也有許多作用搀捷,比如參數(shù)驗(yàn)簽星掰,TOKEN校驗(yàn),統(tǒng)一的脫敏處理嫩舟,重復(fù)請(qǐng)求處理等氢烘。方便我們用的同時(shí),不會(huì)入侵到原有代碼家厌,非常高效播玖。
這里是參考網(wǎng)易云免費(fèi)直播課Tony老師的課學(xué)習(xí)的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饭于,一起剝皮案震驚了整個(gè)濱河市蜀踏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掰吕,老刑警劉巖果覆,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異殖熟,居然都是意外死亡局待,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)燎猛,“玉大人恋捆,你說(shuō)我怎么就攤上這事≈乇粒” “怎么了沸停?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)昭卓。 經(jīng)常有香客問(wèn)我愤钾,道長(zhǎng),這世上最難降的妖魔是什么候醒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任能颁,我火速辦了婚禮,結(jié)果婚禮上倒淫,老公的妹妹穿的比我還像新娘伙菊。我一直安慰自己,他們只是感情好敌土,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布镜硕。 她就那樣靜靜地躺著,像睡著了一般返干。 火紅的嫁衣襯著肌膚如雪兴枯。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天矩欠,我揣著相機(jī)與錄音财剖,去河邊找鬼。 笑死癌淮,一個(gè)胖子當(dāng)著我的面吹牛躺坟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播该默,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼瞳氓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了栓袖?” 一聲冷哼從身側(cè)響起匣摘,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裹刮,沒(méi)想到半個(gè)月后音榜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捧弃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年赠叼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了擦囊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嘴办,死狀恐怖瞬场,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涧郊,我是刑警寧澤贯被,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站妆艘,受9級(jí)特大地震影響彤灶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜批旺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一幌陕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汽煮,春花似錦搏熄、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至翎卓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間摆寄,已是汗流浹背失暴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留微饥,地道東北人逗扒。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像欠橘,于是被迫代替她去往敵國(guó)和親矩肩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355