Spring AOP源码:配置文件解析过程

简介: Spring AOP源码:配置文件解析过程

文章导航

Spring AOP:基本概述

Spring AOP源码:配置文件解析过程

Spring AOP源码:配置文件解析过程

Spring AOP源码:拦截器责任链处理过程

目录

文章导航

前言

正文

方法1:parse

方法2:parsePointcut

方法3:createPointcutDefinition

方法4:parseAspect

方法5:isAdviceNode

方法6:parseAdvice

方法7:createAdviceDefinition

方法8:getAdviceClass

总结

前言

本篇文章主要讲解AOP配置中的几个通知类的解析过程,为后续对目标类进行代理做准备;在前面的Spring IOC篇我们讲解了自定义配置的解析,AOP配置的解析过程也是其自定义注解的过程,如果不熟悉自定义解析过程可以看之前的文章

正文

先看下AOP配置文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="accountAdvice" class="service.impl.AccountAdvice" ></bean>
    <bean id="myAccount" class="service.impl.MyAccount" ></bean>
    <aop:config>
      <aop:pointcut id="pointCut" expression="execution(* service.impl.*.*())"/>
      <aop:aspect ref="accountAdvice">
        <aop:after method="after" pointcut-ref="pointCut"></aop:after>
        <aop:before method="before" pointcut-ref="pointCut"></aop:before>
        <aop:around method="around" pointcut-ref="pointCut"></aop:around>
        <aop:after-returning method="afterReturn" pointcut-ref="pointCut"></aop:after-returning>
        <aop:after-throwing method="afterThrow" pointcut-ref="pointCut"></aop:after-throwing>
      </aop:aspect>
    </aop:config>
</beans>

回到BeanDefinition的解析步骤中,由于AOP是属于自定义解析,所以会使用自定义命名空间处理器进行处理,也就是会走delegate.parseCustomElement(ele)方法。

  protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (node instanceof Element) {
          Element ele = (Element) node;
          if (delegate.isDefaultNamespace(ele)) {
            parseDefaultElement(ele, delegate);
          }
          else {
            delegate.parseCustomElement(ele);
          }
        }
      }
    }
    else {
      delegate.parseCustomElement(root);
    }
  }
  public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
  }
  /**
   * Parse a custom element (outside of the default namespace).
   * @param ele the element to parse
   * @param containingBd the containing bean definition (if any)
   * @return the resulting bean definition
   */
  @Nullable
  public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    String namespaceUri = getNamespaceURI(ele);
    if (namespaceUri == null) {
      return null;
    }
    //获取自定义命名空间处理器
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
      error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
    }
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  }

handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)),见方法1详解

方法1:parse

  public BeanDefinition parse(Element element, ParserContext parserContext) {
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    return (parser != null ? parser.parse(element, parserContext) : null);
  }
public BeanDefinition parse(Element element, ParserContext parserContext) {
    CompositeComponentDefinition compositeDef =
        new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
    parserContext.pushContainingComponent(compositeDef);
    //注册AspectJAwareAdvisorAutoProxyCreator到beanFactory工厂中
    configureAutoProxyCreator(parserContext, element);
    List<Element> childElts = DomUtils.getChildElements(element);
    for (Element elt: childElts) {
      String localName = parserContext.getDelegate().getLocalName(elt);
      //解析POINTCUT配置标签,<aop:pointcut>
      if (POINTCUT.equals(localName)) {
        parsePointcut(elt, parserContext);
      }
      //解析<aop:advisor>标签
      else if (ADVISOR.equals(localName)) {
        parseAdvisor(elt, parserContext);
      }
      //解析<aop:aspect>标签
      else if (ASPECT.equals(localName)) {
        parseAspect(elt, parserContext);
      }
    }
    parserContext.popAndRegisterContainingComponent();
    return null;
  }

parsePointcut(elt, parserContext),见方法2详解

parseAspect(elt, parserContext),见方法4详解

方法2:parsePointcut

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
    //获取id属性值
    String id = pointcutElement.getAttribute(ID);
    //获取expression表达式值
    String expression = pointcutElement.getAttribute(EXPRESSION);
    AbstractBeanDefinition pointcutDefinition = null;
    try {
      this.parseState.push(new PointcutEntry(id));
      //将表达式内容封装成RootBeanDefinition对象
      pointcutDefinition = createPointcutDefinition(expression);
      pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
      String pointcutBeanName = id;
      if (StringUtils.hasText(pointcutBeanName)) {
        //如果有设置id名称,则将其作为beanName并将definition注册到beanFactory工厂中
        parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
      }
      else {
        //没有名称则生成并注册到beanFactory工厂中
        pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
      }
      //注册组件到上下文中
      parserContext.registerComponent(
          new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
    }
    finally {
      this.parseState.pop();
    }
    return pointcutDefinition;
  }

createPointcutDefinition(expression),见方法3详解

方法4:parseAspect

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));
      //存放解析的BeanDefinition
      List<BeanDefinition> beanDefinitions = new ArrayList<>();
      List<BeanReference> beanReferences = new ArrayList<>();
      //获取declare-parents标签
      List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
      //遍历解析封装成BeanDefinition
      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.
      NodeList nodeList = aspectElement.getChildNodes();
      boolean adviceFoundAlready = false;
      for (int i = 0; i < nodeList.getLength(); i++) {
        Node node = nodeList.item(i);
        //判断是否是通知配置标签,如<aop:after>、<aop:before> 等
        if (isAdviceNode(node, parserContext)) {
          if (!adviceFoundAlready) {
            adviceFoundAlready = true;
            if (!StringUtils.hasText(aspectName)) {
              parserContext.getReaderContext().error(
                  "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
                  aspectElement, this.parseState.snapshot());
              return;
            }
            //封装切面类为RuntimeBeanReference,并添加到集合中
            beanReferences.add(new RuntimeBeanReference(aspectName));
          }
          //解析当前通知标签
          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);
      //解析并注册到beanFactory中
      for (Element pointcutElement : pointcuts) {
        parsePointcut(pointcutElement, parserContext);
      }
      parserContext.popAndRegisterContainingComponent();
    }
    finally {
      this.parseState.pop();
    }
  }

isAdviceNode(node, parserContext),见方法5详解

parseAdvice(aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences),见方法6详解

方法5:isAdviceNode

private boolean isAdviceNode(Node aNode, ParserContext parserContext) {
    if (!(aNode instanceof Element)) {
      return false;
    }
    else {
      //获取节点名称
      String name = parserContext.getDelegate().getLocalName(aNode);
      //判断是否是befor、after、after-returning、after-throwing、around
      return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) ||
          AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name));
    }
  }

方法6:parseAdvice

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
      //创建MethodLocatingFactoryBean的RootBeanDefinition 
      RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
      //设置切面名称
      methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
      //设置method方法名称
      methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
      //标志为合成的
      methodDefinition.setSynthetic(true);
      // create instance factory definition
      //创建SimpleBeanFactoryAwareAspectInstanceFactory类型的RootBeanDefinition 
      RootBeanDefinition aspectFactoryDef =
          new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
      aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
      aspectFactoryDef.setSynthetic(true);
      // register the pointcut
      //封装成完整的通知类BeanDefinition
      AbstractBeanDefinition adviceDef = createAdviceDefinition(
          adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
          beanDefinitions, beanReferences);
      // configure the advisor
      //将advice再加层封装成advisor
      RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
      advisorDefinition.setSource(parserContext.extractSource(adviceElement));
      advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
      //判断是否有order属性,有则添加到定义信息中
      if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
        advisorDefinition.getPropertyValues().add(
            ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
      }
      // register the final advisor
      //生成beanName并注册到beanFactory中 
      parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
      return advisorDefinition;
    }
    finally {
      this.parseState.pop();
    }
  }

createAdviceDefinition(adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,beanDefinitions, beanReferences),见方法7详解

方法7:createAdviceDefinition

private AbstractBeanDefinition createAdviceDefinition(
      Element adviceElement, ParserContext parserContext, String aspectName, int order,
      RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
      List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    //获取对应类型的消息通知类并封装成RootBeanDefinition 
    RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
    adviceDefinition.setSource(parserContext.extractSource(adviceElement));
    //添加切面名称
    adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
    //添加declarationOrder值
    adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
    //添加returning属性值
    if (adviceElement.hasAttribute(RETURNING)) {
      adviceDefinition.getPropertyValues().add(
          RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
    }
    //添加throwing属性值
    if (adviceElement.hasAttribute(THROWING)) {
      adviceDefinition.getPropertyValues().add(
          THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
    }
    //添加arg-names属性值
    if (adviceElement.hasAttribute(ARG_NAMES)) {
      adviceDefinition.getPropertyValues().add(
          ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
    }
    //获取构造函数
    ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
    //添加第一个参数为方法对象
    cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
    //解析其表达式
    Object pointcut = parsePointcutProperty(adviceElement, parserContext);
    //将表达式添加到构造函数中的第二位
    if (pointcut instanceof BeanDefinition) {
      cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
      beanDefinitions.add((BeanDefinition) pointcut);
    }
    else if (pointcut instanceof String) {
      RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
      cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
      beanReferences.add(pointcutRef);
    }
    //将切面工厂实例作为构造函数的第三个参数
    cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
    return adviceDefinition;
  }

getAdviceClass(adviceElement, parserContext),见方法8详解

方法8:getAdviceClass

  private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
    //获取当前元素对应的通知名称
    String elementName = parserContext.getDelegate().getLocalName(adviceElement);
    //根据不同的名称,匹配不同的消息通知类型
    if (BEFORE.equals(elementName)) {
      return AspectJMethodBeforeAdvice.class;
    }
    else if (AFTER.equals(elementName)) {
      return AspectJAfterAdvice.class;
    }
    else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
      return AspectJAfterReturningAdvice.class;
    }
    else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
      return AspectJAfterThrowingAdvice.class;
    }
    else if (AROUND.equals(elementName)) {
      return AspectJAroundAdvice.class;
    }
    else {
      throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
    }
  }

到这里对AOP配置的解析就完成了,我们可以看到解析后的5个通知对象如下:

17f443d31fc202f4444d91ed0109973f_f72d76d930cc4396b109bc96e6b1895c.png

总结

AOP自定命名空间解析会往beanFactory工厂中注册AspectJAwareAdvisorAutoProxyCreator类型的beanDefinition,对于aspect标签的解析中,会将< aop:after >等元素解析成advisor对象的beanDefition,为后续AOP代理增强做准备。


目录
相关文章
|
3月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
467 0
|
2月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
7月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1170 13
|
4月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
4月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
8月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
535 70
|
5月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
968 0
|
6月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
723 0