Spring aop之<aop:config>标签解析

794 阅读5分钟

一、前言

在开发过程中,程序员除了要关心主业务逻辑之外,还需要关注其他统一的日志、异常处理等,这些功能属于系统中不可缺少的功能,如果每一个接口的代码中都需要与这些日志、监控代码耦合在一起,往往会增加系统的维护成本,也会降低工作效率。为了解决这个问题,spring使用了面向切面编程(AOP:Aspect Oriented Programming)的思想,允许我们可以使用配置化的方式将这些通用功能注入到业务代码中。

对于Spring aop的分析将分为三个章节,分别是spring aop标签的解析,代理的生成,以及代理类的执行逻辑等。

Spring aop支持注解和xml的配置方式,xml的配置方式如下图。本文将介绍spring aop xml配置标签的原理。


二、标签解析

自定义标签解析一文中,介绍了spring解析自定义标签的原理。对于<aop:config>标签来说,其解析类是ConfigBeanDefinitionParser。

@Override
  @Nullable
  public BeanDefinition parse(Element element, ParserContext parserContext){
    CompositeComponentDefinition compositeDef =
        new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
    parserContext.pushContainingComponent(compositeDef);
    //在容器中注册AspectJAwareAdvisorAutoProxyCreator,实现了
    //BeanPostProcessor,用于生成类的代理
    configureAutoProxyCreator(parserContext, element);
​    //对aop:config的下级标签进行处理
    List<Element> childElts = DomUtils.getChildElements(element);
    for (Element elt: childElts) {
      String localName = parserContext.getDelegate().getLocalName(elt);
      //如果下级标签是aop:pointcut,用于定义全局的切点
      if (POINTCUT.equals(localName)) {
        parsePointcut(elt, parserContext);
      }
      //如果下级标签是aop:advisor(用于定义通知,用于与aop:aspect略有差异)
      else if (ADVISOR.equals(localName)) {
        parseAdvisor(elt, parserContext);
      }
      //如果标签是aop:aspect,用于定义切面(切点+通知)
      else if (ASPECT.equals(localName)) {
        parseAspect(elt, parserContext);
      }
    }
    parserContext.popAndRegisterContainingComponent();
    return null;
  }

AspectJAwareAdvisorAutoProxyCreator的类继承关系图如下,实现了BeanPostProcessor,我们可以猜测,代理类的生成逻辑:bean实例化完成后,调用AspectJAwareAdvisorAutoProxyCreator父类实现AbstractAutoProxyCreator实现的postProcessBeforeInstantiation(),根据我们配置的切点策略,判断是否为当前对象生成代理对象。

代码如下,具体逻辑在下文介绍。

@Override
  public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);
​
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      if (this.advisedBeans.containsKey(cacheKey)) {
        return null;
      }
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return null;
      }
    }
​
    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
      if (StringUtils.hasLength(beanName)) {
        this.targetSourcedBeans.add(beanName);
      }
      Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
      Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
    }
​
    return null;
  }​


我们以aop:aspect作为例子,讲解spring如何解析aop自定义标签:

private void parseAspect(Element aspectElement, ParserContext parserContext) {
    //获取id
    String aspectId = aspectElement.getAttribute(ID);
    //ref对应增强类的beanName
    String aspectName = aspectElement.getAttribute(REF);
​
    try {
      this.parseState.push(new AspectEntry(aspectId, aspectName));
      List<BeanDefinition> beanDefinitions = new ArrayList<>();
      List<BeanReference> beanReferences = new ArrayList<>();
      //是否配置了declare-parents,用于被代理类提供其他的接口功能
      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.
      //通过循环处理aop:aspect的下级标签,主要包含定义的切面。
      NodeList nodeList = aspectElement.getChildNodes();
      boolean adviceFoundAlready = false;
      for (int i = 0; i < nodeList.getLength(); i++) {
        Node node = nodeList.item(i);
        //是否是通知,aop:before,aop:after等
        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;
            }
            beanReferences.add(new RuntimeBeanReference(aspectName));
          }
          //解析aop:before这类节点成BeanDefinition,注入到容器中
          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);
​
      List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
      for (Element pointcutElement : pointcuts) {
        parsePointcut(pointcutElement, parserContext);
      }
​
      parserContext.popAndRegisterContainingComponent();
    }
    finally {
      this.parseState.pop();
    }
  }

//解析aop:before这类节点成BeanDefinition,注入到容器中
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)));
​
      // 创建MethodLocatingFactoryBean的工厂类beanDefifnition,用于返回对应的增强类方法
      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
      RootBeanDefinition aspectFactoryDef =
          new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
      aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
      aspectFactoryDef.setSynthetic(true);
​
      // 根据通知类型(before/after等),创建通知。
      AbstractBeanDefinition adviceDef = createAdviceDefinition(
          adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
          beanDefinitions, beanReferences);
​
      // 构建切面,切面=切点+通知
      RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
      advisorDefinition.setSource(parserContext.extractSource(adviceElement));
      advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
      //aop:aspect是否配置了order(切面的执行顺序)
      if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
        advisorDefinition.getPropertyValues().add(
            ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
      }
​
      //在容器中注册切面(切点+通知)
      parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
​
      return advisorDefinition;
    }
    finally {
      this.parseState.pop();
    }
  }​

private AbstractBeanDefinition createAdviceDefinition(
      Element adviceElement, ParserContext parserContext, String aspectName, int order,
      RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
      List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    
    RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
    adviceDefinition.setSource(parserContext.extractSource(adviceElement));
​
    adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
    adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
    //处理returning等标签
    if (adviceElement.hasAttribute(RETURNING)) {
      adviceDefinition.getPropertyValues().add(
          RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
    }
    if (adviceElement.hasAttribute(THROWING)) {
      adviceDefinition.getPropertyValues().add(
          THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
    }
    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;
  }

//根据通知类型,返回对应的处理类,比如aspect:before的处理类是AspectJMethodBeforeAdvice
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 + "].");
    }
​  }

//before类型通知的处理器,构造方法包含三个参数,分别是切面所在的增强方法、切点、切面的工厂类
public AspectJMethodBeforeAdvice(
      Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
​
    super(aspectJBeforeAdviceMethod, pointcut, aif);
  }

在容器中注册的切面的实现类是AspectJPointcutAdvisor:

public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
​
  private final AbstractAspectJAdvice advice;
​
  private final Pointcut pointcut;
​
  @Nullable
  private Integer order;
​}

可以发现,切面=通知+切点,包含了生成代理的策略。

三、总结

至此,容器中注册了两种bean,第一种是AspectJAwareAdvisorAutoProxyCreator,用于在bean实例化后,生成代理;第二种是切面,切点+通知,用于判断是否生成代理的策略。

Spring解析<aop:config>标签的逻辑:

  1. 在容器中注册AspectJAwareAdvisorAutoProxyCreator,这是一种BeanPostProcessor,用于生成代理类;
  2. 遍历<aop:config>的子标签列表,如果是<aop:aspect>标签:解析<aop-aspect>的子标签,如果添加了declare-parents注解,则生成对应的BeanDefinition,根据子标签的类型(before/after/after-returning/after-throwing),选择对应Advice实现类,解析增强类和对应的增强方法、切点,生成AspectJPointcutAdvisor的beanDefinition(主要包含增强方法、切点等),并注册到容器中,这些bean都实现了Advisor接口。这些类将在AspectJAwareAdvisorAutoProxyCreator.postProcessBeforeInstantiation(),生成代理类时作为策略使用。