SpringBoot AoP(3)切面方法解析

273 阅读3分钟

前言

依赖注入完成之后,初始化方法会调用各个后置处理器的postProcessAfterInitialization()方法,当为CommonAnnotationBeanPostProcessor的时候,会实现@PostConstruct 标记的方法。
引入aspect jar包之后,会多出一个后置处理器AnnotationAwareAspectJAutoProxyCreator,说明aop的实现逻辑在此后置处理器的postProcessAfterInitialization()方法中。

论述

AbstractAutoProxyCreator->postProcessAfterInitialization():
    if (this.earlyProxyReferences.remove(cacheKey) != bean) {
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
AbstractAutoProxyCreator->wrapIfNecessary():
    // 如果有的话,就创建代理
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
AbstractAdvisorAutoProxyCreator->getAdvicesAndAdvisorsForBean():
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);	
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
AbstractAdvisorAutoProxyCreator->findEligibleAdvisors():
    // 找切面方法,已在上篇分析过
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 解析切面方法,然后遍历bean方法,看是否符合切面表达式
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    ..........
    return eligibleAdvisors;
AbstractAdvisorAutoProxyCreator->findAdvisorsThatCanApply():
    return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
AopUtils->findAdvisorsThatCanApply():
    for (Advisor candidate : candidateAdvisors) {
        // 如果为true的话,就将该切面方法添加到eligibleAdvisors集合
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
AopUtils->canApply():
    // advisor一般是PointcutAdvisor的子类
    return canApply(pca.getPointcut(), targetClass, hasIntroductions);

此篇仅讲述解析切面方法的部分,下面以@Around("doCut()") public int doAround()方法为例

AopUtils->canApply():
    // 这里matches方法一般都返回true,但是getClassFilter第一次调用时,会解析切面方法
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }
    ......
// 遍历切面方法candidateAdvisors的过程中,pc.getClassFilter()在一系列调用栈之后,会走到这里
PointcutParser->parsePointcutExpression():
    // expression是注解值,inScope是aop类
    Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);
PointcutParser->resolvePointcutExpression():
    PatternParser parser = new PatternParser(expression);
    parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
    // 此处返回ReferencePointcut对象
    Pointcut pc = parser.parsePointcut();
    validateAgainstSupportedPrimitives(pc, expression);
    IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);
    // 解析切入点doCut()
    pc = pc.resolve(resolutionScope);	
    return pc;
Pointcut->resolve():	
    this.resolveBindings(bindingResolutionScope, bindingTable);
ReferencePointcut->resolveBindings():
    // 根据所给name寻找是否有符合其名称的切点方法
    ResolvedPointcutDefinition pointcutDef = searchType.findPointcut(name);	// --1
    // 没有找到切点方法,会尝试从外部类寻找
    ......
    // 若外部类也没找到的话,就会报错了
    if (pointcutDef == null) {
        scope.message(IMessage.ERROR, this, "can't find referenced pointcut " + name);
        return;
    }
ResolvedType->findPointcut()	// --1
    for (Iterator<ResolvedMember> i = getPointcuts(); i.hasNext();) {
        ResolvedPointcutDefinition f = (ResolvedPointcutDefinition) i.next();
        if (f != null && name.equals(f.getName())) {
            // 如果方法名和注解中的值一样的话,就返回,否则继续寻找
            return f;
        }
    }
    //  i.hasNext()先在所给aop类中找寻切点方法,然后从其父类中寻找
    ......
// 一系列调用栈之后,会走到这里
Java15ReflectionBasedReferenceTypeDelegate->getDeclaredPointcuts():
    // 获取带有@PointCut注解的方法,封装成PointCut对象
    Pointcut[] pcs = this.myType.getDeclaredPointcuts();
    ......
    // 得到注解值并解析
    String pcExpr = pcs[i].getPointcutExpression().toString();
    org.aspectj.weaver.patterns.Pointcut pc = parser.resolvePointcutExpression(pcExpr, getBaseClass(), parameters[i]);
AjTypeImpl->getDeclaredPointcuts():
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
        // asPointcut()方法会查方法中是否有@Pointcut注解,有则返回Pointcut对象实例,否则返回空
        Pointcut pc = asPointcut(method);
        if (pc != null) pointcuts.add(pc);
    }

Java15ReflectionBasedReferenceTypeDelegate->getDeclaredPointcuts()方法中,得到Pointcut对象数组后,会对表达式进行解析,继续调用Pointcut->resolvePointcutExpression()方法,parser.parsePointcut()方法根据表达式返回不同类型的Pointcut,@annotation表达式返回AnnotationPointcut对象,之后pc.resolve(resolutionScope)也不相同 分支2中通过this.myType.getDeclaredPointcuts()方法得到的Pointcut对象数组构成

总结

在找到切面方法之后,会对每一个切面方法解析和验证,该过程只进行一次(从AspectJExpressionPointcut->buildPointcutExpression()方法开始)

  1. @Around("doCut()"):解析doCut()表达式,得知是一个方法,从OperationAop类寻找带有@Pointcut注解的方法
  2. @Pointcut("@annotation(com.hx.anno.HodayDouble)"):解析@annotation(com.hx.anno.HodayDouble)表达式,得知是一个注解,验证是否有HodayDouble这个注解
  3. 验证带有@Pointcut注解方法名是否叫doCut(),如果是,返回,如果不是,从OperationAop父类寻找带有@Pointcut注解的方法,重复步骤2-3
  4. 如果没有的话,从OperationAop外部类寻找带有@Pointcut注解的方法,重复步骤2-4
  5. 结束,验证成功继续进行,验证失败抛出异常