【Spring AOP】【五】Spring AOP源码解析-筛选合适的通知器

1  前言

我们了解过AOP配置解析以及AOP的切入时机,那么这篇我们看看,AOP是如何给某个Bean筛选合适的通知器。

2  源码分析

2.1  方法通读

我们从AOP切入时机中,为当前bean筛选通知器开始继续看起:

protected Object[] getAdvicesAndAdvisorsForBean(
        Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    // 为当前的bean筛选合适的通知器
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    // 如果集合为空表示不需要代理
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 获取所有的通知器
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 筛选哪些是可以作用到当前bean的
    // 通过 ClassFilter 和 MethodMatcher 对目标类和方法进行匹配
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 扩展操作
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 通知器排序根据order
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

2.2  findAdvisorsThatCanApply

(1)派小弟AopUtils来筛选,我们继续往里走

protected List<Advisor> findAdvisorsThatCanApply(
        List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        // 派AopUtils来进行筛选
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

(2)进入findAdvisorsThatCanApply,接着进入canApply,判断通知器是否能作用到当前bean

// candidateAdvisors所有的通知器集合
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    // eligibleAdvisors存放IntroductionAdvisor类型的通知器并且可以用于当前bean的
    // IntroductionAdvisor这个类型的具体是干什么的暂时未知 先略过
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    // hasIntroductions表示是否有元素
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        // IntroductionAdvisor忽略IntroductionAdvisor类型的通知器
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        // 筛选除了IntroductionAdvisor类型的通知器并且可以作用到当前bean的
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

(3)canApply,判断通知器是否能作用到当前bean

上一篇解析AOP的时候,不知道你是否还记得我们的每个通知都被封装成了什么对象么,我们回忆下:

我们继续看源码:

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        /*
          从通知器中获取类型过滤器 ClassFilter,并调用 matchers 方法进行匹配。
          ClassFilter 接口的实现类 AspectJExpressionPointcut 为例,该类的
          匹配工作由 AspectJ 表达式解析器负责,也就是解析表达式判断当前bean符不符合,具体怎么解析的我们就不看了奥
         */
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        // 对于PointcutAdvisor类型的通知器,这里继续调用重载方法进行筛选 
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        // It doesn't have a pointcut so we assume it applies.
        return true;
    }
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 通知器的classFilter先进行匹配 也就是类级别的先匹配才能继续匹配方法
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }
    // 方法的匹配器
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        return true;
    }
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }
    /*
      查找当前类及其父类(以及父类的父类等等)所实现的接口,由于接口中的方法是 public,
      所以当前类可以继承其父类,和父类的父类中所有的接口方法
     */
    Set<Class<?>> classes = new LinkedHashSet<>();
    if (!Proxy.isProxyClass(targetClass)) {
        classes.add(ClassUtils.getUserClass(targetClass));
    }
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    for (Class<?> clazz : classes) {
        // 获取当前类的方法列表,包括从父类中继承的方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            // 使用 methodMatcher 匹配方法,匹配成功即可立即返回
            if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }
    return false;
}

以上是通知器筛选的过程,筛选的工作主要由 ClassFilter 和 MethodMatcher 完成。在 AOP 中,切点 Pointcut 是用来匹配连接点(看成是方法),以 AspectJExpressionPointcut 类型的切点为例。该类型切点实现了ClassFilter 和 MethodMatcher 接口,匹配的工作则是由 AspectJ 表达式解析器复杂。除了使用 AspectJ 表达式进行匹配,Spring 还提供了基于正则表达式的切点类,以及更简单的根据方法名进行匹配的切点类。

2.3  extendAdvisors拓展通知器

关于通知器的拓展我们看看里边做了什么:

// 派小弟AspectJProxyUtils来拓展
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // 如果通知器列表是一个空列表,直接返回
    if (!advisors.isEmpty()) {
        boolean foundAspectJAdvice = false;
        for (Advisor advisor : advisors) {
            // 检测 每个通知器advisor是否存在类型是否正确
            if (isAspectJAdvice(advisor)) {
                foundAspectJAdvice = true;
                break;
            }
        }
        // 往头部添加了一个默认的通知器DefaultPointcutAdvisor 我猜的哈 我怀疑是多个通知器组成链表 这个充当头吧 我猜的哈
        if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
            advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
            return true;
        }
    }
    return false;
}

可以看到拓展主要是做了一个类型检查,主要是往通知器集合里放了一个默认的通知器,暂时不知道起到什么作用,留着等会儿研究下。

3  小结

筛选通知器,可以看到我们的通知都是根据里边的表达式进行解析匹配的,优先匹配类,如果类都不匹配则不会匹配每个方法去,行,暂时先到这里,有理解不到位的地方欢迎大家指正,我去研究下他为啥要往通知器集合里放个默认的,以及我还没看到比如一个方法有多个通知器,这多个通知器是怎么串起来的,我研究去了哈。

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