美文网首页
Spring 4.3 源码分析之 Aop 配置式Aop (二)

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

作者: 爱吃鱼的KK | 来源:发表于2018-02-12 23:46 被阅读108次
    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<String> targetSourcedBeans =
            Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
    
    private final Set<Object> earlyProxyReferences =
            Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>(16));
    
    // proxyTypes 中存储的是 beanClass <--> 最终被代理生成的类的类型
    private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<Object, Class<?>>(16);
    
    // advisedBeans 中存储的是 class <--> 是否应该被代理 (TRUE | FALSE)
    private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<Object, Boolean>(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<Element> childElts = DomUtils.getChildElements(element);// 2. 得到 <aop: config> 标签下面的所有子标签
        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, 解析 <aop:pointcut>, <aop:advisor>, <aop:aspect>
    

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

    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 (二)

          本文链接:https://www.haomeiwen.com/subject/lmnhtftx.html