zoukankan      html  css  js  c++  java
  • 动态AOP自定义标签

    前言

    之前的文章讲述过自定义注解,如果声明了自定义的注解,那么就一定会在程序的某个地方注册了对应的解析器。我们发现在AopNamespaceHandler中的init函数:

    public class AopNamespaceHandler extends NamespaceHandlerSupport {
    
        /**
         * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
         * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
         * and '{@code scoped-proxy}' tags.
         */
        @Override
        public void init() {
            // In 2.0 XSD as well as in 2.1 XSD.
            registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
            registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
            registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
    
            // Only in 2.0 XSD: moved to context namespace as of 2.1
            registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        }
    
    }

    从上述的代码中可以看出,在解析配置文件的时候,一旦遇到aspectj-autoproxy注解时就会使用解析器AspectJAutoProxyBeanDefinitionParser进行解析,那么我们来看一下这个的内部实现。

    注册AnnotationAwareAspectJAutoProxyCreator

     所有解析器,因为是对BeanDefinitionParser接口的统一实现,入口都是从parse函数开始的,AspectJAutoProxyBeanDefinitionParser的parse函数如下:

    public BeanDefinition parse(Element element, ParserContext parserContext) {
            //注册AnnotationAwareAspectJAutoProxyCreator
            AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
            //对于注解中的子类的处理
            extendBeanDefinition(element, parserContext);
            return null;
        }

    其中registerAspectJAnnotationAutoProxyCreatorIfNecessary函数是我们比较关心的,也是关键逻辑的实现:

    public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                ParserContext parserContext, Element sourceElement) {
            //注册或者升级AutoProxy定义beanName为org.SpringFramework.aop.config.internalAutoProxyCreator的BeanDefinition
            BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
            //对于proxy-target-class以及expose-proxy属性的处理
            useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
            //注册组件并通知,便于监听器做进一步处理,其中,BeanDefinition的className为AnnotationAwareAspectJAutoProxyCreator
            registerComponentIfNecessary(beanDefinition, parserContext);
        }

    在这个函数中主要完成了三件事情,基本上述代码每一行就是一个完整的逻辑。

    1.注册或者升级AnnotationAwareAspectJAutoProxyCreator 

     对于AOP的实现,基本上都是靠AnnotationAwareAspectJAutoProxyCreator去完成,它可以根据@Point注解定义的切点来自动代理相匹配的bean。但是为了配置简单,Spring使用了自定义配置来帮助我们自动注册AnnotationAwareAspectJAutoProxyCreator,其注册过程就是在以下方法实现的:

    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                BeanDefinitionRegistry registry, @Nullable Object source) {
    
            return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
        }
    private static BeanDefinition registerOrEscalateApcAsRequired(
                Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
            //如果存在自动代理创建器且存在的自动代理创建器与现在的不一致,那么需要根据优先级来判断到底需要使用哪个
            if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
                BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
                if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                    int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                    int requiredPriority = findPriorityForClass(cls);
                    if (currentPriority < requiredPriority) {
                        //改变bean最重要的就是改变bean所对应的className属性
                        apcDefinition.setBeanClassName(cls.getName());
                    }
                }
                //如果已经存在自动代理创建器并且与将要创建的一致,那么无需再创建
                return null;
            }
    
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
            return beanDefinition;
        }

     以上代码实现了自动注册AnnotationAwareAspectJAutoProxyCreator类的功能,同时这里还涉及了一个优先级的问题,如果已经存在了自动代理创建器,而且存在的自动代理创建器与现在的不一致,那么要根据优先级来判断到底需要使用哪个。

    2.处理proxy-target-class以及expose-proxy属性

     useClassProxyingIfNecessary实现了proxy-target-class属性以及expose-proxy属性的处理。

    private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
            if (sourceElement != null) {
                //对于proxy-target-class属性的处理
                boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
                if (proxyTargetClass) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
                //对于expose-proxy属性的处理
                boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
                if (exposeProxy) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    //强制使用过程其实也是一个属性设置的过程
        public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
            if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
                BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
                definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
            }
        }

    ❤ proxy-target-class:SpringAOP部分使用JDK动态代理或者CGLIB来为目标对象创建代理(建议尽量使用JDK的动态代理)。如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理,并且所有该目标类型实现的接口都将被代理。 若该目标对象没有实现任何接口,则会创建一个CGLIB代理。如果你希望强制使用CGLIB代理(例如希望代理目标对象的所有方法,而不是只是实现自接口的方法),那么也可以,但是需要考虑下面两个问题:

      1.无法通知(advise)Final方法,因为它们不能被覆写;

      2.你需要将CGLIB二进制发行包放在classpath下面;

    与之相较,JDK本身就提供了动态代理,强制使用CGLIB代理需要将<aop:config>的proxy-target-class属性设为true:

    <aop:config proxy-target-class="true">...</aop:config>

    当需要使用CGLIB代理和@AspectJ自动代理支持,可以按照下面的方式设置<aop:aspectj-autoproxy>的proxy-target-class属性:

    <aop:aspectj-autoproxy proxy-target-class="true"/>

    而实际的使用的过程中才会发现细节问题的差别。

    ❤ JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。

    ❤ CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的Java字节码编辑类库)操作字节码实现的,性能比JDK强。

    ❤ expose-proxy:有时候目标对象内部的自我调用将无法实施切面中的增强,如下所示:

    public interface AService { 
        public void a(); 
        public void b();
    }
    
    @Service()
    public class AServicelmpll implements AService {
        @Transactional(propagation = Propagation.REQUIRED) 
        public void a() { 
            this.b{);
        }
        
        @Transactional(propagation = Propagation.REQUIRES_NEW) 
        public void b() {
        }
    }

     此处的this指向目标对象,因此调用this.b()将不会执行b事物切面,即不会执行事物增强,因此b方法的事物定义“@Transactional(propagation = Propagation.REQUIRES_NEW)”将不会实施,为了解决这个问题,我们可以这样做:

    <aop:aspectj-autoproxy expose-proxy="true"/>

    然后将以上代码中的“this.b()”修改为“((AService)AopContext.currentProxy()).b();”,即可。通过以上的修改便可以完成对a和b方法的同时增强。

    3.注册组件并通知,便于监听器做进一步的处理 
    private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
            if (beanDefinition != null) {
                parserContext.registerComponent(
                        new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
            }
        }

     参考:《Spring源码深度解析》 郝佳 编著: 

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    mysql添加外键的4种方式
    时序分析(1):时序约束原理(旧版、可能有错)
    SignalTap II——基本使用和高级技巧
    Testbench编写技巧
    异步复位同步释放
    协议——SCCB与IIC的区别
    协议——IIC
    Modelsim——显示状态机名称的方法
    亚稳态
    计数器(2):递增再递减,不断循环
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10230210.html
Copyright © 2011-2022 走看看