1. 程式人生 > >AOP原始碼解析(三)增強器的獲取

AOP原始碼解析(三)增強器的獲取

普通增強器的獲取邏輯通過getAdvisor方法實現,實現步驟包括對切點的註解的獲取及根據註解資訊生成增強。

    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)
    {
        validate(aif.getAspectMetadata().getAspectClass());
        //切點資訊的獲取
        AspectJExpressionPointcut ajexp = getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
        if(ajexp == null)
            return null;
        else
            //根據切點資訊生成增強器
            return new InstantiationModelAwarePointcutAdvisorImpl(this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
    }

(1)切點資訊的獲取。所謂獲取切點資訊就是指定註解的表示式資訊的獲取,如@Before("test()")
    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class candidateAspectClass)
    {
        //獲取方法上的註解
        AbstractAspectJAdvisorFactory.AspectJAnnotation aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if(aspectJAnnotation == null)
        {
            return null;
        } else
        {
            //使用AspectJExpressionPointcut例項封裝獲取的資訊
            AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
            //提取得到的註解中的表示式如:@Pointcut("execution(* *.*test*(..))")中的execution(* *.*test*(..))<pre name="code" class="java">   

))
ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); return ajexp; } }
 protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method)
    {
        //設定敏感的註解類
        Class classesToLookFor[] = {
            org/aspectj/lang/annotation/Before, org/aspectj/lang/annotation/Around, org/aspectj/lang/annotation/After, org/aspectj/lang/annotation/AfterReturning, org/aspectj/lang/annotation/AfterThrowing, org/aspectj/lang/annotation/Pointcut
        };
        for(Class<? extends Annotation> c : classsesToLookFor)
        {
            AspectJAnnotation foundAnnotation = findAnnotation(method, c);
            if(foundAnnotation != null)
                return foundAnnotation;
        }

        return null;
    }
//獲取指定方法上的註解並使用AspectJAnnotation封裝
    private static AspectJAnnotation findAnnotation(Method method, Class toLookFor)
    {
        Annotation result = AnnotationUtils.findAnnotation(method, toLookFor);
        if(result != null)
            return new AspectJAnnotation(result);
        else
            return null;
    }
(2)根據切點資訊生成增強。所有的增強都有Advisor實現類InstantiationModelAwarePontcutAdvisorImpl統一瘋長。
    public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName)
    {
        declaredPointcut = ajexp;
        this.method = method;
        atAspectJAdvisorFactory = af;
        aspectInstanceFactory = aif;
        declarationOrder = declarationOrderInAspect;
        this.aspectName = aspectName;
        if(aif.getAspectMetadata().isLazilyInstantiated())
        {
            Pointcut preInstantiationPointcut = Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), declaredPointcut);
            pointcut = new PerTargetInstantiationModelPointcut(declaredPointcut, preInstantiationPointcut, aif);
            lazy = true;
        } else
        {
            instantiatedAdvice = instantiateAdvice(declaredPointcut);
            pointcut = declaredPointcut;
            lazy = false;
        }
    }

封裝過程只是簡單地將資訊封裝在類的例項中,所有的額資訊單純地複製。在例項初始化的過程中還完成了對於增強器的初始化。因為不同的增強所體現的邏輯是不同的,比如@Before(“test()”)與After(“test()”)標籤的不同就是增強器增強的位置不同,所以就需要不同的增強器來完成不同的邏輯,而根據註解中的資訊初始化對應的額增強器就是在instantiateAdvice函式中實現的。
    private Advice instantiateAdvice(AspectJExpressionPointcut pcut)
    {
        return atAspectJAdvisorFactory.getAdvice(method, pcut, aspectInstanceFactory, declarationOrder, aspectName);
    }

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)
    {
        Class candidateAspectClass = aif.getAspectMetadata().getAspectClass();
        validate(candidateAspectClass);
        AbstractAspectJAdvisorFactory.AspectJAnnotation aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if(aspectJAnnotation == null)
            return null;
       //if we get here,we know we have an AspectJ method. Check that its an AspectJ-annotated class 
       if(!isAspect(candidateAspectClass))
            throw new AopConfigException((new StringBuilder()).append("Advice must be declared inside an aspect type: Offending method '").append(candidateAdviceMethod).append("' in class [").append(candidateAspectClass.getName()).append("]").toString());
        if(logger.isDebugEnabled())
            logger.debug((new StringBuilder()).append("Found AspectJ method: ").append(candidateAdviceMethod).toString());
        AbstractAspectJAdvice springAdvice;
        switch(_cls4..SwitchMap.org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotationType[aspectJAnnotation.getAnnotationType().ordinal()])
        {
        case 1: // '\001'
            springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
            break;

        case 2: // '\002'
            springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
            break;

        case 3: // '\003'
            springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
            AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();
            if(StringUtils.hasText(afterReturningAnnotation.returning()))
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            break;

        case 4: // '\004'
            springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
            if(StringUtils.hasText(afterThrowingAnnotation.throwing()))
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            break;

        case 5: // '\005'
            springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
            break;

        case 6: // '\006'
            if(logger.isDebugEnabled())
                logger.debug((new StringBuilder()).append("Processing pointcut '").append(candidateAdviceMethod.getName()).append("'").toString());
            return null;

        default:
            throw new UnsupportedOperationException((new StringBuilder()).append("Unsupported advice type on method ").append(candidateAdviceMethod).toString());
        }
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrderInAspect);
        String argNames[] = parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if(argNames != null)
            springAdvice.setArgumentNamesFromStringArray(argNames);
        springAdvice.calculateArgumentBindings();
        return springAdvice;
    }
        }
從函式中可以看到,Spring會根據不同的註解生成不同的增強器,例如AtBefore會對應AspectJMethodBeforeAdvice。

2.增加同步例項化增強器

如果尋找的增強器不為空而且又配置了增強延遲初始化,那麼就需要在首位加入同步例項化增強器。同步例項化增強器SyntheticInstantiationAdvisor,如下:

 protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor
    {

        public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif)
        {
            super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() {
                //目前方法前呼叫,類似@Before
                public void before(Method method, Object args[], Object target)
                {   //簡單初始化aspect
                    aif.getAspectInstance();
                }

3.獲取DeclareParents註解


DeclareParents主要用於引介增強的註解形式的實現,而其實現方式馭普通增強很類似,只不過使用DeclareParentsAdvisor對功能進行封裝。

    private Advisor getDeclareParentsAdvisor(Field introductionField)
    {
        DeclareParents declareParents = (DeclareParents)introductionField.getAnnotation(org/aspectj/lang/annotation/DeclareParents);
        if(declareParents == null)
            return null;
        if(org/aspectj/lang/annotation/DeclareParents.equals(declareParents.defaultImpl()))
            throw new IllegalStateException("defaultImpl must be set on DeclareParents");
        else
            return new DeclareParentsAdvisor(introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
    }
後面一節我們一起分析一下,Spring如何尋找匹配的增強器,篇幅較長,也比較複雜,但還是希望我們一起看下去,因為這個過程雖然很艱辛,堅持過後,你會發現,收穫是非常巨大的。