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

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


目录
相关文章
|
21天前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
6天前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
什么是线程池?从底层源码入手,深度解析线程池的工作原理
|
6天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
|
10天前
|
开发工具
Flutter-AnimatedWidget组件源码解析
Flutter-AnimatedWidget组件源码解析
|
28天前
|
测试技术 Python
python自动化测试中装饰器@ddt与@data源码深入解析
综上所述,使用 `@ddt`和 `@data`可以大大简化写作测试用例的过程,让我们能专注于测试逻辑的本身,而无需编写重复的测试方法。通过讲解了 `@ddt`和 `@data`源码的关键部分,我们可以更深入地理解其背后的工作原理。
23 1
|
1月前
|
开发者 Python
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
67 1
|
1月前
|
算法 安全 Java
深入解析Java多线程:源码级别的分析与实践
深入解析Java多线程:源码级别的分析与实践
|
4月前
|
XML Java uml
spring 源码解析——第一篇(ioc xml方式)
spring 源码解析——第一篇(ioc xml方式)
49 0
|
4月前
|
安全 Java 应用服务中间件
阿里技术官架构使用总结:Spring+MyBatis源码+Tomcat架构解析等
分享Java技术文以及学习经验也有一段时间了,实际上作为程序员,我们都清楚学习的重要性,毕竟时代在发展,互联网之下,稍有一些落后可能就会被淘汰掉,因此我们需要不断去审视自己,通过学习来让自己得到相应的提升。
|
4月前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
66 1

热门文章

最新文章

推荐镜像

更多