Spring AOP源码解析(二)

前言

  上一篇中我们扒拉了一下spring-aop的底裤,详细解读了spring-aop中的各种抽象概念,本篇开始我们一起研究研究spring-aop的织入过程(基于5.2.6.RELEASE)。建议各位同学本地打开一份源码对照食用,效果更佳。

  前方高能!!!本篇重度依赖于上一篇解读的抽象概念,不熟悉的同学请速速撤离,以免误伤。

  正式开始之前,各位同学还请思考一下,在拥有了关于AOP的全局视角之后,如果是你,会怎样有机地结合这些概念来实现织入呢?我听到有同学说利用BeanPostProcessor。嗯,不错,Bean后置处理器给我们提供了这样一个切入点——可以在Bean自动装配完毕、行将可用之前对它进行定制,如果我们在这里返回一个代理对象,那么它就会取代原始对象。具体来说,就是在BeanPostProcessor#postProcessAfterInitialization(...)回调中完成织入并返回新创建的代理对象。那么,真的是这样吗?

一切从EnableAspectJAutoProxy说起

  EnableAspectJAutoProxy的作用自不必多说,不过我们今天深入一点,探究一下它的源码。这个说白了就是看它通过Import元注解向容器中导入了些什么,我们知道Spring中EnableXXX类型的注解都是代理给Import元注解向容器中导入功能组件的。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    /**
     * 是基于JDK动态代理还是CGLIB
     */
    boolean proxyTargetClass() default false;

    /**
     * 是否需要暴露代理对象到ThreadLocal中
     */
    boolean exposeProxy() default false;
}

注意到注解中的两个属性也存在于Advised接口,而Advised又保存着AOP配置信息,因此我们可以大胆地猜测这两个属性值最终会同步给Advised

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                      BeanDefinitionRegistry registry) {
        // 向容器中导入一个类型为AnnotationAwareAspectJAutoProxyCreator的Bean
    // 同时设置它的优先级为Ordered.HIGHEST_PRECEDENCE,角色为BeanDefinition.ROLE_INFRASTRUCTURE
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        // 提取EnableAspectJAutoProxy注解的属性
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
      // 如果proxyTargetClass=true,同步给AnnotationAwareAspectJAutoProxyCreator
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
      // 如果exposeProxy=true,同步给AnnotationAwareAspectJAutoProxyCreator
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}

AspectJAutoProxyRegistrar做的事情非常简单:

  1. 向容器中导入一个AnnotationAwareAspectJAutoProxyCreator类型的Bean,并设置优先级和角色
  2. 同步EnableAspectJAutoProxy注解的属性值给它

那么问题来了,这个AnnotationAwareAspectJAutoProxyCreator是个什么鬼呢?看名字的话它是某种自动代理的创建器,那么是不是就是它实现了BeanPostProcessor接口呢?

Variant AutoProxyCreator

Hierarchy of AnnotationAwareAspectJAutoProxyCreator
aj-arch.png

  查看类图,结果确如我们所想,AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,只不过是其父类实现的。好呗,扒就扒到底,AbstractAutoProxyCreator走起。

What is SmartInstantiationAwareBeanPostProcessor

  题外话,AbstractAutoProxyCreator实现的并不是普通的BeanPostProcessor,而是SmartInstantiationAwareBeanPostProcessor。有些同学可能不太熟悉这个接口,简单说明一下,我们先看它的父接口InstantiationAwareBeanPostProcessor

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
    /**
     * 在Bean创建之前调用,如果返回一个非空对象,就会跳过标准的Spring Bean创建流程而直接来到
     * postProcessAfterInitialization(...),具体逻辑可查看AbstractAutowireCapableBeanFactory#createBean(...)
     * 也就是说,如果我们想自己全盘接管Bean的生命周期,就可以使用这个回调
     */
    @Nullable
    default Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
     * 是否需要为刚创建好的Bean执行自动注入等标准流程,true表示执行标准的Spring Bean初始化流程,
     * false表示由用户自己控制初始化流程,具体逻辑可查看AbstractAutowireCapableBeanFactory#populateBean(...)
     */
    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    /**
     * 在PropertyValues应用到目标Bean上之前,给用户一个机会去修改PropertyValues,这一步其实在
     * BeanFactoryPostProcessor#postProcessBeanFactory(...)中也能实现,只是没有这里来得方便
     */
    @Nullable
    default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, 
                                               String beanName) throws BeansException {
        return null;
    }

    /**
     * 已由#postProcessProperties(...)代替
     */
    @Deprecated
    @Nullable
    default PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, 
                                                   Object bean, String beanName) throws BeansException {
        return pvs;
    }
}

spring-context中,CommonAnnotationBeanPostProcessor就实现了InstantiationAwareBeanPostProcessor接口用以支持javax.annotation.Resource注解。接下来是SmartInstantiationAwareBeanPostProcessor

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
    /**
     * 预测#postProcessBeforeInstantiation(...)的回调参数beanClass,主要还是用来做type match
     */
    @Nullable
    default Class predictBeanType(Class beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
   * 返回候选的构造函数,用来决定是否采用autowire by constructor这种装配策略,
   * 具体逻辑可查看AbstractAutowireCapableBeanFactory#createBeanInstance(...)
     */
    @Nullable
    default Constructor[] determineCandidateConstructors(Class beanClass, 
                                                          String beanName) throws BeansException {
        return null;
    }

    /**
     * Spring解决循环依赖的思路是提前创建 + 缓存,#getEarlyBeanReference(...)给了我们一个机
     * 会去访问这个被提前创建的Bean,因此我们可以在这里提前创建代理对象被返回,具体逻辑可查看
     * AbstractAutowireCapableBeanFactory#getEarlyBeanReference(...)
     * NOTE: 因为循环依赖的关系,这里传递进来的Bean是没有完全初始化好的,换句话说客户端是不能直接使用的
     */
    default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

AbstractAutoProxyCreator是如何执行织入的

Implementing SmartInstantiationAwareBeanPostProcessor

  好了,言归正题,回到AbstractAutoProxyCreator,挑出它对SmartInstantiationAwareBeanPostProcessor的实现部分。

    /**
   * 换个角度理解,如果目标对象已经被包装过,那么它的类型肯定
   * 会发生改变,因此也就需要一种机制来告诉容器包装后类型,否则
   * 后续类型匹配就会出错,这个机制就是#predictBeanType(...)
   */
    @Override
    @Nullable
    public Class predictBeanType(Class beanClass, String beanName) {
        if (this.proxyTypes.isEmpty()) {
            return null;
        }
    // 获取缓存key
        Object cacheKey = getCacheKey(beanClass, beanName);
    // proxyTypes的类型是Map>
    // 不管是JDK动态代理,还是CGLIB动态生成子类,最终都会改变
    // 目标对象在容器中的类型,proxyTypes中保存着包装后的类型
        return this.proxyTypes.get(cacheKey);
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
    // earlyProxyReferences的类型是Map
    // getEarlyBeanReference(...)是最早能够访问到目标对象的地方
    // 然而,目标对象可能会在后续的后处理中被改变,比如被其它的处理器修改。
    // 但是只要在earlyProxyReferences中保存着原始目标对象,后续就可以
    // 检测目标对象是否真的被改变了
        this.earlyProxyReferences.put(cacheKey, bean);
    // 尝试包装目标对象,画重点,织入的核心逻辑
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

    @Override
    public Object postProcessBeforeInstantiation(Class beanClass, String beanName) {
        Object cacheKey = getCacheKey(beanClass, beanName);
        // targetSourcedBeans中保存着通过自定义TargetSourceCreator创建的目标对象
    // 前面说过,#postProcessBeforeInstantiation(...)这个回调给了客户端全盘
    // 接管Bean生命周期的机会,TargetSourceCreator这个工厂接口就是设计来让客户
    // 端自行处理Bean的创建和属性注入等逻辑的。换言之,通过自定义TargetSourceCreator
    // 返回的目标对象被认为是完全初始化了的,并且会跳过标准的Spring Bean生命周期管理,那么
    // 这里就是最后的机会来完成织入并返回代理对象了
        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      // advisedBeans的类型是Map
      // 如果值为false,表示当前这个Bean不需要被代理
      // 值为true,表示已经被代理过了
      // 那么如果不需要代理,肯定就直接返回了
      // 如果已经代理过了,自然也不需要二次代理
      // 所以这里才用containsKey(...)判断一下
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
      // 1、isInfrastructureClass()检测Bean所属的类是不是Spring AOP的支撑类
      // 比如Advisor、Pointcut等等,这些类型是不需要被代理的
      // 2、shouldSkip(...)给了子类一个机会来决定 beanClass + beanName 二元组
      // 对应的Bean是不是不用进行代理
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

    // 如果用户确实需要全盘接管目标对象的生命周期
    // 那么这里是最后一个机会来完成织入并返回代理对象了
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
        // 记录下来
                this.targetSourcedBeans.add(beanName);
            }
      // 现在,目标对象已经到位 --> 从TargetSource中获取
      // 接下来只要知道可以作用在目标对象上的Advisor都有
      // 哪些就可以着手创建代理了
      
      // step 1: 模板方法,由子类实现,返回所有可能的Advisor
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
      // step 2: 根据已经掌握的信息 targetSouce + Advisors 来创建代理
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
      // 代理创建完成以后,把它的类型记下来
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
    
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
      // 前面说过,earlyProxyReferences中保存着bean的原始引用
      // 现在只需要简单判断一下就知道它有没有在其它地方被修改
      // 如果是,那就不得不重新尝试一下对它进行代理了
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

如果不考虑自定义TargetSourceCreator这种非常规流程,AbstractAutoProxyCreator一共有两个机会来完成织入,一处在getEarlyBeanReference(...),另一处在postProcessAfterInitialization(...)。这两处都调用了wrapIfNecessary(...)来完成最后的织入,继续往下看。

wrapIfNecessary
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 如果用户自行控制生命周期,就什么都不做
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
    // 对不需要被代理的类型,也什么都不做
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
    // 对可能需要被代理类型,进一步检查它是否真的需要被代理
    // 这一步的检测逻辑同 #postProcessBeforeInstantiation(...)
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

    // step 1: 通过模板方法获取所有Advisor
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 如果不为空,也就是配置了Advice,当然就需要被代理了
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
      // strp 2: 根据 目标对象 + Advisors 这两份信息来创建代理
      // createProxy(...)会根据配置信息来创建代理(jdk dynamic proxy/cglib subclassing)
      // 并且,Advisors经过处理后会组成一个拦截器链,这一部分的内容比较复杂,我们下篇再说
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      // 记录下代理对象的类型
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
        // 没有找到任何Advisor,也就是说没有配置任何Advice
    // 那还代理给毛,自然就是false了
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

wrapIfNecessary(...)postProcessBeforeInstantiation(...)做的事情差不多,核心逻辑其实只有两步:

  1. 获取可以作用在目标对象上的Advisor集合
  2. 将第1步获取到的Advisor list转换成Interceptor list并安装到Join point上,说人话就是创建代理对象

createProxy(...)整个流程比较复杂,涉及到Advice的适配和扩展、拦截器链的初始化和安装、代理的创建等等,我们下篇再说。getAdvicesAndAdvisorsForBean(...)是一个模板方法,实现它的是AbstractAutoProxyCreator的子类AbstractAdvisorAutoProxyCreator,接下来我们分析一下getAdvicesAndAdvisorsForBean(...)

AbstractAdvisorAutoProxyCreator是如何筛选Advisor的

getAdvicesAndAdvisorsForBean
    @Override
    @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName,
                                                  @Nullable TargetSource targetSource) {
        List advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

可以看到,它代理给了findEligibleAdvisors(...),继续跟踪。

    protected List findEligibleAdvisors(Class beanClass, String beanName) {
    // 获取BeanFactory中所有的Advisor
        List candidateAdvisors = findCandidateAdvisors();
    // Sping AOP中Advisor的类型主要是PointcutAdvisor
    // 这一步核心逻辑就是用Pointcut来对Advisor进行筛选
        List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 扩展方法,给子类一个增删改Advisor的机会
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
      // 对Advisor进行排序,默认通过Spring标准的Ordered接口和@Order注解指定优先级
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

findEligibleAdvisors(...)的流程还是比较清晰的:

  1. 找出容器中所有的Advisor
  2. 通过Pointcut进行筛选
  3. 对剩下的Advisor进行排序
findCandidateAdvisors
    protected List findCandidateAdvisors() {
        Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

发现它又代理给了advisorRetrievalHelper#findAdvisorBeans()

    public List findAdvisorBeans() {
        // 先读缓存
    String[] advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
      // 缓存没有的话,从BeanFactory中一次性把所有的Advisor Bean的名称获取出来
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
    // BeanFactory中也没有,那就真的没有了
        if (advisorNames.length == 0) {
            return new ArrayList<>();
        }

    // 否则的话,根据beanName挨个调用getBean(...)
        List advisors = new ArrayList<>();
        for (String name : advisorNames) {
      // 首先判断一下这个name所代表的Advisor是否符合条件,默认返回true
      // 可由AbstractAdvisorAutoProxyCreator及其子类进行改写,比如
      // InfrastructureAdvisorAutoProxyCreator就要求Advisor的角色
      // 必须是ROLE_INFRASTRUCTURE,所以@EnableCaching/@EnableTransactionManagement
      // 导入的一票Advisor的role都是ROLE_INFRASTRUCTURE
            if (isEligibleBean(name)) {
        // 跳过正在初始化的Advisor,毕竟这种的拿了也没用
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Skipping currently created advisor '" + name + "'");
                    }
                }
                else {
                    try {
            // 逐个获取
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
            // 如果目标对象和附加在它之上的Advisor间存在循环依赖
            // 这也是抛出BeanCurrentlyInCreationException异常的原因
            // 对这种情况,忽略这个Advisor
            // 个人感觉这种情况是否还是抛出异常比较好?
                        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;
    }
findAdvisorsThatCanApply
    protected List findAdvisorsThatCanApply(
            List candidateAdvisors, Class beanClass, String beanName) {
        // 在ThreadLocal中记录一下beanName
    // 用在spring自定义的 bean() AspectJ表达式中,不是很常用
        ProxyCreationContext.setCurrentProxiedBeanName(beanName);
        try {
            return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
        }
        finally {
            ProxyCreationContext.setCurrentProxiedBeanName(null);
        }
    }

核心逻辑都在AopUtils中了。

    public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List eligibleAdvisors = new ArrayList<>();
    // IntroductionAdvisor我们就不说了,用得比较少
        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;
            }
      // 判断Advisor能否适用于此beanClass
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

    public static boolean canApply(Advisor advisor, Class targetClass, boolean hasIntroductions) {
    // 忽略IntroductionAdvisor吧,我们不讲它
    // 毕竟PointcutAdvisor几乎就能代表spring中的Advisor了
        if (advisor instanceof IntroductionAdvisor) {
            return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
        }
    // 如果是PointcutAdvisor
        else if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pca = (PointcutAdvisor) advisor;
      // 使用Pointcut进行筛选
            return canApply(pca.getPointcut(), targetClass, hasIntroductions);
        }
        else {
      // Pointcut都没有的话默认它可以
      // 简单理解,这是一种generic advisor
            return true;
        }
    }

  public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 首先看看类型是否匹配
    if (!pc.getClassFilter().matches(targetClass)) {
      return false;
    }
    
        // Special case: MethodMatcher.TRUE 对任何方法都返回match
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
            // 自然没有判断的必要
      return true;
    }

    // 忽略Introduction吧
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    Set> classes = new LinkedHashSet<>();
    // 获取targetClass的原始类型,只有原始类型上才可能存在用户定义的方法
    // 也只有这些方法才有必要判断它是否匹配
    // 1. 不能是jdk dymamic proxy,jdk dynamic proxy上就不可能有
    if (!Proxy.isProxyClass(targetClass)) {
      // 2. 虽然不是jdk dynamic proxy,但有可能是cglib动态生成的子类啊,
      // cglib动态生成的子类也不可能有,所以需要获取一下user class
      classes.add(ClassUtils.getUserClass(targetClass));
    }
    
    // targetClass所实现接口也获取一下,接口中会定义方法,
    // 方法上可能有注解,而这些注解,有可能就是判断条件,比如说
    // spring-tx的@Transactional是可以标注在接口上的
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    for (Class clazz : classes) {
      // 这没啥说的了,所有方法拿出来,逐个使用MethodMatcher判断一下
      // 这里不涉及isRuntime()的情况,那种情况需要方法被实际调用了才能判断
      // 对jdk dynamic proxy来说就是InvocationHandler#invoke(...)被调用的时候
      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;
  }
sortAdvisors
    protected List sortAdvisors(List advisors) {
        AnnotationAwareOrderComparator.sort(advisors);
        return advisors;
    }

默认的排序策略是很简单的,根据Advisor实现的Ordered接口或标注的Order注解提供的优先级进行排序。

结语

  本篇我们从EnableAspectJAutoProxy注解着手,一步一步分析了spring-aop是如何通过BeanPostProcessor来执行织入的,下一篇我们一起看看创建动态代理的全流程吧~~

你可能感兴趣的:(Spring AOP源码解析(二))