【Spring AOP】【三】Spring AOP源码解析-注解方式加载解析过程

1  前言

上一篇我们讲解了XML方式解析AOP的配置,这篇我们来解析下注解方式的解析过程。

2  代码准备

@Component
@Aspect
public class MyAspect {

    @Pointcut("execution( * com.virtuous.demo.spring.cycle.A.*(..))")
    public void myPointCut() {
    }


    @After("myPointCut()")
    public void myAfter(JoinPoint point) {
        System.out.println("myAfter");
    }
  
    @Before("myPointCut()")
    public void myBefore(JoinPoint point) {
        System.out.println("myBefore");
    }
}
<context:component-scan base-package="com.virtuous.demo.spring.cycle"/>
<!-- 这行有点关键了  由于我们是测试 这行表示启动自动代理 会加载对AOP注解相关解析的类-->
<aop:aspectj-autoproxy/>
@Component
public class A {
    public void say() {
        System.out.println("111");
    }
}
public class ApplicationContextTest {
    @Test
    public void testFactoryBean() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring/spring-alias.xml");
        A a = applicationContext.getBean("a", A.class);
        a.say();

    }
}

3  源码分析

注解方式的加载,可能以前用过@EnableAspectJAutoProxy,和我们在XML里配置的<aop:aspectj-autoproxy/>都是为了加载一个类AnnotationAwareAspectJAutoProxyCreator,在我调试的时候发现这个类并不是主动加载解析的,也就是不是在启动的时候去主动解析Aspect的,而是在createBean的时候,通过initializeBean方法里的后置处理作为入口,来对当前的bean寻找合适的通知器的,所以我们就主要分析下findCandidateAdvisors方法。

(1)AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors()

@Override
    protected List<Advisor> findCandidateAdvisors() {
        // 先调用父类的加载 AbstractAdvisorAutoProxyCreator的findCandidateAdvisors
        // 父类的加载是从bean工厂里收集Advisor类型的bean
        List<Advisor> advisors = super.findCandidateAdvisors();
        // 解析 @Aspect 注解,并构建通知器
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }

(2)进入父类的通知器查询

 (3)进入advisorRetrievalHelper的findAdvisorBeans()方法

public List<Advisor> findAdvisorBeans() {
        // 缓存判断
        String[] advisorNames = this.cachedAdvisorBeanNames;
     // 如果 cachedAdvisorBeanNames 为空,这里到容器中查找,并设置缓存,后续直接使用缓存即可 
if (advisorNames == null) { // 看方法名字大概猜从Bean工厂里找到Advisor类型的bean的所有beanNames advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { return new ArrayList<>(); } List<Advisor> advisors = new ArrayList<>();
// 遍历 advisorNames
for (String name : advisorNames) { if (isEligibleBean(name)) {
// 忽略正在创建中的 advisor bean
if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isTraceEnabled()) { logger.trace("Skipping currently created advisor '" + name + "'"); } } else { try { // 看这个 主要就是从Bean工厂里找Advisor类型的bean advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; String bceBeanName = bce.getBeanName(); if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { if (logger.isTraceEnabled()) { logger.trace("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } continue; } } throw ex; } } } } return advisors; }

(4)然后是解析注解类型的进入aspectJAdvisorsBuilder的buildAspectJAdvisors():

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<>();
                aspectNames = new ArrayList<>();
                // 从容器中获取所有 bean 的名称
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                for (String beanName : beanNames) {
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    // 根据 beanName 获取 bean 的类型忽略为空的
                    Class<?> beanType = this.beanFactory.getType(beanName, false);
                    if (beanType == null) {
                        continue;
                    }
                    // 看这个 检测 beanType 是否包含 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);
                            // 获取通知器
                            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();
    }
    List<Advisor> advisors = new ArrayList<>();
    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;
}

可以看到该方法里会找到Aspect注解的bean然后进行通知器的解析,大概方法的执行流程是:

  1. 获取容器中所有 bean 的名称(beanName)
  2. 遍历上一步获取到的 bean 名称数组,并获取当前 beanName 对应的 bean 类型(beanType)
  3. 根据 beanType 判断当前 bean 是否是一个的 Aspect 注解类,若不是则不做任何处理
  4. 调用 advisorFactory.getAdvisors 获取通知器

 下面我们来重点分析advisorFactory.getAdvisors(factory)这个调用,如下:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 获取 aspectClass 和 aspectName
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
    List<Advisor> advisors = new ArrayList<>();
    // getAdvisorMethods 用于返回不包含 @Pointcut 注解的方法
    for (Method method : getAdvisorMethods(aspectClass)) {
        // 为每个方法分别调用 getAdvisor 方法
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, 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;
}
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
        int declarationOrderInAspect, String aspectName) {
    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    // 获取切点实现类
    AspectJExpressionPointcut expressionPointcut = getPointcut(
            candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }
    // 创建 Advisor 实现类
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

如上,getAdvisor 方法包含两个主要步骤,一个是获取 AspectJ 表达式切点,另一个是创建 Advisor 实现类。在第二个步骤中,包含一个隐藏步骤 -- 创建 Advice。下面我将按顺序依次分析这两个步骤,先看获取 AspectJ 表达式切点的过程,如下:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 获取方法上的 AspectJ 相关注解,包括 @Before,@After,@Around等
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }
    // 创建一个 AspectJExpressionPointcut 对象
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    // 设置切点表达式
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    if (this.beanFactory != null) {
        ajexp.setBeanFactory(this.beanFactory);
    }
    return ajexp;
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
        Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
        AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
        if (foundAnnotation != null) {
            return foundAnnotation;
        }
    }
    return null;
}

说完切点的获取过程,下面再来看看 Advisor 实现类的创建过程。如下:

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 PerTargetInstantiationModelPointcut(
                this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
        this.lazy = true;
    }
    else {
        this.pointcut = this.declaredPointcut;
        this.lazy = false;
        // 上边是初始化,具体每个参数代表什么我没看,重点看看这个方法做了什么
        this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
    }
}

instantiateAdvice(this.declaredPointcut),这个方法用于创建通知 Advice。那下面我们就来看看构建通知的过程是怎样的,如下:

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();
    validate(candidateAspectClass);
    // 获取Advice 注解
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    // 没有就直接返回
    if (aspectJAnnotation == null) {
        return null;
    }
    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;
    // 按照注解类型生成相应的 Advice 实现类
    switch (aspectJAnnotation.getAnnotationType()) {
        // 这个应该走不到这里 因为切点在前边已经创建了其实
        case AtPointcut:
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        // @Around -> AspectJAroundAdvice
        case AtAround:
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        // @Before -> AspectJMethodBeforeAdvice
        case AtBefore:
            springAdvice = new AspectJMethodBeforeAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        // @After -> AspectJAfterAdvice
        case AtAfter:
            springAdvice = new AspectJAfterAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        // @AfterReturning -> AspectJAfterAdvice
        case AtAfterReturning:
            springAdvice = new AspectJAfterReturningAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;
        // @AfterThrowing -> AspectJAfterThrowingAdvice
        case AtAfterThrowing:
            springAdvice = new AspectJAfterThrowingAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;
        default:
            throw new UnsupportedOperationException(
                    "Unsupported advice type on method: " + candidateAdviceMethod);
    }
    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    /*
     * 获取方法的参数列表名称,比如方法 int sum(int numX, int numY),
     * getParameterNames(sum) 得到 argNames = [numX, numY]
     */
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        // 设置参数名
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();
    return springAdvice;
}

上面的代码逻辑不是很复杂,主要的逻辑就是根据注解类型生成与之对应的通知对象。下面来总结一下获取通知器(getAdvisors)整个过程的逻辑,如下:

  1. 从目标 bean 中获取不包含 Pointcut 注解的方法列表
  2. 遍历上一步获取的方法列表,并调用 getAdvisor 获取当前方法对应的 Advisor
  3. 创建 AspectJExpressionPointcut 对象,并从方法中的注解中获取表达式,最后设置到切点对象中
  4. 创建 Advisor 实现类对象 InstantiationModelAwarePointcutAdvisorImpl
  5. 调用 instantiateAdvice 方法构建通知
  6. 调用 getAdvice 方法,并根据注解类型创建相应的通知

如上就是通知器的一个创建过程,其中有一些方法或者变量没去细扣,主要是从源码角度了解解析的一个大概时机和过程。

通知有5个,我们就其中一个的创建大概看一下里边,拿@After来看:

public class AspectJAfterAdvice extends AbstractAspectJAdvice
        implements MethodInterceptor, AfterAdvice, Serializable {
    public AspectJAfterAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }
    @Override
    @Nullable
    public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            // 这里留个todo 好像是跟调用链有关系 后续看
            return mi.proceed();
        }
        finally {
            // 调用通知方法
            invokeAdviceMethod(getJoinPointMatch(), null, null);
        }
    }

    @Override
    public boolean isBeforeAdvice() {
        return false;
    }

    @Override
    public boolean isAfterAdvice() {
        return true;
    }
}
protected Object invokeAdviceMethod(
        @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
        throws Throwable {
    // 调用通知方法,并向其传递参数
    return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
// As above, but in this case we are given the join point.
protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
        @Nullable Object returnValue, @Nullable Throwable t) throws Throwable {
    return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterCount() == 0) {
        actualArgs = null;
    }
    try {
        // 通过反射调用通知方法
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    }
    catch (IllegalArgumentException ex) {
        throw new AopInvocationException("Mismatch on arguments to advice method [" +
                this.aspectJAdviceMethod + "]; pointcut expression [" +
                this.pointcut.getPointcutExpression() + "]", ex);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    }

4 小结

我们本文讲解了注解方式配置的AOP是如何进行查找的,那么下节我们讲解是怎么代理对象的以及多个代理又是怎么执行先后的。

posted @ 2023-02-20 07:36  酷酷-  阅读(151)  评论(0编辑  收藏  举报