Spring AOP源码学习:AOP 注解的解析

前言

上文介绍了 AOP 的基本概念,本文开始进入 AOP 的源码解析,本文仍以 AspectJ 来进行介绍,首先是 AOP 注解的解析。

 

正文

当使用 <aop:aspectj-autoproxy /> 注解开启 AOP 功能时。

Spring会从“META-INF/spring.handlers” 配置文件中拿到该注解对应的 NamespaceHandlerSupport:AopNamespaceHandler

在 AopNamespaceHandler 的 init 方法会给该注解注册对应的解析器,aspectj-autoproxy 对应的解析器是:AspectJAutoProxyBeanDefinitionParser。

@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());
}

我们知道,当解析到 <aop:aspectj-autoproxy /> 注解时,会调用 AspectJAutoProxyBeanDefinitionParser 的 parse方法。

关于自定义注解的解析内容之前 IoC 的文章介绍过了,如果不了解的可以参考:Spring IoC:parseCustomElement 详解

 

AspectJAutoProxyBeanDefinitionParser#parse

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 1.注册AspectJAnnotationAutoProxyCreator
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    // 2.对于注解中子节点的处理
    extendBeanDefinition(element, parserContext);
    return null;
}

1.注册 AspectJAnnotationAutoProxyCreator,见代码块1。

 

代码块1:AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {
    // 1.注册AspectJAnnotationAutoProxyCreator
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 2.对于proxy-target-class以及expose-proxy属性的处理
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 3.注册组件并通知,便于监听器做进一步处理
    registerComponentIfNecessary(beanDefinition, parserContext);
}

1.注册 AspectJAnnotationAutoProxyCreator,见代码块2。

2.对于 proxy-target-class 以及 expose-proxy 属性的处理,见代码块3。

 

代码块2:AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 1.如果注册表中已经存在beanName=org.springframework.aop.config.internalAutoProxyCreator的bean,则按优先级进行选择。
    // beanName=org.springframework.aop.config.internalAutoProxyCreator,可能存在的beanClass有三种,按优先级排序如下:
    // InfrastructureAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        // 拿到已经存在的bean定义
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // 如果已经存在的bean的className与当前要注册的bean的className不相同,则按优先级进行选择
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            // 拿到已经存在的bean的优先级
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            // 拿到当前要注册的bean的优先级
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                // 如果当前要注册的bean的优先级大于已经存在的bean的优先级,则将bean的className替换为当前要注册的bean的className,
                apcDefinition.setBeanClassName(cls.getName());
            }
            // 如果小于,则不做处理
        }
        // 如果已经存在的bean的className与当前要注册的bean的className相同,则无需进行任何处理
        return null;
    }
    // 2.如果注册表中还不存在,则新建一个Bean定义,并添加到注册表中
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    // 设置了order为最高优先级
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 注册BeanDefinition,beanName为org.springframework.aop.config.internalAutoProxyCreator
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

org.springframework.aop.config.internalAutoProxyCreator 是内部管理的自动代理创建者的 bean 名称,可能对应的 beanClassName 有三种,对应的注解如下:

InfrastructureAdvisorAutoProxyCreator:<tx:annotation-driven />

AspectJAwareAdvisorAutoProxyCreator:<aop:config />

AnnotationAwareAspectJAutoProxyCreator:<aop:aspectj-autoproxy />

当同时存在多个注解时,会使用优先级最高的 beanClassName 来作为 org.springframework.aop.config.internalAutoProxyCreator 的 beanClassName。本系列文章暂不考虑同时存在其他注解的情况,所以在这边会注册的 beanClassName 为:AnnotationAwareAspectJAutoProxyCreator。

 

代码块3:useClassProxyingIfNecessary

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
    if (sourceElement != null) {
        boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
        if (proxyTargetClass) {
            // 如果节点设置了proxy-target-class=true,则给beanName为org.springframework.aop.config.internalAutoProxyCreator
            // 的BeanDefinition添加proxyTargetClass=true的属性,之后创建代理的时候将强制使用Cglib代理
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
        if (exposeProxy) {
            // 如果节点设置了expose-proxy=true,则给beanName为org.springframework.aop.config.internalAutoProxyCreator
            // 的BeanDefinition添加exposeProxy=true的属性,之后创建拦截器时会根据该属性选择是否暴露代理类
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

 

总结

本文的内容比较简单,最重要的内容就是注册了内部管理的自动代理创建者的 bean:AnnotationAwareAspectJAutoProxyCreator,AOP 的大部分重要内容都在这个bean 里,之后会一一介绍。

 

相关文章

Spring AOP:基本概念

程序员囧辉 CSDN认证博客专家 Spring MySQL 分布式
当你的才华还撑不起你的野心的时候,你就应该静下心来学习,愿你在我这里能有所收获。公众号:程序员囧辉,分享个人在Java学习路上的一些学习、思考、经验和总结。
©️2020 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值