Spring AOP 源码解析(AOP入口浅谈)

本文深入剖析Spring AOP配置过程,从AOP源码入口开始,详细解读如何通过AopNamespaceHandler解析aop配置,包括pointcut、advisor、aspect等标签的解析流程,以及AspectJAwareAdvisorAutoProxyCreator的注册和配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AOP源码入口

要想知道aop的实现原理,我们首先要了解的就是spinng ioc容器是怎么解析我们的aop配置的,这里我们暂且只看xml形式的配置解析

好了,回到上面的spring配置文件

<aop:config>
        <aop:aspect id="log" ref="aspectTest">
            <aop:pointcut id="addAllMethod" expression="execution(* com.wangjn.demo.MyPointCut.*(..))" />
            <aop:before method="logBefore" pointcut-ref="addAllMethod" />
            <aop:after method="logAfter" pointcut-ref="addAllMethod" />
        </aop:aspect>
    </aop:config>

可以看到都是spring aop的自定义标签,在之前解析spring ioc源码的时候我们已经知道,spring是支持自定义的标签解析的,自定义标签的入口文件在META-INF/spring.handlers中,我们找到spring aopjar包下的这个目录,打开这个文件

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

可以看到这里配置了一个aop命名空间的解析器 org.springframework.aop.config.AopNamespaceHandler,现在我们就从这个入口文件来进行源码分析

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());
        // 支持 @AspectJ 声明式配置
        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());
    }
}

这里我们可以看到对config属性的解析是由ConfigBeanDefinitionParser完成的。ConfigBeanDefinitionParser是BeanDefinitionParser接口的实现,其中的parse方法封装了对aop:config属性标签的解析逻辑

ConfigBeanDefinitionParser的parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
    CompositeComponentDefinition compositeDef =
            new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
    parserContext.pushContainingComponent(compositeDef);

    configureAutoProxyCreator(parserContext, element);

    List<Element> childElts = DomUtils.getChildElements(element);
    for (Element elt: childElts) {
        String localName = parserContext.getDelegate().getLocalName(elt);
        // pointCut标签
        if (POINTCUT.equals(localName)) {
            parsePointcut(elt, parserContext);
        }
        // advisor标签
        else if (ADVISOR.equals(localName)) {
            parseAdvisor(elt, parserContext);
        }
        // aspect标签
        else if (ASPECT.equals(localName)) {
            parseAspect(elt, parserContext);
        }
    }

    parserContext.popAndRegisterContainingComponent();
    return null;
}

可以清晰的看到,这里分别对pointcut,advisor,aspect三种标签进行解析

configureAutoProxyCreator(parserContext, element)

这里配置了代理创建类,调用了AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary方法完成配置

private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
    AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}
public static void registerAspectJAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {
    // 获取代理创建类 AspectJAwareAdvisorAutoProxyCreator并命名为org.springframework.aop.config.internalAutoProxyCreator
    
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 判断是否使用 CGLIB 创建代理,指定proxy-target-class=true则使用CGLIB,否则使用JDK动态代理
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 往 spring ioc 容器注册AspectJAwareAdvisorAutoProxyCreator代理创建器
    registerComponentIfNecessary(beanDefinition, parserContext);
}

可以看到这段代码主要做了两件事情

  1. 注册代理创建器 AspectJAwareAdvisorAutoProxyCreator,这个类很重要,是 aop的核心实现类
  2. 指定创建代理的方式,其中如果aop:config节点配置了proxy-target-class=true,则使用CGLIB创建代理,否则默认使用JDK 动态代理
parsePointcut

解析aop的切入点配置,对应 aop:pointcut标签

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
    // 获取我们配置的 pointCut 切入点 id
    String id = pointcutElement.getAttribute(ID);
    // 获取要切入的 expression 表达式
    String expression = pointcutElement.getAttribute(EXPRESSION);

    AbstractBeanDefinition pointcutDefinition = null;

    try {
        // 记录当前处理节点
        this.parseState.push(new PointcutEntry(id));
        // 创建 AspectJExpressionPointcut 类型的 beanDefinition,并且把expression属性赋给此bean
        pointcutDefinition = createPointcutDefinition(expression);
// 设置源数据        pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
        
        // 设置 bean的id名称
        String pointcutBeanName = id;
        if (StringUtils.hasText(pointcutBeanName)) {
        // 注册beanDefinution 至spring容器
        parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
        }
        else {
            pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
        }

        parserContext.registerComponent(
                new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
    }
    finally {
        this.parseState.pop();
    }

    return pointcutDefinition;
}

这里主要是配置了aop切入点的bean AspectJExpressionPointcut

private void parseAspect(Element aspectElement, ParserContext parserContext) {
    // 获取切面id
    String aspectId = aspectElement.getAttribute(ID);
    // 获取切面要执行的具体逻辑类 这个是必须设置的属性
    String aspectName = aspectElement.getAttribute(REF);

    try {
        this.parseState.push(new AspectEntry(aspectId, aspectName));
        List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
        List<BeanReference> beanReferences = new ArrayList<BeanReference>();

        List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
        for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
            Element declareParentsElement = declareParents.get(i);
            beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
        }

        // We have to parse "advice" and all the advice kinds in one loop, to get the
        // ordering semantics right.
        // 解析advice标签
        NodeList nodeList = aspectElement.getChildNodes();
        boolean adviceFoundAlready = false;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            // 判断 advice 的类型是否是限定的几种(after before around)
            if (isAdviceNode(node, parserContext)) {
                if (!adviceFoundAlready) {
                    adviceFoundAlready = true;
                    // 如果没有配置 ref 属性,抛出异常
                    if (!StringUtils.hasText(aspectName)) {
                        parserContext.getReaderContext().error(
                                "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
                                aspectElement, this.parseState.snapshot());
                        return;
                    }
                    beanReferences.add(new RuntimeBeanReference(aspectName));
                }
                // 开始解析advice节点
                AbstractBeanDefinition advisorDefinition = parseAdvice(
                        aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
                beanDefinitions.add(advisorDefinition);
            }
        }

        AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
                aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
        parserContext.pushContainingComponent(aspectComponentDefinition);
        // 解析 aspect 标签中的 pointcut
        List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
        for (Element pointcutElement : pointcuts) {
            parsePointcut(pointcutElement, parserContext);
        }

        parserContext.popAndRegisterContainingComponent();
    }
    finally {
        this.parseState.pop();
    }
}

parseAdvice这段代码的主要逻辑其实就是解析 advice标签,并最终包装成advisor。advisor其实相当于整合了advice 通知和pointcut 切点,相当于通过 advisor把切点和要执行的切面逻辑连接了起来

private AbstractBeanDefinition parseAdvice(
        String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
        List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

    try {
        this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

        // create the method factory bean
        // 创建切面增强方法对应的 methodDefinition,并设置 methodName targetBeanName属性
        RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
        methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
        methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
        methodDefinition.setSynthetic(true);

        // create instance factory definition
        // 创建切面类 FactoryBean
        RootBeanDefinition aspectFactoryDef =
                new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
        aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
        aspectFactoryDef.setSynthetic(true);

        // register the pointcut
        // 选择合适的 advice 并注册 pointcut
        AbstractBeanDefinition adviceDef = createAdviceDefinition(
                adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
                beanDefinitions, beanReferences);

        // configure the advisor
        // 配置 advisor,对应的类型是 AspectJPointcutAdvisor.class
        RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
        advisorDefinition.setSource(parserContext.extractSource(adviceElement));
        advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
        if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
            advisorDefinition.getPropertyValues().add(
                    ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
        }

        // register the final advisor
        parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

        return advisorDefinition;
    }
    finally {
        this.parseState.pop();
    }
}

**这里划一下上面代码提到的几个重要的类:

  • Pointcut 对应 AspectJExpressionPointcut.class
  • Advice 对应 AspectJMethodBeforeAdvice,AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice,AspectJAroundAdvice, 五种通知类型
  • Advisor 对应 AspectJPointcutAdvisor.class

总结
到这里,对AOP的配置信息已经解析完成,并成功的放入Spring容器中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值