Spring源碼解析 -- AOP原理(1)

源碼分析基於spring 4.3.x
本文經過閱讀Spring源碼,分析Spring AOP的功能是如何實現的。
關於閱讀源碼的思路,可參考 -- 如何閱讀java源碼java

首先,明確幾個概念
pointcut:切入點,根據方法名或者正則表達式去攔截一個方法。
advice:通知,在攔截到方法執行前或執行後的加強操做。
aspect:切面,一個切面能夠包括多個切入點和通知,spring內部的切入點和通知是無序的。
advisor:只有一個通知和一個切入點的單元切面,能夠看作一種特殊的aspect。正則表達式

開始閱讀源碼前,本文主要關注的幾個問題:spring

  1. xml中<aop:aspectj-autoproxy/>元素的做用
  2. spring如何根據Aspect註解建立切面
  3. spring如何根據切面信息建立代理對象
  4. spring是如何對多個通知進行鏈式調用的

<aop:aspectj-autoproxy/> 的做用

aspectj-autoproxy是spring內部定義的標籤。
前面解析Spring讀取bean元數據的文章說過,在spring中除了<beans>標籤,其餘標籤須要編寫一個NamespaceHandlerSupport實現類來完成標籤解析工做。
在spring源碼中搜索一下,就能夠發現aspectj-autoproxy的解析類AopNamespaceHandlerexpress

public void init() {
    this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
    this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}

而AspectJAutoProxyBeanDefinitionParser類負責對aspectj-autoproxy標籤進行解析:
AspectJAutoProxyBeanDefinitionParser#parse緩存

public BeanDefinition parse(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    extendBeanDefinition(element, parserContext);
    return null;
}

AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary -> AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary微信

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

這裏將AnnotationAwareAspectJAutoProxyCreator注入到spring上下文環境中。
AnnotationAwareAspectJAutoProxyCreator是一個關鍵的類,繼承了BeanPostProcessor接口,它負責完成建立切面工做。源碼分析

建立advisor

AbstractAutoProxyCreator#postProcessAfterInitialization -> AbstractAutoProxyCreator#wrapIfNecessary
(AbstractAutoProxyCreator是AnnotationAwareAspectJAutoProxyCreator的父類)post

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ...
    
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);    // #1
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));    // #2
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

#1 根據bean元數據構造通知和單元切面
#2 建立代理對象ui

getAdvicesAndAdvisorsForBean -> AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean -> AbstractAdvisorAutoProxyCreator#findEligibleAdvisorsthis

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();    // #1
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);    // #2
    extendAdvisors(eligibleAdvisors);    // #3
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);    // #4
    }
    return eligibleAdvisors;
}

這是一個很是重要的方法,實現了幾個關鍵步驟
#1 findCandidateAdvisors -- 查找全部的單元切面(第一次會建立)
#2 findAdvisorsThatCanApply -- 根據目標bean的class過濾一部分的單元切面
#3 extendAdvisors -- 擴充單元切面列表,spring會根據須要添加一些內部使用的單元切面
#4 sortAdvisors -- 對單元切面排序

查找全部的單元切面

findCandidateAdvisors -> AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors -> BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;

    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {    // #1
                List<Advisor> advisors = new LinkedList<Advisor>();
                aspectNames = new LinkedList<String>();
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                for (String beanName : beanNames) {    // #2
                    if (!isEligibleBean(beanName)) {    // #3
                        continue;
                    }
                    // We must be careful not to instantiate beans eagerly as in this case they
                    // would be cached by the Spring container but would not have been weaved.
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    if (this.advisorFactory.isAspect(beanType)) {    // #4
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);    // #5
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            MetadataAwareAspectInstanceFactory factory =
                                    new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);    // #6
                            if (this.beanFactory.isSingleton(beanName)) {    // #7
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);    // #8
                        }
                        else {
                            // Per target or per this.
                            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;
            }
        }
    }

    ... #9
}

#1 典型的double check
#2 遍歷全部的bean
#3 判斷是否爲合格的切面類
#4 是否爲切面類(是否有Aspect註解)
#5 獲取切面信息
#6 構建一系列的單元切面
#7 加入緩存
#8 加入結果
#9 緩存不爲空,從緩存中獲取數據返回

步驟#6調用ReflectiveAspectJAdvisorFactory.getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);

    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);    // #1

    List<Advisor> advisors = new LinkedList<Advisor>();
    for (Method method : getAdvisorMethods(aspectClass)) {    // #2
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);    // #3
        if (advisor != null) {
            advisors.add(advisor);
        }
    }
    ...
    return advisors;
}

#1 lazySingletonAspectInstanceFactory對getAspectInstance進行了緩存,保證getAspectInstance方法返回單例
#2 getAdvisorMethods獲取全部沒有Pointcut註解的方法(有Pointcut註解的方法不多是Advisor)
#3 使用方法元數據構造單元切面

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
        int declarationOrderInAspect, String aspectName) {

    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

    AspectJExpressionPointcut expressionPointcut = getPointcut(
            candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());    // #1
    if (expressionPointcut == null) {
        return null;
    }

    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);    // #2
}

#1 獲取切入點Pointcut,並構建了AspectJExpressionPointcut
#2 建立單元切面InstantiationModelAwarePointcutAdvisorImpl,該類中包含屬性 --
被攔截類declaringClass,被攔截方法aspectJAdviceMethod,切入點declaredPointcut和通知instantiatedAdvice。

InstantiationModelAwarePointcutAdvisorImpl#構造方法 -> InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice -> ReflectiveAspectJAdvisorFactory#getAdvice

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    ... // #1

    AbstractAspectJAdvice springAdvice;

    switch (aspectJAnnotation.getAnnotationType()) {    // #2
        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;
        case AtAround:
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtPointcut:
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        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();
    return springAdvice;
}

#1 aspectInstanceFactory#getAspectMetadata獲取切面信息,再獲取對應的aspectJAnnotation
#2 根據切面類型,構建不一樣的通知實現類
@After對應AspectJAfterAdvice
@Around對應AspectJAroundAdvice
@Before對應AspectJMethodBeforeAdvice
@AfterReturning對應AspectJAfterReturningAdvice
@AfterThrowing對應AspectJAfterThrowingAdvice
除了AspectJAroundAdvice,這些通知實現類都實現了MethodInterceptor(方法攔截器接口)。

findAdvisorsThatCanApply 過濾單元切面

這裏會遍歷全部的單元切面,檢查bean的class和class中是否有方法能夠匹配對應的單元切面,若是沒有則過濾該單元切面。代碼比較繁瑣,不展開了。

extendAdvisors(eligibleAdvisors)擴充單元切面

AspectJAwareAdvisorAutoProxyCreator.extendAdvisors -> AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary

spring會在advisors列表開始位置添加ExposeInvocationInterceptor。
ExposeInvocationInterceptor是一個特殊的MethodInterceptor,它將當前方法攔截器調用鏈放置到線程上下文中,以便有須要時使用。

建立代理對象

咱們知道,spring經過動態代理類實現aop,有jdk動態代理和cglib兩種方法。
若是要使用jdk動態代理,被代理類必須實現一個接口。
這裏只關注jdk動態代理如何實現aop。

AbstractAutoProxyCreator#wrapIfNecessary方法#2步驟 -> AbstractAutoProxyCreator.createProxy(第三個參數就是Advices)

protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

    ...
    
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);    // #1
    for (Advisor advisor : advisors) {
        proxyFactory.addAdvisor(advisor);    // #2
    }

    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    return proxyFactory.getProxy(getProxyClassLoader());    // #3
}

#1 類型檢查及轉化,如將Advices轉換成Advisor
#2 將Advisor添加到proxyFactory中
#3 構造代理對象

proxyFactory.getProxy -> ProxyCreatorSupport.createAopProxy

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    return getAopProxyFactory().createAopProxy(this);    // #1
}

#1 構造代理對象,createAopProxy方法參數是this,將proxyFactory將自身做爲參數,

DefaultAopProxyFactory#createAopProxy

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {    // #1
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {    // #2
        return new JdkDynamicAopProxy(config);
    }
}

#1 根據用戶配置和目標bean是否有實現接口,判斷是否須要使用ObjenesisCglibAopProxy
#2 使用JDK動態代理

來看看JdkDynamicAopProxy#getProxy

public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);    // #1
}

#1 這裏構造了jdk的動態代理對象,注意newProxyInstance方法第三個參數是this,即JdkDynamicAopProxy,JdkDynamicAopProxy實現了InvocationHandler。

Spring AOP源碼解析內容過長,關於多個通知鏈式調用的解析留到下一篇文章解析

若是您以爲本文不錯,歡迎關注個人微信公衆號,您的關注是我堅持的動力!

相關文章
相關標籤/搜索