Spring AOP創建代理之獲取增強器


在上一篇文章中我們講解了關於Spring AOP 自定義標籤的解析,其中主要是完成了對 AspectJAwareAdvisorAutoProxyCreator 和 AnnotationAwareAspectJAutoProxyCreator 類型的自動註冊,我們看一下它們兩個的層次接口,如下圖所示:

從上圖的類層次結構圖中我們可以發現這兩個類是父子關係,那麼我們就針對子類 AnnotationAwareAspectJAutoProxyCreator 來進行分析。首先可以看到該類實現了 BeanPostProcessor 接口,而實現 BeanPostProcessor 接口後,當 Spring 加載這個 Bean 時會在實例化前會調用其 postProcessBeforeInstantiation 方法,實例化結束後會調用 postProcesssAfterIntialization 方法,我們對於 AOP 邏輯的分析也由 postProcessBeforeInstantiation 方法開始。

聲明:調試案例即爲初識Spring AOP一文中的案例

postProcessBeforeInstantiation

首先我們定位到 AbstractAutoProxyCreator 類中,查看其中實現的 postProcessBeforeInstantiation方法:

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        //根據給定的bean的class和name構建出個key,格式:beanClassName_beanName
        Object cacheKey = this.getCacheKey(beanClass, beanName);
        //如果未處理過
        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }

            // 給定的bean類是否代表一個基礎設施類,基礎設施類則添加到advisedBeans中,或者配置了指定bean
            if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        //爲bean實例創建目標源。 如果設置,則使用任何TargetSourceCreators。 如果不應該使用自定義TargetSource,則返回null。
        TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }

            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        } else {
            return null;
        }
    }

以上內容我們重點關注 shouldSkip()方法,在調試過程中發現,當 beanName 爲“host”時,會執行 shouldSkip()方法,進而會創建切面信息。當 beanName 爲“ProxyAnnotation2”時,cacheKey 會添加到 advisedBeans 中,這點會在 postProcesssAfterIntialization 方法中有所體現。

那接下來我們對 shouldSkip()方法學習一番,其實現在 AspectJAwareAdvisorAutoProxyCreator 類中:

    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        Iterator var4 = candidateAdvisors.iterator();

        Advisor advisor;
        do {
            if (!var4.hasNext()) {
                return super.shouldSkip(beanClass, beanName);
            }

            advisor = (Advisor)var4.next();
        } while(!(advisor instanceof AspectJPointcutAdvisor) || !((AspectJPointcutAdvisor)advisor).getAspectName().equals(beanName));

        return true;
    }

獲取增強器

findCandidateAdvisors()方法會查找增強信息,如果無則創建。因爲是在 bean 實例化之前,所以需要創建增強信息。通過 findCandidateAdvisors()進行查找,其代碼在 AnnotationAwareAspectJAutoProxyCreator 類中被覆寫了,具體如下:

    protected List<Advisor> findCandidateAdvisors() {
        // 當使用註解方式配置AOP的時候並不是丟棄了對XML配置的支持,
    	// 在這裏調用父類方法加載配置文件中的AOP聲明
        List<Advisor> advisors = super.findCandidateAdvisors();
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }

        return advisors;
    }

AnnotationAwareAspectJAutoProxyCreator間接繼承了AbstractAdvisorAutoProxyCreator, 在實現獲取增強的方法中除了保留父類的獲取配置文件中定義的增強外,同時添加了獲取 Bean 的註解增強的功能,那麼其實現正是由 this.aspectJAdvisorsBuilder.buildAspectJAdvisors()來實現的。

其中 buildAspectJAdvisors()會獲取定義的切面信息,並返回給 shouldSkip()方法。該方法在 BeanFactoryAspectJAdvisorsBuilder 類中實現,其定義如下:

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;
    if (aspectNames == null) {
        synchronized(this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList();
                List<String> aspectNames = new ArrayList();
                // 獲取所有的beanName
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                String[] var18 = beanNames;
                int var19 = beanNames.length;

                // 循環所有的beanName找出對應的增強方法
                for(int var7 = 0; var7 < var19; ++var7) {
                    String beanName = var18[var7];
                    if (this.isEligibleBean(beanName)) {
                        Class<?> beanType = this.beanFactory.getType(beanName);
                        // 如果存在Aspect註解
                        if (beanType != null && this.advisorFactory.isAspect(beanType)) {
                            aspectNames.add(beanName);
                            AspectMetadata amd = new AspectMetadata(beanType, beanName);
                            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                // 解析標記Aspect註解中的增強方法
                                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                if (this.beanFactory.isSingleton(beanName)) {
                                    //將增強器存入緩存中,下次可以直接取
                                    this.advisorsCache.put(beanName, classAdvisors);
                                } else {
                                    this.aspectFactoryCache.put(beanName, factory);
                                }

                                advisors.addAll(classAdvisors);
                            } else {
                                if (this.beanFactory.isSingleton(beanName)) {
                                    throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton");
                                }

                                MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                this.aspectFactoryCache.put(beanName, factory);
                                advisors.addAll(this.advisorFactory.getAdvisors(factory));
                            }
                        }
                    }
                }

                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    } else {
        // 記錄在緩存中
        List<Advisor> advisors = new ArrayList();
        Iterator var3 = aspectNames.iterator();

        while(var3.hasNext()) {
            String aspectName = (String)var3.next();
            List<Advisor> cachedAdvisors = (List)this.advisorsCache.get(aspectName);
            if (cachedAdvisors != null) {
                advisors.addAll(cachedAdvisors);
            } else {
                MetadataAwareAspectInstanceFactory factory = (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);
                advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
        }

        return advisors;
    }
}

調試過程中的信息如下:

通過調試可以理解其實現過程如下:

  1. 獲取所有 beanName,這一步驟中所有在 beanFactory 中註冊的 Bean 都會被提取出來;
  2. 遍歷所有的 beanName,並找出聲明 AspectJ 註解的類,進行下一步的處理;
  3. 對標記爲AspectJ 註解的類進行增強器的提取;
  4. 將提取結果加入緩存。

至此,我們已經完成了 Advisor 的提取,在上面的步驟中最爲重要也最爲繁雜的就是增強器的獲取,而這一切功能委託給了 getAdvisors 方法去實現 this.advisorFactory.getAdvisors(factory);

首先我們看下 BeanFactoryUtils.beanNamesForTypeIncludingAncestors()方法,它主要是將我們之前對 BeanDefinition 的解析和讀取的結果提取出來。然後是 this.advisorFactory.isAspect(beanType)方法,該方法在 AbstractAspectJAdvisorFactory 類中實現:

    public boolean isAspect(Class<?> clazz) {
        return this.hasAspectAnnotation(clazz) && !this.compiledByAjc(clazz);
    }

    private boolean hasAspectAnnotation(Class<?> clazz) {
        return AnnotationUtils.findAnnotation(clazz, Aspect.class) != null;
    }

    public static <A extends Annotation> A findAnnotation(Class<?> clazz, @Nullable Class<A> annotationType) {
        if (annotationType == null) {
            return null;
        } else if (!AnnotationFilter.PLAIN.matches(annotationType) && !AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) {
            return (Annotation)MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()).get(annotationType).withNonMergedAttributes().synthesize(MergedAnnotation::isPresent).orElse((Object)null);
        } else {
            //判斷此Class 是否存在Aspect.class註解
            A annotation = clazz.getDeclaredAnnotation(annotationType);
            if (annotation != null) {
                return annotation;
            } else {
                Class<?> superclass = clazz.getSuperclass();
                return superclass != null && superclass != Object.class ? findAnnotation(superclass, annotationType) : null;
            }
        }
    }

    private boolean compiledByAjc(Class<?> clazz) {
        Field[] var2 = clazz.getDeclaredFields();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            Field field = var2[var4];
            if (field.getName().startsWith("ajc$")) {
                return true;
            }
        }

        return false;
    }

如果 bean 存在 Aspect.class註解,就可以獲取此bean中的增強器了,接着我們來看看 List classAdvisors = this.advisorFactory.getAdvisors(factory); ,該方法在 ReflectiveAspectJAdvisorFactory 類中實現:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 獲取標記爲AspectJ的類
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    // 獲取標記爲AspectJ的name
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    this.validate(aspectClass);
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
    List<Advisor> advisors = new ArrayList();
    
    // 對aspectClass的每一個帶有註解的方法進行循環(帶有PointCut註解的方法除外),取得Advisor,並添加到集合裏。
    // (這是裏應該是取得Advice,然後取得我們自己定義的切面類中PointCut,組合成Advisor)
    Iterator var6 = this.getAdvisorMethods(aspectClass).iterator();

    while(var6.hasNext()) {
        Method method = (Method)var6.next();
        //將類中的方法封裝成Advisor
        Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    //如果尋找的增強器不爲空而且又配置了增強延遲初始化,那麼需要在首位加入同步實例化增強器
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    Field[] var12 = aspectClass.getDeclaredFields();
    int var13 = var12.length;

    //獲取DeclareParents註解
    for(int var14 = 0; var14 < var13; ++var14) {
        Field field = var12[var14];
        Advisor advisor = this.getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    return advisors;
}

調試截圖如下:

該方法首先完成了對增強器的獲取,包括獲取註解以及根據註解生成增強的步驟,然後考慮在配置中可能會將增強配置成延遲初始化,那麼需要在首位加入同步實例化增強器以保證增強使用之前的實例化,最後是對 DeclareParents 註解的獲取,下面分爲三個步驟進行介紹。

1、普通增強器的獲取

普通增強器的獲取邏輯通過 getAdvisor 方法實現,不過在此之前呢需要將所有帶註解的 Method 都取出來,該邏輯通過 getAdvisorMethods()實現。

private List<Method> getAdvisorMethods(Class<?> aspectClass) {
    List<Method> methods = new ArrayList();
    ReflectionUtils.doWithMethods(aspectClass, (method) -> {
        // 聲明爲Pointcut的方法不處理,即不放到 methods中
        if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
            methods.add(method);
        }

    }, ReflectionUtils.USER_DECLARED_METHODS);
    methods.sort(METHOD_COMPARATOR);
    return methods;
}

public static void doWithMethods(Class<?> clazz, ReflectionUtils.MethodCallback mc, @Nullable ReflectionUtils.MethodFilter mf) {
    // 通過反射獲取類中所有的方法
    Method[] methods = getDeclaredMethods(clazz, false);
    Method[] var4 = methods;
    int var5 = methods.length;

    int var6;
    for(var6 = 0; var6 < var5; ++var6) {
        Method method = var4[var6];
        if (mf == null || mf.matches(method)) {
            try {
                mc.doWith(method);
            } catch (IllegalAccessException var9) {
                throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + var9);
            }
        }
    }

    if (clazz.getSuperclass() == null || mf == USER_DECLARED_METHODS && clazz.getSuperclass() == Object.class) {
        if (clazz.isInterface()) {
            Class[] var10 = clazz.getInterfaces();
            var5 = var10.length;

            for(var6 = 0; var6 < var5; ++var6) {
                Class<?> superIfc = var10[var6];
                doWithMethods(superIfc, mc, mf);
            }
        }
    } else {
        doWithMethods(clazz.getSuperclass(), mc, mf);
    }

}

獲取到使用註解的 Method 之後,就要遍歷執行 getAdvisor(),其定義如下:

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) {
    this.validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    // 獲取PointCut信息(主要是PointCut裏的表達式)
    // 把Method對象也傳進去的目的是,比較Method對象上的註解,是不是下面註解其中一個
    // 如果不是,返回null;如果是,就把取得PointCut內容包裝返回
    // 被比較註解:Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class
    AspectJExpressionPointcut expressionPointcut = this.getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    // 根據PointCut信息生成增強器
    return expressionPointcut == null ? null : new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

(1)獲取切點信息

所謂獲取切點信息就是指定註解的表達式信息的獲取,如 @Before(value = "rentPointCut()")

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 獲取方法上的註解
    // 比較Method對象上的註解,是不是下面註解其中一個,如果不是返回null
    // 被比較註解:Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class
    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(* com.msdn.bean.Host.rent())")
        ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
        if (this.beanFactory != null) {
            ajexp.setBeanFactory(this.beanFactory);
        }

        return ajexp;
    }
}

詳細看下上面方法中使用到的方法 findAspectJAnnotationOnMethod。

protected static AbstractAspectJAdvisorFactory.AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    Class[] var1 = ASPECTJ_ANNOTATION_CLASSES;
    int var2 = var1.length;

    for(int var3 = 0; var3 < var2; ++var3) {
        Class<?> clazz = var1[var3];
        // 設置要查找的註解類,看看方法的上註解是不是這些註解其中之一
        AbstractAspectJAdvisorFactory.AspectJAnnotation<?> foundAnnotation = findAnnotation(method, clazz);
        if (foundAnnotation != null) {
            return foundAnnotation;
        }
    }

    return null;
}

其中 ASPECTJ_ANNOTATION_CLASSES 表示註解類數組,用來遍歷匹配。我們繼續往下查看。

    private static <A extends Annotation> AbstractAspectJAdvisorFactory.AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
        A result = AnnotationUtils.findAnnotation(method, toLookFor);
        return result != null ? new AbstractAspectJAdvisorFactory.AspectJAnnotation(result) : null;
    }

此方法的功能是獲取指定方法上的註解並使用 AspectJAnnotation 封裝。

(2)根據切點信息生成增強類

所有的增強都有 Advisor 實現類 InstantiationModelAwarePontcutAdvisorImpl 進行統一封裝的。我們看下其構造函數:

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    
    this.declaredPointcut = declaredPointcut;
    this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    this.methodName = aspectJAdviceMethod.getName();
    this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    this.aspectJAdviceMethod = aspectJAdviceMethod;
    this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    this.aspectInstanceFactory = aspectInstanceFactory;
    this.declarationOrder = declarationOrder;
    this.aspectName = aspectName;
    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
        this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
        this.lazy = true;
    } else {
        this.pointcut = this.declaredPointcut;
        this.lazy = false;
        this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
    }

}

調試過程中該部分信息如下:

通過調試結果可以發現該類的構造函數只是簡單地將信息封裝在類的實例中,所有的信息單純地賦值,在實例初始化的過程中還完成了對於增強器的初始化。調試還可以發現不同的增強所體現的邏輯是不同的,比如 @Before(value = “rentPointCut()”)與@After(value = “rentPointCut()”) 標籤的不同就是增強器增強的位置不同,所以就需要不同的增強器來完成不同的邏輯,而根據註解中的信息初始化對應的增強器就是在 instantiateAdvice()方法中實現的。

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
    Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    return advice != null ? advice : EMPTY_ADVICE;
}

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    this.validate(candidateAspectClass);
    AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    } else if (!this.isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");
    } else {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Found AspectJ method: " + candidateAdviceMethod);
        }

        Object springAdvice;
        switch(aspectJAnnotation.getAnnotationType()) {
            case AtPointcut:
                if (this.logger.isDebugEnabled()) {
                    this.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())) {
                    ((AbstractAspectJAdvice)springAdvice).setReturningName(afterReturningAnnotation.returning());
                }
                break;
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    ((AbstractAspectJAdvice)springAdvice).setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            default:
                throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
        }

        ((AbstractAspectJAdvice)springAdvice).setAspectName(aspectName);
        ((AbstractAspectJAdvice)springAdvice).setDeclarationOrder(declarationOrder);
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            ((AbstractAspectJAdvice)springAdvice).setArgumentNamesFromStringArray(argNames);
        }

        ((AbstractAspectJAdvice)springAdvice).calculateArgumentBindings();
        return (Advice)springAdvice;
    }
}

從上述函數代碼中可以看到,Spring會根據不同的註解生成不同的增強器,正如代碼switch (aspectJAnnotation.getAnnotationType()),根據不同的類型來生成。例如 AtBefore 會對應 AspectJMethodBeforeAdvice。在AspectJMethodBeforeAdvice 中完成了增強邏輯。那接下來,我們一起來分析一下 AspectJMethodBeforeAdvice,也就是 @Before 註解對應的通知實現類。看看它的邏輯是什麼樣的。

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
    public AspectJMethodBeforeAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }

    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        this.invokeAdviceMethod(this.getJoinPointMatch(), (Object)null, (Throwable)null);
    }

    public boolean isBeforeAdvice() {
        return true;
    }

    public boolean isAfterAdvice() {
        return false;
    }
}

我們重點關注 before 方法,該方法調用了父類中的 invokeAdviceMethod,然後 invokeAdviceMethod 在調用 invokeAdviceMethodWithGivenArgs,最後在 invokeAdviceMethodWithGivenArgs 通過反射執行通知方法。

關於 AspectJMethodBeforeAdvice 就簡單介紹到這裏吧,至於剩下的幾種實現,大家可以自己去看看。

2、增加同步實例化增強器

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

    protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {
        public SyntheticInstantiationAdvisor(MetadataAwareAspectInstanceFactory aif) {
            super(aif.getAspectMetadata().getPerClausePointcut(), (method, args, target) -> {
                //簡單初始化aspect
                aif.getAspectInstance();
            });
        }
    }
3、獲取DeclareParents註解

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

    private Advisor getDeclareParentsAdvisor(Field introductionField) {
        DeclareParents declareParents = (DeclareParents)introductionField.getAnnotation(DeclareParents.class);
        if (declareParents == null) {
            return null;
        } else if (DeclareParents.class == declareParents.defaultImpl()) {
            throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
        } else {
            return new DeclareParentsAdvisor(introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
        }
    }

至此關於增強器的獲取已結束,現在我們回到 shouldSkip()方法,發現最後它又調用了父類的 shouldSkip()方法,其定義如下:

    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        return AutoProxyUtils.isOriginalInstance(beanName, beanClass);
    }

    static boolean isOriginalInstance(String beanName, Class<?> beanClass) {
        if (StringUtils.hasLength(beanName) && beanName.length() == beanClass.getName().length() + ".ORIGINAL".length()) {
            return beanName.startsWith(beanClass.getName()) && beanName.endsWith(".ORIGINAL");
        } else {
            return false;
        }
    }

非指定 bean 執行 isOriginalInstance()方法,將不會返回 true。

後續的內容調試過程中並未發現其它內容,那麼 postProcessBeforeInstantiation()方法分析到此爲止。接下來學習 postProcesssAfterIntialization 方法。

postProcesssAfterIntialization

postProcesssAfterIntialization 方法定義如下:

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            //根據給定的bean的class和name構建出個key,格式:beanClassName_beanName
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                //如果它適合被代理,則需要封裝指定bean 
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }

        return bean;
    }

在上面的代碼中用到了方法 wrapIfNecessary,繼續跟蹤到方法內部:

  protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
      //如果已經處理過
       if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
           return bean;
       } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {// 無需增強,一般是基礎設施類
           return bean;
       } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {// 給定的bean類是否代表一個基礎設施類,基礎設施類不應代理,或者配置了指定bean不需要自動代理
           // 獲取增強
           Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
           if (specificInterceptors != DO_NOT_PROXY) {
               this.advisedBeans.put(cacheKey, Boolean.TRUE);
               //針對增強創建代理
               Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
               this.proxyTypes.put(cacheKey, proxy.getClass());
               return proxy;
           } else {
               this.advisedBeans.put(cacheKey, Boolean.FALSE);
               return bean;
           }
       } else {
           this.advisedBeans.put(cacheKey, Boolean.FALSE);
           return bean;
       }
   }

調試過程中發現,當 beanName 爲“host”時,會獲取到增強,具體是通過 getAdvicesAndAdvisorsForBean()方法實現的,其代碼在 AbstractAdvisorAutoProxyCreator 類中,如下:

    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
        List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);
        return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
    }

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //查找要在自動代理中使用的所有候選Advisor
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        //搜索給定的候選Advisor,以查找可以應用於指定bean的所有Advisor
        List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        this.extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
        }

        return eligibleAdvisors;
    }

其中的 findCandidateAdvisors()在 AnnotationAwareAspectJAutoProxyCreator 類中被覆寫了。

    protected List<Advisor> findCandidateAdvisors() {
        List<Advisor> advisors = super.findCandidateAdvisors();
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }

        return advisors;
    }

調試過程中發現獲取的增強方法如下圖所示:

從上述方法和圖片可以看出,創建代理主要包含了兩個步驟:

  1. 獲取增強方法或者增強器;
  2. 根據獲取的增強進行代理。

對於指定 bean 的增強方法的獲取一定是包含兩個步驟,獲取所有的增強以及尋找所有增強中使用與 bean 的增強並應用,那麼 findCandidateAdvisorsfindAdvisorsThatCanApply 便是做了這兩件事情。當然,如果無法找到對應的增強器便返回 DO_NOT_PROXY,其中 DO_NOT_PROXY=null

尋找匹配的增強器

前面講的獲取增強器是發生在 postProcessBeforeInstantiation 方法中,完成了所有增強器的解析,但是對於所有增強器來講,並不一定都適用於當前的 Bean,所以還需要將 Bean 與增強器相對應,即一對多的關係。那麼該處理髮生在哪裏呢,我們在 getAdvicesAndAdvisorsForBean方法中有提到 findAdvisorsThatCanApply ,該方法就是用來挑選出適合的增強器。

    protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
        ProxyCreationContext.setCurrentProxiedBeanName(beanName);

        List var4;
        try {
            // 過濾已經得到的advisors
            var4 = AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
        } finally {
            ProxyCreationContext.setCurrentProxiedBeanName((String)null);
        }

        return var4;
    }

   public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        } else {
            List<Advisor> eligibleAdvisors = new ArrayList();
            Iterator var3 = candidateAdvisors.iterator();

            while(var3.hasNext()) {
                Advisor candidate = (Advisor)var3.next();
                // 首先處理引介增強
                if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                    eligibleAdvisors.add(candidate);
                }
            }

            boolean hasIntroductions = !eligibleAdvisors.isEmpty();
            Iterator var7 = candidateAdvisors.iterator();

            // 對於普通bean的處理
            while(var7.hasNext()) {
                Advisor candidate = (Advisor)var7.next();
                if (!(candidate instanceof IntroductionAdvisor) && canApply(candidate, clazz, hasIntroductions)) {
                    eligibleAdvisors.add(candidate);
                }
            }

            return eligibleAdvisors;
        }
    }

findAdvisorsThatCanApply 函數的主要功能是尋找增強器中適用於當前 class 的增強器。引介增強與普通的增強的處理是不一樣的,所以分開處理。而對於真正的匹配在 canApply 中實現。

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor)advisor).getClassFilter().matches(targetClass);
    } else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor)advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    } else {
        return true;
    }
}

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    //通過Pointcut的條件判斷此類是否能匹配
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    } else {
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        if (methodMatcher == MethodMatcher.TRUE) {
            return true;
        } else {
            IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
            if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
                introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher)methodMatcher;
            }

            Set<Class<?>> classes = new LinkedHashSet();
            if (!Proxy.isProxyClass(targetClass)) {
                classes.add(ClassUtils.getUserClass(targetClass));
            }

            classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
            Iterator var6 = classes.iterator();

            while(var6.hasNext()) {
                Class<?> clazz = (Class)var6.next();
                //反射獲取類中所有的方法
                Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
                Method[] var9 = methods;
                int var10 = methods.length;

                for(int var11 = 0; var11 < var10; ++var11) {
                    Method method = var9[var11];
                    /根據匹配原則判斷該方法是否能匹配Pointcut中的規則,如果有一個方法能匹配,則返回true
                        if (introductionAwareMethodMatcher != null) {
                            if (introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) {
                                return true;
                            }
                        } else if (methodMatcher.matches(method, targetClass)) {
                            return true;
                        }
                }
            }

            return false;
        }
    }
}

首先判斷 bean 是否滿足切點的規則,如果能滿足,則獲取 bean 的所有方法,判斷是否有方法能匹配規則,有方法匹配規則,就代表此 Advisor 能作用於該 bean,然後將該 Advisor 加入 eligibleAdvisors 集合中。

這裏我們重點關注一下匹配規則,即 matches 方法,該方法在 AspectJExpressionPointcut 中實現。

    public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
        this.obtainPointcutExpression();
        ShadowMatch shadowMatch = this.getTargetShadowMatch(method, targetClass);
        if (shadowMatch.alwaysMatches()) {
            return true;
        } else if (shadowMatch.neverMatches()) {
            return false;
        } else if (hasIntroductions) {
            return true;
        } else {
            RuntimeTestWalker walker = this.getRuntimeTestWalker(shadowMatch);
            return !walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass);
        }
    }

至此,我們在 postProcesssAfterIntialization 方法中找到了所有匹配 Bean 中的增強器,下一章會講解代理的創建。

拓展篩選出的增強器列表

findEligibleAdvisors 方法中在執行完 findAdvisorsThatCanApply()方法後,還有一個 extendAdvisors()方法,雖然它做的事情不多,邏輯也比較簡單。但是我發現執行完該方法後 eligibleAdvisors 會新增一個對象,因此覺得需要一探究竟。該方法在 AspectJAwareAdvisorAutoProxyCreator 類中,定義如下:

    protected void extendAdvisors(List<Advisor> candidateAdvisors) {
        AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
    }

    public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
        if (!advisors.isEmpty()) {
            boolean foundAspectJAdvice = false;
            Iterator var2 = advisors.iterator();

            while(var2.hasNext()) {
                Advisor advisor = (Advisor)var2.next();
                //檢測 advisors 列表中是否存在 AspectJ 類型的 Advisor 或 Advice
                if (isAspectJAdvice(advisor)) {
                    foundAspectJAdvice = true;
                    break;
                }
            }

            if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
                //向 advisors 列表的首部添加 DefaultPointcutAdvisor
                advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
                return true;
            }
        }

        return false;
    }

makeAdvisorChainAspectJCapableIfNecessary 方法主要的目的是向通知器列表首部添加 DefaultPointcutAdvisor 類型的通知器,也就是 ExposeInvocationInterceptor.ADVISOR。至於添加此種類型增強器的意圖,我會在後面文章裏說明,這裏不便展開。關於 extendAdvisors 這個方法,這裏就先說到這了。

總結

本篇文章也接近尾聲了,這篇文章有點長,大家看下來應該也挺累的。在學習源碼的過程中,我參考了很多資料,最終按照自己學習的進度記錄下來的,比如我重點寫了關於 postProcessBeforeInstantiation 的使用,以上講解是根據我調試的順序進行的,與網上講的可能會有些出入。不過總算是把主要邏輯弄清楚了,某些細節部分可能沒有講解到。大家在閱讀的過程中,如果發現文章中出現錯誤或不妥之處,這裏還請指明,也請多多指教。大家共同學習,一起進步。

參考

《Spring 源碼深度解析》- 郝佳

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章