• AOP原理分析《四》- 获取增强器


    增强方法的获取代码:

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        // 查找"候选的"增强方法
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
    
        // 查找适应当前bean的增强方法
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    
        // 拓展advisors
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            // 排序
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    对于增强方法的处理,首先是获取了"候选的"增强方法,然后缩小范围,获取了适应当前bean使用的增强方法。
    所谓的适配就是指是不是符合切入点表达式。
    这里可以猜测到获取"候选的"增强肯定是被缓存起来的。

    获取增强器

    获取候选的增强器
    public List<Advisor> findAdvisorBeans() {
        // 是否已经被缓存过
        String[] advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            // 查找容器中所有实现了Advisor的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<>();
        for (String name : advisorNames) {
            // 实例化Advisor类型的bean,并把它们加入到advisors中
            advisors.add(this.beanFactory.getBean(name, Advisor.class));
        }
        return advisors;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    查找适配当前bena的增强器
    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new ArrayList<>();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                eligibleAdvisors.add(candidate);
            }
        }
        boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                // already processed
                continue;
            }
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    看了方法的内容无非就是遍历"候选增强器",虽然是2个循环,但是主要不同点是canApply方法的参数不同。
    那么需要看下canApply方法。

    public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
        if (advisor instanceof IntroductionAdvisor) {
            return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
        }
        else if (advisor instanceof PointcutAdvisor) {
    		// 一般都是这个类型
            PointcutAdvisor pca = (PointcutAdvisor) advisor;
            return canApply(pca.getPointcut(), targetClass, hasIntroductions);
        }
        else {
            // It doesn't have a pointcut so we assume it applies.
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    canApply方法分类判断了IntroductionAdvisor类型和PointcutAdvisor类型2种情况。
    IntroductionAdvisor类型的直接就执行了matches方法,而PointcutAdvisor代码如下:

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    
        // 添加目标类的所有接口,既然后续查找了所有的方法那么这是有必要???
        classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    
        for (Class<?> clazz : classes) {
            // 递归查找 clazz的所有方法
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method : methods) {
                if (introductionAwareMethodMatcher != null ?
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    看到最后我们还是发现最后是调用了matches方法进行匹配,如果匹配上说明增强器适配当前的bena,匹配不上则不适配。而对于matches就不什么研究了。
    猜测是matches调用了底层aop表达式解析方法进行了适配。

    到这里我们不免提出几个疑问:

    • @Before等注解在哪里处理的,肯定是我们在哪里遗漏了
    • 我们多次在"增强器适配"的方法中看到了分类谈论,如按candidate类型分类而且似乎就是分为2类
      下面讲解决一下上面的几个疑问。

    传送门:保姆式Spring5源码解析

    欢迎与作者一起交流技术和工作生活

    联系作者

  • 相关阅读:
    振弦采集模块读取传感器频率值的问题
    SpringBoot使用
    Python | 快速获取某一列数组中前 N 个最大值/最小值的索引 | 三种方法总结
    Vue3+vite中引入Echarts图表
    redis详细解析和配置选择
    你不知道的Set集合
    加密和验签
    【微服务】Nacos2.x服务发现?RPC调用?重试机制?
    很普通的四非生,保研破局经验贴
    树链剖分模板
  • 原文地址:https://blog.csdn.net/yuchangyuan5237/article/details/126816009