spring aop —— 深入理解advisor

之前的一篇博客,我們簡單分析了下aop是如何創(chuàng)建代理對象的。這一篇博客會繼續(xù)介紹spring的aop的一些概念,通知、切面。以及相對應的源碼實現(xiàn)。

aop配置

開啟aop到代理類生成這篇博客里,我們舉了一個例子。

//聲明這是一個組件
@Component
//聲明這是一個切面Bean
@Aspect
@Slf4j
public class ServiceAspect {

    //配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點
    @Pointcut("execution(* com.hdj.learn.spring.aop.service..*(..))")
    public void aspect() {
    }

    /*
     * 配置前置通知,使用在方法aspect()上注冊的切入點
     * 同時接受JoinPoint切入點對象,可以沒有該參數(shù)
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint) {
        log.info("before " + joinPoint);
    }

    //配置后置通知,使用在方法aspect()上注冊的切入點
    @After("aspect()")
    public void after(JoinPoint joinPoint) {
        log.info("after " + joinPoint);
    }

    //配置環(huán)繞通知,使用在方法aspect()上注冊的切入點
    @Around("aspect()")
    public void around(JoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        try {
            ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
        }
    }

    //配置后置返回通知,使用在方法aspect()上注冊的切入點
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint) {
        log.info("afterReturn " + joinPoint);
    }

    //配置拋出異常后通知,使用在方法aspect()上注冊的切入點
    @AfterThrowing(pointcut = "aspect()", throwing = "ex")
    public void afterThrow(JoinPoint joinPoint, Exception ex) {
        log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
    }

}

首先,我們思考使用aop的目的,就是不改變源碼的前提下,往一個方法的前后插入一個代碼塊 了解這個后,再看一次這個配置就很清楚了,我們需要知道:
1)需要往哪個類的哪個方法前后插入代碼塊?
2)究竟需要插入什么樣的代碼

比如:

@Pointcut("execution(* com.hdj.learn.spring.aop.service..*(..))")
public void aspect() {
}

這個配置說明了,需要對com.hdj.learn.spring.aop.service這個包及其子包下的任意類的任意方法 進行代理。

@Before("aspect()")
public void before(JoinPoint joinPoint) {
    log.info("before " + joinPoint);
}

這個配置,說明要在被代理的方法前,插入一段代碼:
log.info("before " + joinPoint);

了解這些后,就可以帶著問題開始分析這些配置:
1)ServiceAspect 這些配置,是如何被讀取到spring里的
2)spring是如何應用這些配置,生成代理類的

Aspect 的配置如何讀取到spring中

流程比較長,這里就不貼代碼了,說下結(jié)論,spring在真正createBean方法前,會調(diào)用各個BeanPostProcessor ,在各個BeanPostProcessor里,有一個AnnotationAwareAspectJAutoProxyCreator,spring會在該類的 postProcessBeforeInitialization 里進行Advisor的初始化

可以這樣理解,spring在創(chuàng)建一個類之前,會看下有沒有配置aop啊,如果有的話,會把配置給轉(zhuǎn)換成一個個advisor,然后緩存起來(這樣后面需要生成代理類時候,就可以直接使用了)


findCandidateAdvisors的方式有兩種,一種是上圖第7步的findAdvisorBeans還有一種是第8步的buildAspectJAdvisors

findAdvisorBeans方式

代碼很長,其實去掉那些讀緩存的代碼,就一句話:
advisors.add(this.beanFactory.getBean(name, Advisor.class));
找到實現(xiàn)了Advisor接口的類,并返回。(這里沒有講的很詳細,有時間補充下細節(jié) todo)

public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = null;
    synchronized (this) {
        advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the auto-proxy creator apply to them!
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
    }
    if (advisorNames.length == 0) {
        return new LinkedList<>();
    }

    List<Advisor> advisors = new LinkedList<>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    //核心方法 <-------------------------
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Skipping advisor '" + name +
                                        "' with dependency on currently created bean: " + ex.getMessage());
                            }
                            // Ignore: indicates a reference back to the bean we're trying to advise.
                            // We want to find advisors other than the currently created bean itself.
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }
    return advisors;
}

動態(tài)生成advisors方式

我們的ServiceAspect例子里,并沒有實現(xiàn)任何接口,只是使用了一個 @Aspect注解。因此不能使用上述方式,spring會通過我們的AspectJ注解(比如@Pointcut、@Before) 動態(tài)的生成各個Advisor

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = null;

    synchronized (this) {
        aspectNames = this.aspectBeanNames;
        if (aspectNames == null) {
            List<Advisor> advisors = new LinkedList<>();
            aspectNames = new LinkedList<>();
            //找到所有的類(因為是Object所以基本上就是所有被spring管理的類)
            String[] beanNames =
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
            for (String beanName : beanNames) {
                if (!isEligibleBean(beanName)) {
                    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;
                }
                //是否是Aspect(比如含有@Aspect注解)
                if (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);
                        //生成Advisor
                        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 {
                        // 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;
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new LinkedList<>();
    for (String aspectName : aspectNames) {
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    return advisors;
}

流程挺長的,總結(jié)下:
1)找到所有被spring管理的類(父類是Object的類)
2)如果類含有 @Aspect 注解,調(diào)用advisorFactory.getAdvisors方法生成對應的advisor
3)返回advisors

我們繼續(xù)看看,最核心的,Advisor的創(chuàng)建

Advisor的創(chuàng)建

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors

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

    // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
    // so that it will only instantiate once.
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new LinkedList<>();
    //遍歷所有沒有 @Pointcut注解的方法
    for (Method method : getAdvisorMethods(aspectClass)) {
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    // If it's a per target aspect, emit the dummy instantiating aspect.
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    // Find introduction fields.
    for (Field field : aspectClass.getDeclaredFields()) {
        Advisor advisor = getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    return advisors;
}

最核心的,就是遍歷所有的 沒有Pointcut注解的方法,調(diào)用getAdvisor生成對應的Advisor

也就是,生成的Advisor的實現(xiàn)類,其實是 InstantiationModelAwarePointcutAdvisorImpl

不同類型通知

使用過Aop都知道,不同的注解,比如@Before、@After、@Around 都是不一樣的。InstantiationModelAwarePointcutAdvisorImpl 這個類,實際上,是對底層Advisor的包裝,它記錄了所對應 @AspectJ的類、配置的方法、對應的切入點、以及最重要的通知,這個通知會在InstantiationModelAwarePointcutAdvisorImpl的構(gòu)造函數(shù)中被初始化。

private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
            this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice

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

    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    validate(candidateAspectClass);

    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // If we get here, we know we have an AspectJ method.
    // Check that it's an AspectJ-annotated class
    if (!isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: " +
                "Offending method '" + candidateAdviceMethod + "' in class [" +
                candidateAspectClass.getName() + "]");
    }

    if (logger.isDebugEnabled()) {
        logger.debug("Found AspectJ method: " + candidateAdviceMethod);
    }

    AbstractAspectJAdvice springAdvice;

    switch (aspectJAnnotation.getAnnotationType()) {
        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;
}

很簡單,spring會根據(jù)不同的注解的類型,生成對應的Advice

到了這里,簡單的總結(jié)下,spring會在真正創(chuàng)建一個類之前,根據(jù)我們帶有@Aspect類的配置生成對應的 Advise 對象,這些對象會被緩存起來。在這之后,就是在spring創(chuàng)建完bean后,根據(jù)這個bean生成對應的代理對象,并替換掉(也就是說,實際調(diào)用方法時候調(diào)用的對象變?yōu)檫@個生成的代理對象) 代理對象的創(chuàng)建,已經(jīng)在這一篇博客里講過了,這里就不在說明了,補充一點,生成的代理的方法。

代理方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //省略...

    try {
        //equals 方法
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        //hashCode方法
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        //如果是 DecoratingProxy類
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        //實現(xiàn)了Advised接口
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        if (this.advised.exposeProxy) {
            // ThreadLocal里記錄下當前被代理的對象
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // May be null. Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }

        // 核心方法,獲取當前方法的攔截器
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // We need to create a method invocation...
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            //調(diào)用這些攔截器及方法
            retVal = invocation.proceed();
        }

        // Massage return value if necessary.
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

方法很長,總結(jié)下:
1)hashCode、equals方法單獨處理
2)根據(jù)當前方法等,生成所需的方法攔截器
3)調(diào)用方法及攔截器

ReflectiveMethodInvocation.proceed() 方法有點長,就不分析了,大概邏輯是:
1)會根據(jù)我們之前生成的各個Advisor對應的切入點,判斷下當前的方法是否滿足該切入點。如果滿足,將其適配為MethodInterceptor 接口并返回。
2)核心調(diào)用邏輯,就是取出一個個攔截器,先判斷下方法是否滿足攔截器條件,如果滿足就調(diào)用。

這一步還是很復雜的,相關(guān)的設計模式也挺值得學習的,后續(xù)有時間單獨寫個博客分析下。

總結(jié)

到了這里,spring aop advisor相關(guān)應該算是解釋清楚了??偨Y(jié)下:
1)在創(chuàng)建spring之前,spring會根據(jù)我們的配置(可能是xml、可能是注解)生成一個個Advisor
2)創(chuàng)建對應的對象
3)將創(chuàng)建的對象替換為我們對應的代理對象

而生成的代理的方法,其實就是遍歷一個個的方法攔截器(通過第一步的Advisor生成/ 適配器模式)然后一個個調(diào)用

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

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