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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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代理增强做准备。


目录
相关文章
|
1月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
14天前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
73 25
|
14天前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
66 24
|
1月前
|
Java 测试技术 应用服务中间件
Spring Boot 配置文件总结
Spring Boot 提供全局配置文件 `application.properties` 和 `application.yml`,用于修改自动配置的默认值。前者使用键值对配置,后者使用缩进和冒号。不同环境(开发、测试、生产)可切换配置文件,通过 `spring.profiles.active` 指定。例如,开发环境端口为4790,测试环境为4791,生产环境为4792。配置示例展示了属性、List、Map定义及引用方法。
63 14
|
2月前
|
XML Java 数据格式
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
本文介绍了在使用Spring框架时,如何通过创建`applicationContext.xml`配置文件来管理对象。首先,在resources目录下新建XML配置文件,并通过IDEA自动生成部分配置。为完善配置,特别是添加AOP支持,可以通过IDEA的Live Templates功能自定义XML模板。具体步骤包括:连续按两次Shift搜索Live Templates,配置模板内容,输入特定前缀(如spring)并按Tab键即可快速生成完整的Spring配置文件。这样可以大大提高开发效率,减少重复工作。
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
|
1月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
81 8
|
3月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
111 5
|
3月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
61 5
|
27天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
201 17
Spring Boot 两种部署到服务器的方式
|
27天前
|
Dart 前端开发 JavaScript
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
59 17
springboot自动配置原理

推荐镜像

更多