AOP原理分析(一)準(zhǔn)備階段和代理階段

Aop的執(zhí)行原理,我們應(yīng)該基本都了解:通過編寫切面類,我們可以在指定的切入點(diǎn)處插入我們額外的代碼塊,就好比代理模式中,我們可以在執(zhí)行目標(biāo)方法的前后干一些自己想干的事情。那么這是怎么樣實現(xiàn)的呢?
我們自己寫的類中的代碼是硬編碼寫死的,要想改變一個已經(jīng)寫好的類,我們常見的操作就是動態(tài)代理了,沒錯,AOP的底層就是將切入點(diǎn)所在的類創(chuàng)建成了代理對象。
我們知道Spring中的一個主要功能就是管理所有的bean對象,在創(chuàng)建對象的時候,為我們提供了很多的擴(kuò)展點(diǎn),可以方便我們來干預(yù)對象的創(chuàng)建,那么Aop究竟是在哪一個擴(kuò)展點(diǎn)的地方幫我們創(chuàng)建了代理對象呢?本文就來講解下從Aop的前置準(zhǔn)備到創(chuàng)建代理對象的整個流程。

一、@EnableAspectJAutoProxy

熟悉Spring開發(fā)的模式的話,我們都知道,開啟一個新的功能的話,我們基本需要在配置類上加上一個@EnableXxx的注解,而這類@EnableXxx的注解多半是向Spring容器中注入了影響bean創(chuàng)建生命周期的bean信息,開啟基于注解的Aop的@EnableAspectJAutoProxy注解同樣于此,他為我們導(dǎo)入了AnnotationAwareAspectJAutoProxyCreator類的定義信息。我們來看下AnnotationAwareAspectJAutoProxyCreator的繼承實現(xiàn)結(jié)構(gòu)圖:

AnnotationAwareAspectJAutoProxyCreator結(jié)構(gòu)圖.png

在整個結(jié)構(gòu)圖中,能干預(yù)bean的生命周期的是左上角的BeanPostProcessor接口,那么整個AOP功能的具體實現(xiàn)就是在對于InstantiationAwareBeanPostProcessor的方法的具體實現(xiàn)中,就是AbstractAutoProxyCreator類中postProcessBeforeInstantiation方法和postProcessAfterInitialization方法,我們下面就關(guān)注它的具體實現(xiàn):

二、Aop的前置準(zhǔn)備

1、AOP基礎(chǔ)準(zhǔn)備工作

首先,我們來看AbstractAutoProxyCreator類中postProcessBeforeInstantiation方法的具體實現(xiàn),他實現(xiàn)的接口是InstantiationAwareBeanPostProcessor,而InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation的觸發(fā)時機(jī)是在Spring容器開始準(zhǔn)備創(chuàng)建bean之前,Spring給我們提供了一個機(jī)會可以自己在這創(chuàng)建對象,而不走Spring的創(chuàng)建對象流程,但是AOP會在此就創(chuàng)建對象嗎?我們繼續(xù)往下看。
我們先看下前半部分代碼:

    Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {//advisedBeans已經(jīng)分析過的組件,不是增強(qiáng)過的組件
                return null;
            }//所有增強(qiáng)了的組件會被緩存在advisedBeans中,如果是我們需要增強(qiáng)的bean,就放在緩存中
            //isInfrastructureClass(beanClass):判斷當(dāng)前類是否有@Aspect注解,即當(dāng)前類是否是切面;shouldSkip中解析了所有的切面類并封裝了切面類中的切面方法
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {// shouldSkip:如果是切面類,則跳過處
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

這段代碼中主要在管理advisedBeans這個集合,這個集合中key為bean的名稱,value為boolean值,個人理解為這個集合的意義是,key表示處理過的beanName,而value值表示這個bean應(yīng)不應(yīng)該被增強(qiáng)【不代理】。那什么樣的類是不需要增強(qiáng)的呢?這就需要關(guān)注if中的兩個條件了,一個是isInfrastructureClass(beanClass),另一個是shouldSkip(beanClass, beanName),滿足這兩個條件之一的都會加入advisedBeans集合,并且標(biāo)記為不增強(qiáng);一起來看看這兩個方法:

  1. isInfrastructureClass(beanClass):判斷是不是AOP的基礎(chǔ)設(shè)施類,如果是的話,就加入到advised集合中,并標(biāo)記為不應(yīng)該增強(qiáng),那么究竟什么類是AOP的基礎(chǔ)設(shè)施類呢?深入到代碼里我們發(fā)現(xiàn):分為兩大塊:一個是實現(xiàn)了Advice,Pointcut,Advisor,AopInfrastructureBean這四個接口的類屬于AOP的基礎(chǔ)設(shè)施類;另一個是這個類上面標(biāo)注了@Aspect,即切面類
  2. shouldSkip(beanClass, beanName):這個方法是AOP準(zhǔn)備過程中的一個重要點(diǎn),因為這個方法中干了很多事情,為后面的AOP代理做好了鋪墊;代碼如下:
        protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // TODO: Consider optimization by caching the list of the aspect names
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        for (Advisor advisor : candidateAdvisors) {
            if (advisor instanceof AspectJPointcutAdvisor &&
                    ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
                return true;
            }
        }
        return super.shouldSkip(beanClass, beanName);
    }

整個應(yīng)不應(yīng)該跳過的邏輯是:獲取所有的Advisor集合,然后遍歷Advisor集合,如果當(dāng)前bean的名稱和其中的一個AspectJPointcutAdvisor的aspectName相同,其實就是說明這個類是切面類,那么就會跳過。到這里,我相信有的小伙伴就會發(fā)出一個疑問了:前面不是處理過了嗎?為什么此處還是需要處理呢?其實這里我也有點(diǎn)疑問,但是它干了更多的事情,那就是創(chuàng)建并緩存了所有的Advisor對象。
在findCandidateAdvisors方法的邏輯中首先是super執(zhí)行了AbstractAdvisorAutoProxyCreator中的邏輯:找到所有的Advisor的實現(xiàn)類的bean名稱,并進(jìn)行g(shù)etBean創(chuàng)建對象;其次時尋找@Aspect注解的類,然后為通知方法構(gòu)建Advisor,整個的構(gòu)建流程可以歸納如下:

  1. 拿到容器中所有的bean名稱
  2. 循環(huán)遍歷beanName,拿到beanType,判斷是不是切面類(@Aspect)
  3. 是切面類的話,就拿到類中除去標(biāo)有@Pointcut的方法。然后遍歷方法:
  4. 把每個切面方法構(gòu)建為Advisor【InstantiationModelAwarePointcutAdvisorImpl】
  5. 處理@DeclareParents屬性注解,最終會構(gòu)建DeclareParentsAdvisor【屬于IntroductionAdvisor,類級別的切入】,其中對應(yīng)的Advice為DelegatePerTargetObjectIntroductionInterceptor【屬于IntroductionInterceptor,同樣也是MethodInterceptor】
  6. 緩存進(jìn)advisorsCache 【beanName--->List<Advisor>】

2、Advice的構(gòu)建

其中,在構(gòu)建InstantiationModelAwarePointcutAdvisorImpl中,會構(gòu)建當(dāng)前增強(qiáng)方法的Advice,也就是構(gòu)造方法中的instantiateAdvice方法,最終會調(diào)用ReflectiveAspectJAdvisorFactory的getAdvice方法來構(gòu)建相應(yīng)的Advice,關(guān)鍵代碼如下:

    switch (aspectJAnnotation.getAnnotationType()) {
            case AtPointcut:
                if (logger.isDebugEnabled()) {
                    logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }
                return null;
            case AtAround:
                springAdvice = new AspectJAroundAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtBefore:
                springAdvice = new AspectJMethodBeforeAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtAfter:
                springAdvice = new AspectJAfterAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtAfterReturning:
                springAdvice = new AspectJAfterReturningAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    springAdvice.setReturningName(afterReturningAnnotation.returning());
                }
                break;
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            default:
                throw new UnsupportedOperationException(
                        "Unsupported advice type on method: " + candidateAdviceMethod);
        }

        // Now to configure the advice...
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrder);
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            springAdvice.setArgumentNamesFromStringArray(argNames);
        }
        springAdvice.calculateArgumentBindings();

最終會把切面類beanName和其中的增強(qiáng)方法Advisor存放到advisorsCache緩存中,方便后面使用。

可以說準(zhǔn)備階段,Spring主要就干了兩件事吧:第一件事是標(biāo)記那些不需要代理的AOP基礎(chǔ)設(shè)施類;第二件事就是尋找并創(chuàng)建容器中所有的Advisor,這一步也分為兩小步:其一,去尋找并創(chuàng)建所有直接實現(xiàn)了Advisor接口的;其二,處理標(biāo)注了@Aspect注解的類,并將其以aspectName及List<Advisor>的形式緩存到advisorsCache中。

3、Advisor & Advice

有必要介紹下AOP中的Advisor和Advice,這兩個東西到底有什么區(qū)別和關(guān)系呢?Advice就是我們要干預(yù)正常代碼而額外加的一部分代碼邏輯,其實可以理解為攔截器,而Advisor可以說成是對Advice的一種封裝吧,因為每個Advisor包含一個Advice,然后Advisor還應(yīng)該包括Advice增強(qiáng)的增強(qiáng)表達(dá)式,即它應(yīng)該什么時候進(jìn)行增強(qiáng),即增強(qiáng)條件吧;在Spring中,提供了兩個Advisor的子接口,分別是IntroductionAdvisor和PointcutAdvisor,IntroductionAdvisor提供的getClassFilter()方法和PointcutAdvisor中提供的getPointcut()方法就是各自的增強(qiáng)條件(如下圖),從增強(qiáng)條件我們也可以看出:IntroductionAdvisor是基于類級別的增強(qiáng),而PointcutAdvisor是基于方法級別或者類級別的增強(qiáng),顯然后者方法級別的增強(qiáng)是更加細(xì)粒度的,也是我們常用的@Aspect注解的Advisor。如果這么說還是過于抽象的話,那我們拿我們常用的切面類@Aspect來進(jìn)行來進(jìn)行類比:


Advisor.png
    @Component  //切面也是容器中的組件
@Aspect //說明這是切面
public class LogAspect {

    public LogAspect() {
        System.out.println("LogAspect....");
    }


    @DeclareParents(value = "com.spring.aop.HelloService", defaultImpl = DeclareParentsTestImpl.class)
    private DeclareParentsTest declareParentsTest;

    //前置通知
    @Before("execution(* com.spring.aop.HelloService.sayHello(..))")
    public void logStart(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        System.out.println("logStart()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
    }

    //返回通知
    @AfterReturning(value = "execution(* com.spring.aop.HelloService.sayHello(..))",returning = "result")
    public void logReturn(JoinPoint joinPoint,Object result) {
        String name = joinPoint.getSignature().getName();
        System.out.println("logReturn()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+result+"】");
    }

    //后置通知
    @After("execution(* com.spring.aop.HelloService.sayHello(..))")
    public void logEnd(JoinPoint joinPoint) {
        String name = joinPoint.getSignature().getName();
        System.out.println("logEnd()===>" + name + "...【args:" + Arrays.asList(joinPoint.getArgs()) + "】");
    }

    //異常通知
    @AfterThrowing(value = "execution(* com.spring.aop.HelloService.sayHello(..))",throwing = "e")
    public void logError(JoinPoint joinPoint,Exception e) {
        String name = joinPoint.getSignature().getName();
        System.out.println("logError()==>" + name + "...【args:"+ Arrays.asList(joinPoint.getArgs())+"】【result:"+e+"】");
    }
}

像上面logStart,logReturn,logEnd,logError等就可以理解為一個Advice,而@Before,@AfterReturning,@After,@AfterThrowing中的表達(dá)式就會說一個Pointcut,然后方法加上注解中的表達(dá)式就構(gòu)成了一個Advisor(PointcutAdvisor),相信這么說,大家應(yīng)該都能明白了吧。

三、Aop生成代理

Spring提供了兩個地方來生成Aop代理對象,下面我們來看看在哪兩個地方可以生成Aop的代理對象:

1、Aop生成代理對象的第一個地方

第一個地方是在AbstractAutoProxyCreator的postProcessBeforeInstantiation方法中,Aop的準(zhǔn)備工作做完后,就會查看有沒有自定義的TargetSource,如果有符合的TargetSource的話就會在此處直接創(chuàng)建bean的代理對象,不會繼續(xù)走Spring的創(chuàng)建bean的流程,但是,這個地方有個麻煩的點(diǎn),就是我們需要干預(yù)到AnnotationAwareAspectJAutoProxyCreator的創(chuàng)建,需要修改其Bean的定義信息,將我們自定義的TargetSourceCreator賦值給AnnotationAwareAspectJAutoProxyCreator的customTargetSourceCreators屬性,我們可以如下操作:
自定義的TargetSource:

    public class MyTargetSource implements TargetSource {
    private final Object target;

    public MyTargetSource(Object target) {
        this.target = target;
    }

    @Override
    public Class<?> getTargetClass() {
        return HelloService.class;
    }

    @Override
    public boolean isStatic() {
        return true;
    }

    @Override
    public Object getTarget() throws Exception {
        return target;
    }

    @Override
    public void releaseTarget(Object target) throws Exception {

    }
}

封裝成TargetSourceCreator:

    public class MyTargetSourceCreator implements TargetSourceCreator {
    @Override
    public TargetSource getTargetSource(Class<?> beanClass, String beanName) {
        if (beanName.equals("helloService")) {
            try {
                return new MyTargetSource(ReflectionUtils.accessibleConstructor(HelloService.class).newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

實現(xiàn)BeanFactoryPostProcessor修改AnnotationAwareAspectJAutoProxyCreator的bean定義信息:

    @Component
public class TargetSourceCreatorBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition bd = beanFactory.getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); // AnnotationAwareAspectJAutoProxyCreator的beanName
        bd.getPropertyValues().add("customTargetSourceCreators",new TargetSourceCreator[]{new MyTargetSourceCreator()});
    }
}

如上述配置后,HelloService的對象創(chuàng)建就會在AOP準(zhǔn)備工作做好后,通過如下代碼,獲取到自定義TargetSource后直接創(chuàng)建AOP代理對象,不走這個bean后面的生命周期了:

//創(chuàng)建個代理,如果為這個類指定了targetSource會在此就生成代理直接返回了,不走這個bean后面的生命周期了
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName); //
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

2、Aop生成代理對象的第二個地方

另一種情況是,當(dāng)沒有為對象指定自定義的TargetSource時,Spring會在bean的對象創(chuàng)建完成后的AbstractAutoProxyCreator的postProcessAfterInitialization方法中的wrapIfNecessary方法中創(chuàng)建Aop代理對象,代碼如下:

    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(//創(chuàng)建代理對象,specificInterceptors所有的增強(qiáng)器
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

兩處創(chuàng)建Aop代理對象的邏輯都是一樣的,僅僅是時機(jī)不一樣罷了,Spring默認(rèn)使用的是CGLIB來創(chuàng)建代理對象的,具體的創(chuàng)建過程在此就不說了,后續(xù)有時間再補(bǔ)上,我們需要知道的是:當(dāng)我們執(zhí)行被AOP增強(qiáng)的類時,需要回調(diào)DynamicAdvisedInterceptor這個類中的intercept方法,這也是后面我們講述AOP執(zhí)行流程的入口。

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

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

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