Spring 4.3 源码分析之 Aop 配置式Aop (二)

1. Spring Aop 配置式Aop 概述

上篇是 Spring Aop 简单配置式编程的(通过 配置 ProxyFactoryBean 来针对指定 target 来创建代理对象), 但 ProxyFactoryBean 只能针对单个 target, 所以出现了AbstractAutoProxyCreator(自动获取 BeanFactory 中的所有 Advice/MethodInterceptor, 并针对所有符合 Pointcut 的对象创建代理对象), 这个类是其他自动获取 Advice/MethodInterecptor 的基类, 其主要有一下属性:

/**
 * Convenience constant for subclasses: Return value for "do not proxy".
 * @see #getAdvicesAndAdvisorsForBean
 */
// 代表不需要对这个 bean 动态代理的常量类
protected static final Object[] DO_NOT_PROXY = null;

/**
 * Convenience constant for subclasses: Return value for
 * "proxy without additional interceptors, just the common ones".
 * @see #getAdvicesAndAdvisorsForBean
 */
// 代表 需要进行动态代理的常量
protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0];


/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());

/** Default is global AdvisorAdapterRegistry */
// 这里的 advisorAdapterRegistry 中主要是将 advice, MethodInterceptor 包装成 Advisor, 与将 Advisor 转成 MethodInterceptor
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();

/**
 * Indicates whether or not the proxy should be frozen. Overridden from super
 * to prevent the configuration from becoming frozen too early.
 */
// 标示是否 代理的配置属性不允许改变
private boolean freezeProxy = false;

/** Default is no common interceptors */
// 拦截器的类名 <- 这里适用通配符
private String[] interceptorNames = new String[0];

// 是否将 通过 "interceptorNames" 解析得出的 MethodInterceptor 放在前面
private boolean applyCommonInterceptorsFirst = true;

// 当配置了 customTargetSourceCreators, 则会在 postProcessBeforeInstantiation 方法中进行创建代理类
private TargetSourceCreator[] customTargetSourceCreators;

// Spring 的 IOC 工厂
private BeanFactory beanFactory;

/** 这里的 targetSourcedBeans 将与 earlyProxyReferences 一起分析
 *  targetSourcedBeans: 当在实例化前置方法 postProcessBeforeInstantiation 中创建了代理类, 则在 targetSourcedBeans 中将添加 beanName, 也就是 targetSourcedBeans 中含有 beanName 则说明这个类被动态代理了
 *  earlyProxyReferences: 当 Bean 被循环引用, 并且被暴露了, 则会通过 getEarlyBeanReference 来创建代理类; 在初始化后置方法 postProcessAfterInitialization 中也会通过判断 earlyProxyReferences 中是否存在 beanName 来决定是否需要对 target 进行动态代理
 */
private final Set targetSourcedBeans =
        Collections.newSetFromMap(new ConcurrentHashMap(16));

private final Set earlyProxyReferences =
        Collections.newSetFromMap(new ConcurrentHashMap(16));

// proxyTypes 中存储的是 beanClass <--> 最终被代理生成的类的类型
private final Map> proxyTypes = new ConcurrentHashMap>(16);

// advisedBeans 中存储的是 class <--> 是否应该被代理 (TRUE | FALSE)
private final Map advisedBeans = new ConcurrentHashMap(256);
 
 

这些属性都是为其创建代理对象而工作的, 而 AbstractAutoProxyCreator 本身也是一个 SmartInstantiationAwareBeanPostProcessor (具备在类实例化/初始化的前后设置钩子方法的类), 同时也是一个 ProxyProcessorSupport 类(这个类中配置类创建 Proxy 所需的属性信息), 再看一下对应的 UML 图:


AbstractAutoProxyCreator.png

图中清晰的呈现了 AbstractAutoProxyCreator 通过接口实现与继承 ProxyProcessorSupport 所具备的技能, 而我们常规的自动获取Advisor, 创建代理对象也是在 实例化/初始化 方法的前后钩子方法中.

2. Spring Aop 配置式Aop 创建动态代理方法

在 AbstractAutoProxyCreator 中创建动态代理主要通过:

1. postProcessBeforeInstantiation: 实例化前置方法 <- 当配置类自定义的 customTargetSourceCreators, 并且针对 Bean 能返回对应 TargetBean, 则就可以进行动态代理
2. postProcessAfterInitialization: 初始化后置方法, 这是一般类动态代理生成的地方
3. getEarlyBeanReference: 当 Spring 里面的类循环引用, 则通过 ObjectFactory 回调 getEarlyBeanReference 方法来提前对对象进行代理, 并暴露出去 (以上都是进行对类进行动态代理的方法入口, 但针对同一个类, 三个方法只有一个会被调用)

先来看一下 postProcessBeforeInstantiation, 这是一个类实例化的前置方法, 主逻辑如下:

1. 根据给定的 bean 的 class 和 name 构建出个 key, 格式: beanClassName_beanName
2. targetSourcedBeans.contains(beanName) = true -> 已经通过实例化前置动态代理过对象
    2.1 advisedBeans.containsKey(cacheKey) = true -> 表示通过其他渠道对这个对象进行了动态代理
    2.2 若是 Spring 容器的基础类 或 是被 @AspectJ 注解修饰的类, 则直接跳过
3. 通过 customTargetSourceCreators 来获取 targetSource (这里的 customTargetSourceCreators 是用户自定义的 TargetSource 创建器)
    3.1 通过模版方法 getAdvicesAndAdvisorsForBean 来收集MethodInterceptor / Advice 对象 <- 这里主要分为两类 1. 直接获取 BeanFactory 中所有的 Advisor 2. 获取 Spring 中所有类 @AspectJ 注解修饰的类, 并转化成Advisor, 最后再刷选出符合 Pointcut 的Advisor
    3.2 调用 createProxy 方法来创建代理对象

与之对应的代码如下:

@Override
public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {   // 在 Bean 实例化前面 进行处理
    // 根据给定的 bean 的 class 和 name 构建出个 key, 格式: beanClassName_beanName
    Object cacheKey = getCacheKey(beanClass, beanName);

    /** targetSourcedBeans
     * 参考资料: http://jinnianshilongnian.iteye.com/blog/1492424
     *
     */
    if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {        // targetSourcedBeans.contains(beanName) = true, 表示已经通过实例化前置动态代理过对象
        if (this.advisedBeans.containsKey(cacheKey)) {                            // 此 cacheKey 已经被动态代理了, 直接返回 null
            return null;
        }
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {// 基础类 与 被 Aspect 注释的类 应该直接跳过
            this.advisedBeans.put(cacheKey, Boolean.FALSE);                       // 在 advisedBeans 中设置 cacheKey 应该跳过
            return null;
        }
    }

    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    if (beanName != null) {
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);   // 若配置了 TargetSourceCreator, 并且根据 beanClass, beanName 能获取 TargetBean <-- 这个很少用了
        if (targetSource != null) {
            this.targetSourcedBeans.add(beanName);                                // 从这里可以看出 targetSourcedBeans 是已经被代理的 bean的 name
            /** 获取 运用到 动态代理上的 Advice / MethodInterceptor, 主要分为下面两类:
             *  1. 获取 BeanFactory 中所有 Advisor, 并且仅仅获取 符合 beanClass 的 Advisor (PS: 在计算是否匹配时, 只要满足 其中一个方法匹配就选取)
             *  2. 若配置的是 AnnotationAwareAspectJAutoProxyCreator, 则将获取所有被 @AspectJ 标注的类, 并解析成 Advisor, 最后再帅选合适的 Advisor
             */
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            /** 动态代理对象的创建
             *  1. 将 interceptorNames 解析成的 MethodInterceptor / Advice 包装成 Advisor (包括上面的 specificInterceptors)
             *  2. 选择合适的 AopProxy
             *  3. 创建代理对象
             */
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());                      // key 是 对应的 beanName, value 是代理类的类型 <-- 这里放置 proxy.getClass 主要是用于预测类型
            return proxy;
        }
    }
    return null;
}

与之对应的是 初始化后置方法 postProcessAfterInitialization, 通常我们的代理对象都是在这个方法里面创建的; 这里有个小逻辑, 就是 getEarlyBeanReference 与 postProcessAfterInitialization 这两个方法只会有其中一个会被调用, 所以就会有 earlyProxyReferences.contains(cacheKey) 这个判断, 其他的创建逻辑与上面的实例化前置方法类似!

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {      // 生成代理对象
    if (bean != null) {
        // 根据给定的 bean 的 class 和 name 构建出个 key, 格式 beanClassName_beanName
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 是否已经 由于避免循环依赖而创建 bean  代理
        if (!this.earlyProxyReferences.contains(cacheKey)) {// 这里有个判断 !earlyProxyReferences.contains(cacheKey), 主要还是可能上面 getEarlyBeanReference 方法已经调用过了, 已经生成了代理对象
            // 如果它适合被代理, 则需要封装指定 bean
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {   // 可能 Bean 已经在 postProcessBeforeInstantiation 中处理过了 (PS: 已经生成代理类了)
        return bean;
    }
    // 无需增强 下面 advisedBeans 可能此时还没有含有 cacheKey, 所以 get 出 null
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {            // advisedBeans 的值是在 AbstractAutoProxyCreator.postProcessBeforeInstantiation 里面进行设置
        return bean;
    }
    // 给定的 bean 类是否代表一个基础设施类 ,基础设施类不应代理, 或者配置了指定  bean 不需要地动代理 (PS: 比如被 @AspectJ 注解修饰的类)
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    /** 获取 运用到 动态代理上的 Advice / MethodInterceptor, 主要分为下面两类:
     *  1. 获取 BeanFactory 中所有 Advisor, 并且仅仅获取 符合 beanClass 的 Advisor (PS: 在计算是否匹配时, 只要满足 其中一个方法匹配就选取)
     *  2. 若配置的是 AnnotationAwareAspectJAutoProxyCreator, 则将获取所有被 @AspectJ 标注的类, 并解析成 Advisor, 最后再帅选合适的 Advisor
     */
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {                            // DO_NOT_PROXY 代表着此类不需要进行动态代理, 见子类 BeanNameAutoProxyCreator
        this.advisedBeans.put(cacheKey, Boolean.TRUE);                     // 设置 cacheKey 所代表的类被动态代理了
        Object proxy = createProxy(                                        // 根据 MethodInterceptor/Advice, class, beanName 创建动态代理类
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());                   // key 是 对应的 beanName, value 是代理类的类型 <-- 这里放置 proxy.getClass 主要是用于预测类型
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);                        // 设置 cacheKey 所代表的类 没被动态代理了
    return bean;
}
3. Spring Aop 配置式Aop AbstractAutoProxyCreator 的子类

AbstractAutoProxyCreator 主要有以下子类:

1. BeanNameAutoProxyCreator: 这个类继承 AbstractAutoProxyCreator, 在这个类中主要是根据 配置的 beanNames 来判断是否需要对这个类进行动态代理
2. AbstractAdvisorAutoProxyCreator: 这是一个抽象类, 在其内部通过 advisorRetrievalHelper 获取 BeanFactory 中所有的Advisor, 并通过 AopUtils.findAdvisorsThatCanApply 来查询所有适配的 Advisor
3. DefaultAdvisorAutoProxyCreator: 这个类是AbstractAdvisorAutoProxyCreator的子类, 其增加了 通过类名的前缀来判别是否需要对类进行动态代理
4. AspectJAwareAdvisorAutoProxyCreator: 这个类也是 AbstractAdvisorAutoProxyCreator 的子类, 主要是运用于 通过 aop namespace 的方式配置 Advisor/Pointcut/Advice(这几个对象在解析 XML 时就会生成对应的 AbstractAspectJAdvice, AspectJExpressionPointcut); AspectJAwareAdvisorAutoProxyCreator 主要完成了 1: 完成 Advisor 的排序, 2: 在shouldSkip方法中通过 advisorRetrievalHelper 获取容器中的所有 Advisor
5. AnnotationAwareAspectJAutoProxyCreator: 这是AbstractAutoProxyCreator中使用最频繁的一个类, 这个类主要通过aspectJAdvisorsBuilder解析被 @AspectJ 标注的类上面的其他注解信息来生成 Advisor, 对应的生成过程及策略是交由 aspectJAdvisorFactory 来完成的
4. Spring Aop 配置式Aop 通过 Aop 命名空间配置动态代理

AspectJAwareAdvisorAutoProxyCreator 这个自动代理创建器就是完成这个功能, 而对应得解析器则是 ConfigBeanDefinitionParser, 解析得步骤很复杂, 我们从入口窥得:

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    CompositeComponentDefinition compositeDef =
            new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
    parserContext.pushContainingComponent(compositeDef);
    configureAutoProxyCreator(parserContext, element);           // 1. 在 DefaultListableBeanFactory 中注入 AspectJAwareAdvisorAutoProxyCreator
    List childElts = DomUtils.getChildElements(element);// 2. 得到  标签下面的所有子标签
    for (Element elt: childElts) {
        String localName = parserContext.getDelegate().getLocalName(elt);
        if (POINTCUT.equals(localName)) {                        // 3. 解析 aop:pointcut 标签, 生成 AspectJExpressionPointcut, 并注入到 BeanFactory
            parsePointcut(elt, parserContext);
        }
        else if (ADVISOR.equals(localName)) {                    // 4. 解析 aop:advisor 标签, 生成 DefaultBeanFactoryPointcutAdvisor, 并注入到容器中
            parseAdvisor(elt, parserContext);
        }
        else if (ASPECT.equals(localName)) {                     // 5. 解析 aop:aspect 标签, 并解析其下面得所有子标签, 生成 AspectJPointcutAdvisor/AspectJExpressionPointcut/AbstractAspectJAdvice 并注入 BeanFactory
            parseAspect(elt, parserContext);
        }
    }
    parserContext.popAndRegisterContainingComponent();
    return null;
}
入口分为: 注册 AspectJAwareAdvisorAutoProxyCreator, 解析 , , 

整个解析过程比较冗长, 下面就不叙述, 但其间会出现几个新的角色:

1. AbstractAspectJAdvice: 基于 aop namesapce 解析得到得 Advice 的基类, 它主要完成, 设置Advice方法上对应的参数名(包括 JoinPoint对应的参数名 THIS_JOIN_POINT), 获取方法的所有参数类型并与之对应的参数的名称, 创建统一的方法激活方法
2. AspectJMethodBeforeAdvice: 从 aop namespace 或org.aspectj.lang.annotation.Before注解注释的方法上解析出来的 BeforeAdvice<- 注意这个方法只是Advice, 要进行正真调用需要将其适配成 MethodInterceptor, MethodBeforeAdviceAdapter 就是完成这个功能的
3. AspectJAroundAdvice: 从 aop namespace 或org.aspectj.lang.annotation.Around注解注释的方法上解析出来的 Advice, 这里对应的是 MethodInterceptor, 在激活的方法中通常将 MethodInvocationProceedingJoinPoint 传入方法内
4. AspectJAfterThrowingAdvice: 从 aop namespace 或org.aspectj.lang.annotation.AfterThrowing注解注释的方法上解析出来的 ThrowingAdvice, 其还是先调用 MethodInvocation.preceed(), 若出现异常, 则在 catch 部分激活对应的 Advice 方法
5. AspectJAfterReturningAdvice: 从 aop namespace 或org.aspectj.lang.annotation.AfterReturning注解注释的方法上解析出来的 AfterReturningAdvice <- 这个只是 Advice, 需要 AfterReturningAdviceAdapter 将其转为 MethodInterceptor 后才能正真用于 动态代理的创建上
6. AspectJAfterAdvice: 从 aop namespace 或org.aspectj.lang.annotation.AfterReturning注解注释的方法上解析出来的 AfterAdvice <- 这是个 MethodInterceptor, 激活方法的地方在 finally 中
7. ReflectiveAspectJAdvisorFactory: Advisor/Advice 创建工厂类, 其实就是根据注解 Before/After/AfterReturing/AfterThrowing/Around的注解生成对应的 Advice, 最后在根据 AspectJExpressionPointcut 最终创建 Advisor 
8. BeanFactoryAspectJAdvisorsBuilder: 这个工具类获取 BeanFactory中所有被 @AspectJ 注解标注的 Bean, 并通过  advisorFactory 解析成 Advisor, 存储到缓存中

最终 aop 命名空间解析器会将 xml 中的属性解析成 各种个样的 Advice/Advisor, 而在创建动态代理对象时, 在AspectJAwareAdvisorAutoProxyCreator 中 通过 shouldSkip -> findCandidateAdvisors 收集容器中所有的 Advisor

5. Spring Aop 配置式Aop 通过 AspectJ 注解声明 Aop

与之对应的 AbstractAutoProxyCreator 是 AnnotationAwareAspectJAutoProxyCreator, AnnotationAwareAspectJAutoProxyCreator在创建动态代理对象时会多一一部通过 BeanFactoryAspectJAdvisorsBuilder 获取所有的被 @AspectJ 注解标注的类, 并通过 AspectJAdvisorFactory 解析成各种 Advisor, 其他部分与AspectJAwareAdvisorAutoProxyCreator 差不多; 差点完了, 对应的命名空间解析器是 AspectJAutoProxyBeanDefinitionParser, 其主要还是完成 AnnotationAwareAspectJAutoProxyCreator 的注入工作

6. 总结
1. 声明式 aop 主要还是通过 AsbtractAutoProxyCreator 的子类来创建的
2. 声明式 aop 的创建过程主要在实例化前置方法, 初始化后置方法, 在循环依赖时 通过getEarlyBeanReference 提前获取对象时创建的
3. 基于 aop 命名空间申明的 Advice 与 基于 AspectJ 声明的 Advice, 最终都会生成 AbstractAspectJAdvice/AspectJExpressionPointcut
4. 在 aop 命名空间解析器 ConfigBeanDefinitionParser 中进行了大量的 解析工作, 对应的 AbstractAspectJAdvice/AspectJExpressionPointcut 也是在这里生成的
5. 基于 @AspectJ 注解的Aop  <- 其获取Advisor/Advice 是在第一次创建 aop 对象时才操作, 也就是传说中的 lazy 加载, 主要还是通过 aspectJAdvisorsBuilder 获取所有被 @AspectJ 注解标注的类, 再通过 advisorFactory 解析成 Advisor
7. 参考:

Spring Aop核心源码分析
Spring技术内幕
Spring 揭秘
Spring 源码深度分析
开涛 Spring 杂谈
伤神 Spring 源码分析
Spring源码情操陶冶

你可能感兴趣的:(Spring 4.3 源码分析之 Aop 配置式Aop (二))