AOP 源码系列一 后置处理

1,276 阅读5分钟

前言

会更新AOP 系列源码文章 主要更新目录

图片.png

篇幅问题 会分章程更新

注解原理

@EnableAspectJAutoProxy:AOP 注解驱动,给容器中导入 AspectJAutoProxyRegistrar

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    // 是否强制使用 CGLIB 创建代理对象 
    // 配置文件方式:<aop:aspectj-autoproxy proxy-target-class="true"/>
	boolean proxyTargetClass() default false;
	
    // 将当前代理对象暴露到上下文内,方便代理对象内部的真实对象拿到代理对象
    // 配置文件方式:<aop:aspectj-autoproxy expose-proxy="true"/>
	boolean exposeProxy() default false;
}

AspectJAutoProxyRegistrar 在用来向容器中注册 AnnotationAwareAspectJAutoProxyCreator,以 BeanDefiantion 形式存在,在容器初始化时加载。AnnotationAwareAspectJAutoProxyCreator 间接实现了 InstantiationAwareBeanPostProcessor,Order 接口,该类会在 Bean 的实例化和初始化的前后起作用

工作流程:创建 IOC 容器,调用 refresh() 刷新容器,registerBeanPostProcessors(beanFactory) 阶段,通过 getBean() 创建 AnnotationAwareAspectJAutoProxyCreator 对象,在生命周期的初始化方法中执行回调 initBeanFactory() 方法初始化注册三个工具类:BeanFactoryAdvisorRetrievalHelperAdapter、ReflectiveAspectJAdvisorFactory、BeanFactoryAspectJAdvisorsBuilderAdapter


后置处理

Bean 初始化完成的执行后置处理器的方法:

public Object postProcessAfterInitialization(@Nullable Object bean,String bN){
    if (bean != null) {
        // cacheKey 是 【beanName 或者加上 & 的 beanName】
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                // 去提前代理引用池中寻找该 key,不存在则创建代理
                // 如果存在则证明被代理过,则判断是否是当前的 bean,不是则创建代理
                return wrapIfNecessary(bean, bN, cacheKey);
            }
    }
    return bean;
}

AbstractAutoProxyCreator.wrapIfNecessary():根据通知创建动态代理,没有通知直接返回原实例

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 条件一般不成立,很少使用 TargetSourceCreator 去创建对象 BeforeInstantiation 阶段,doCreateBean 之前的阶段
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    // advisedBeans 集合保存的是 bean 是否被增强过了
    // 条件成立说明当前 beanName 对应的实例不需要被增强处理,判断是在 BeforeInstantiation 阶段做的
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    // 条件一:判断当前 bean 类型是否是基础框架类型,这个类的实例不能被增强
    // 条件二:shouldSkip 判断当前 beanName 是否是 .ORIGINAL 结尾,如果是就跳过增强逻辑,直接返回
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 【查找适合当前 bean 实例的增强方法】(下一节详解)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 条件成立说明上面方法查询到适合当前class的通知
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 根据查询到的增强创建代理对象(下一节详解)
        // 参数一:目标对象
        // 参数二:beanName
        // 参数三:匹配当前目标对象 clazz 的 Advisor 数据
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // 保存代理对象类型
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 返回代理对象
        return proxy;
    }
	// 执行到这里说明没有查到通知,当前 bean 不需要增强
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    // 【返回原始的 bean 实例】
    return bean;
}

获取通知

AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean():查找适合当前类实例的增强,并进行排序

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
	// 查询适合当前类型的增强通知
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        // 增强为空直接返回 null,不需要创建代理
        return DO_NOT_PROXY;
    }
    // 不是空,转成数组返回
    return advisors.toArray();
}

属性解读

AbstractAdvisorAutoProxyCreator.findEligibleAdvisors():

  • candidateAdvisors = findCandidateAdvisors()获取当前容器内可以使用(所有)的 advisor,调用的是 AnnotationAwareAspectJAutoProxyCreator 类的方法,每个方法对应一个 Advisor

    • advisors = super.findCandidateAdvisors()查询出 XML 配置的所有 Advisor 类型

      • advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors():通过 BF 查询出来 BD 配置的 class 中 是 Advisor 子类的 BeanName
      • advisors.add():使用 Spring 容器获取当前这个 Advisor 类型的实例
    • advisors.addAll(....buildAspectJAdvisors())获取所有添加 @Aspect 注解类中的 Advisor

      buildAspectJAdvisors():构建的方法,把 Advice 封装成 Advisor

      • beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false):获取出容器内 Object 所有的 beanName,就是全部的

      • for (String beanName : beanNames):遍历所有的 beanName,判断每个 beanName 对应的 Class 是否是 Aspect 类型,就是加了 @Aspect 注解的类

        • factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName):使用工厂模式管理 Aspect 的元数据,关联的真实 @Aspect 注解的实例对象

        • classAdvisors = this.advisorFactory.getAdvisors(factory):添加了 @Aspect 注解的类的通知信息

          • aspectClass:@Aspect 标签的类的 class

          • for (Method method : getAdvisorMethods(aspectClass)):遍历不包括 @Pointcut 注解的方法

            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName)将当前 method 包装成 Advisor 数据

            • AspectJExpressionPointcut expressionPointcut = getPointcut():获取切点表达式

            • return new InstantiationModelAwarePointcutAdvisorImpl():把 method 中 Advice 包装成 Advisor,Spring 中每个 Advisor 内部一定是持有一个 Advice 的,Advice 内部最重要的数据是当前 method 和aspectInstanceFactory,工厂用来获取实例

              this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut):实例化 Advice 对象,逻辑是获取注解信息,根据注解的不同生成对应的 Advice 对象

        • advisors.addAll(classAdvisors):保存通过 @Aspect 注解定义的 Advisor 数据

      • this.aspectBeanNames = aspectNames:将所有 @Aspect 注解 beanName 缓存起来,表示提取 Advisor 工作完成

      • return advisors:返回 Advisor 列表

  • eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, ...)选出匹配当前类的增强

    • if (candidateAdvisors.isEmpty()):条件成立说明当前 Spring 没有可以操作的 Advisor

    • List<Advisor> eligibleAdvisors = new ArrayList<>():存放匹配当前 beanClass 的 Advisors 信息

    • for (Advisor candidate : candidateAdvisors)遍历所有的 Advisor

      if (canApply(candidate, clazz, hasIntroductions)):判断遍历的 advisor 是否匹配当前的 class,匹配就加入集合

      • if (advisor instanceof PointcutAdvisor):创建的 advisor 是 InstantiationModelAwarePointcutAdvisorImpl 类型

        PointcutAdvisor pca = (PointcutAdvisor) advisor:封装当前 Advisor

        return canApply(pca.getPointcut(), targetClass, hasIntroductions):重载该方法

        • if (!pc.getClassFilter().matches(targetClass))类不匹配 Pointcut 表达式,直接返回 false

        • methodMatcher = pc.getMethodMatcher()获取 Pointcut 方法匹配器,类匹配进行类中方法的匹配

        • Set<Class<?>> classes:保存目标对象 class 和目标对象父类超类的接口和自身实现的接口

        • if (!Proxy.isProxyClass(targetClass)):判断当前实例是不是代理类,确保 class 内存储的数据包括目标对象的class 而不是代理类的 class

        • for (Class<?> clazz : classes)检查目标 class 和上级接口的所有方法,查看是否会被方法匹配器匹配,如果有一个方法匹配成功,就说明目标对象 AOP 代理需要增强

          • specificMethod = AopUtils.getMostSpecificMethod(method, targetClass):方法可能是接口的,判断当前类有没有该方法
          • return (specificMethod != method && matchesMethod(specificMethod))类和方法的匹配,不包括参数
  • extendAdvisors(eligibleAdvisors):在 eligibleAdvisors 列表的索引 0 的位置添加 DefaultPointcutAdvisor,封装了 ExposeInvocationInterceptor 拦截器

  • eligibleAdvisors = sortAdvisors(eligibleAdvisors)对拦截器进行排序,数值越小优先级越高,高的排在前面

    • 实现 Ordered 或 PriorityOrdered 接口,PriorityOrdered 的级别要优先于 Ordered,使用 OrderComparator 比较器
    • 使用 @Order(Spring 规范)或 @Priority(JDK 规范)注解,使用 AnnotationAwareOrderComparator 比较器
    • ExposeInvocationInterceptor 实现了 PriorityOrdered ,所以总是排在第一位,MethodBeforeAdviceInterceptor 没实现任何接口,所以优先级最低,排在最后
  • return eligibleAdvisors:返回拦截器链

结尾

后置处理 算是AOP的第一步理解 需要大概有点AOP思想基础才能 看明白系列文章

本文正在参加「金石计划 . 瓜分6万现金大奖」