淺析AOP實(shí)現(xiàn)原理(3)Spring AOP實(shí)現(xiàn)原理

前言

有一點(diǎn)Spring基礎(chǔ)的同學(xué)應(yīng)該都知道,Spring的動(dòng)態(tài)代理默認(rèn)使用的是JDK動(dòng)態(tài)代理(對(duì)于非接口的類,用cglib泉手,與JDK動(dòng)態(tài)代理類似,這里不多做解釋)偶器,不清楚JDK動(dòng)態(tài)代理的點(diǎn)這里斩萌。并且,我們知道通過(guò)jdk動(dòng)態(tài)代理屏轰,我們可以拿到一個(gè)代理后的對(duì)象颊郎,而Spring的bean實(shí)例化過(guò)程,就涉及到代理對(duì)象的替換

Spring Bean的生命周期

想知道Spring是如何實(shí)現(xiàn)的動(dòng)態(tài)代理亭枷,必須對(duì)bean的生命周期和實(shí)例化過(guò)程有初步了解袭艺。筆者通過(guò)閱讀源碼,得知一個(gè)bean的實(shí)例化過(guò)程叨粘,是由一系列的BeanPostProcessor(后置處理器)處理的猾编,在bean實(shí)例化之前的注解解析瘤睹,實(shí)例化過(guò)程中確定構(gòu)造器,實(shí)例化后的屬性注入答倡,屬性注入完的初始化步驟轰传,分別由特定的后置處理器來(lái)完成。
如果要在Spring當(dāng)中實(shí)現(xiàn)aop動(dòng)態(tài)代理瘪撇,筆者認(rèn)為主要有兩種思路:

  • 1获茬、實(shí)例化之前將BeanDefinition中的class替換為代理類,可以通過(guò)自定義BeanFactoryPostProcessor或者ImportBeanDefinitionRegisterar來(lái)實(shí)現(xiàn)
  • 2倔既、實(shí)例化完成之后恕曲,通過(guò)自定義的BeanPostProcessor將對(duì)象替換成代理對(duì)象

由于jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,都只提供了獲取代理對(duì)象的方法渤涌,因此我們可以猜測(cè)Spring是利用第二種方式實(shí)現(xiàn)的佩谣。

猜想驗(yàn)證

下面是一段aop切面的demo:

@Aspect
@Component
public class MyAspect {
    @Autowired
    private MyService myService;

    @Pointcut("execution(* com.lwl.controller.MyController.testAop(..))")
    public void testAop(){}

    @Around("testAop()")
    public Object aopWeaveing(ProceedingJoinPoint joinPoint) throws Throwable {
       //代理代碼,忽略
    }
}

下面是被代理的方法和類:

@RestController
@RequestMapping("/aop")
public class MyController {
    @GetMapping("test")
    @AssertLogin
    public String testAop(){
        //業(yè)務(wù)代碼实蓬,忽略
    }
}

bean的實(shí)例化過(guò)程主要發(fā)生在context的refresh方法中的finishBeanFactoryInitialization方法茸俭,其中
調(diào)用了DefaultLisableBeanFactory的preInstantiateSingletons,通過(guò)getBean(beanName)來(lái)實(shí)例化bean
因此我們跟蹤到執(zhí)行了自定義beanPostProcessor的AbstractAutowireCapableBeanFactory的initializeBean方法當(dāng)中:

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        //忽略不重要的代碼
        //wrappedBean即bean對(duì)象
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
           //調(diào)用beanPostProcessor的postProcessBeforeInitialization方法
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            //調(diào)用bean中實(shí)現(xiàn)的afterPropertiesSet或被@PostConstuct注解修飾的方法安皱,即初始化方法
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        if (mbd == null || !mbd.isSynthetic()) {
          //調(diào)用beanPostProcessor的postProcessAfterInitialization方法
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

這里需要明確的是调鬓,beanPostProcessor的兩個(gè)方法postProcessBeforeInitialization和postProcessAfterInitialization都能夠替換對(duì)象,區(qū)別在于postProcessBeforeInitialization在bean執(zhí)行初始化方法之前執(zhí)行酌伊,簡(jiǎn)單思考一下腾窝,我們對(duì)對(duì)象進(jìn)行代理時(shí),很可能需要用到該bean對(duì)象初始化之后的屬性腺晾,因此我們猜測(cè)代理的過(guò)程發(fā)生在初始化之后的postProcessAfterInitialization過(guò)程中燕锥。
筆者將斷點(diǎn)打在調(diào)用applyBeanPostProcessorsAfterInitialization方法之前辜贵,可以看到此時(shí)的wrapperBean還是MyController


斷點(diǎn)往下走悯蝉,執(zhí)行完applyBeanPostProcessorsAfterInitialization方法,發(fā)現(xiàn)wrappedBean已經(jīng)變成了CGLib動(dòng)態(tài)代理對(duì)象:

這就驗(yàn)證了我們之前的猜測(cè)

實(shí)現(xiàn)原理分析

我們進(jìn)入applyBeanPostProcessorsAfterInitialization方法看看spring到底做了什么事

    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
         //遍歷容器中所有的beanPostProcessor托慨,執(zhí)行postProcessAfterInitialization方法
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

可以看到該方法中遍歷了spring容器里所有的beanPostProcessor鼻由,并挨個(gè)執(zhí)行postProcessAfterInitialization。由于筆者是在平時(shí)開(kāi)發(fā)的springboot環(huán)境中隨便寫(xiě)了一個(gè)aop類做調(diào)試厚棵,因此此時(shí)的容器中有非常多的后置處理器蕉世,簡(jiǎn)單看一下一共有三十個(gè):


image.png

目前我們能確定的是,一定是某一個(gè)后置處理器執(zhí)行完了postProcessAfterInitialization方法婆硬,才將對(duì)象替換成了代理對(duì)象狠轻,那如何從這么多的beanPostProcessor中找到那個(gè)兇手??jī)煞N方法:

  • 1彬犯、還是用之前打斷點(diǎn)的方法向楼,看看哪一個(gè)后置處理器執(zhí)行完以后對(duì)象發(fā)生了改變查吊,但是這里有三十多個(gè)后置處理器,作為一個(gè)有追求的程序員考慮到算法時(shí)間復(fù)雜度為O(n)湖蜕,不行這個(gè)方法需要優(yōu)化
  • 2逻卖、靠猜- -筆者死磕源碼的經(jīng)歷,大部分情況靠猜昭抒,事實(shí)上百分之八十都能猜對(duì)评也。這三十個(gè)beanPostProcessor中,從名字上看灭返,似乎只有數(shù)組下標(biāo)為5的AnnotationAwareAspectJAutoProxyCreator看起來(lái)比較可疑(又是Aspect又是AutoProxy的)
    因此我們將斷點(diǎn)條件修改為只有beanPostProcessor為該對(duì)象時(shí)才暫停:


    image.png

    繼續(xù)調(diào)試盗迟,成功進(jìn)入斷點(diǎn),執(zhí)行之前為MyController


    image.png

    斷點(diǎn)執(zhí)行完以后對(duì)象變成了Cglib代理對(duì)象:
    image.png

    猜想再次被驗(yàn)證了熙含,那么我們的重點(diǎn)就轉(zhuǎn)換為查看這個(gè)beanPostProcessor中究竟做了什么诈乒,一路跟蹤:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
             //重點(diǎn)看這個(gè)方法,它返回了代理后的對(duì)象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
              //根據(jù)bean的類型和bean的名稱婆芦,獲取與該類相關(guān)的切面
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
           //此處創(chuàng)建代理對(duì)象
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
        return bean;
    }


protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
         //從切面對(duì)象中提取我們定義的增強(qiáng)邏輯代碼怕磨,構(gòu)造成proxyFactory代理工廠
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);

        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
                //通過(guò)代理工廠創(chuàng)建代理
        return proxyFactory.getProxy(getProxyClassLoader());
    }

//該方法為proxyFactory中的getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

調(diào)試發(fā)現(xiàn)createAopProxy()返回了一個(gè)CglibAopProxy,說(shuō)明此時(shí)使用的是cglib動(dòng)態(tài)代理



而CglibAopProxy的getProxy里面就是我們非常熟悉的cglib動(dòng)態(tài)代理的代碼了:

public Object getProxy(@Nullable ClassLoader classLoader) {
             //rootClass即MyController.class
            Class<?> rootClass = this.advised.getTargetClass();
            Class<?> proxySuperClass = rootClass;
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }

            enhancer.setSuperclass(proxySuperClass);
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

           //根據(jù)要代理的類生成callback消约,這個(gè)是重點(diǎn)
            Callback[] callbacks = getCallbacks(rootClass);
            Class<?>[] types = new Class<?>[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);
            return createProxyClassAndInstance(enhancer, callbacks);
    }



private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
         //advised即之前創(chuàng)建好的proxyFactory肠鲫,包含了我們交給spring管理的所有Aspect
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();
        //將切面構(gòu)造成Callback,主要看這個(gè)類
        Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
             ... ...
    }


//構(gòu)造的callback:
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
       //重點(diǎn)看重寫(xiě)的intercept方法
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            TargetSource targetSource = this.advised.getTargetSource();
            try {
             //如果代理對(duì)象被設(shè)置為可以暴露或粮,則將當(dāng)前的代理對(duì)象注入
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                target = targetSource.getTarget();
                Class<?> targetClass = (target != null ? target.getClass() : null);
              //把切面的增強(qiáng)邏輯轉(zhuǎn)換成list
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
             //如果沒(méi)有增強(qiáng)邏輯导饲,則直接執(zhí)行原來(lái)的方法
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                }
                else {
               //有增強(qiáng)邏輯,則根據(jù)spring aop配置規(guī)則確定執(zhí)行順序
                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                }
                retVal = processReturnType(proxy, target, method, retVal);
                return retVal;
            }
        }
    }

總結(jié)

Spring的aop氯材,是在bean被實(shí)例化并初始化之后渣锦,通過(guò)beanPostProcessor的postProcessAfterInitialization,通過(guò)cglib(jdk動(dòng)態(tài)代理)氢哮,在實(shí)際邏輯執(zhí)行前后插入我們的增強(qiáng)邏輯

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袋毙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子冗尤,更是在濱河造成了極大的恐慌听盖,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裂七,死亡現(xiàn)場(chǎng)離奇詭異皆看,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)背零,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)腰吟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人徙瓶,你說(shuō)我怎么就攤上這事毛雇÷加铮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵禾乘,是天一觀的道長(zhǎng)澎埠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)始藕,這世上最難降的妖魔是什么蒲稳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮伍派,結(jié)果婚禮上江耀,老公的妹妹穿的比我還像新娘。我一直安慰自己诉植,他們只是感情好祥国,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著晾腔,像睡著了一般舌稀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灼擂,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天壁查,我揣著相機(jī)與錄音,去河邊找鬼剔应。 笑死睡腿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的峻贮。 我是一名探鬼主播席怪,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼纤控!你這毒婦竟也來(lái)了挂捻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嚼黔,失蹤者是張志新(化名)和其女友劉穎细层,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唬涧,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年盛撑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碎节。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抵卫,死狀恐怖狮荔,靈堂內(nèi)的尸體忽然破棺而出胎撇,到底是詐尸還是另有隱情,我是刑警寧澤殖氏,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布晚树,位于F島的核電站,受9級(jí)特大地震影響雅采,放射性物質(zhì)發(fā)生泄漏爵憎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一婚瓜、第九天 我趴在偏房一處隱蔽的房頂上張望宝鼓。 院中可真熱鬧,春花似錦巴刻、人聲如沸愚铡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沥寥。三九已至,卻和暖如春柠座,著一層夾襖步出監(jiān)牢的瞬間营曼,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工愚隧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蒂阱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓狂塘,卻偏偏與公主長(zhǎng)得像录煤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荞胡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354