Spring 源码分析(三) —— AOP(四)获取指定增强

from:https://my.oschina.net/kaywu123/blog/632488

获取指定增强

        上文中已经提到了获取指定增强方法的两个步骤:(1)获取所有的增强,(2)寻找所有增强中适用于 bean 的增强并应用,而这两个步骤是由 findCandidateAdvisors 和 findAdvisorsThatCanApply 来完成的。下图是相关的时序图:

        但在介绍源码前,我必须先了解一下增强以及其相关逻辑。

获取增强

        通知器的解析

        好了,让我们重新回到源码分析,值得注意的是 我们分析的是使用注释的 AOP,我们对于 findCandidateAdvisors 方法的实现进行追踪,在 AnnotationAwareAspectAutoProxyCreator 中发现了他的实现方法。

AnnotationAwareAspectAutoProxyCreator.java

@Override
protected List<Advisor> findCandidateAdvisors() {
   // 当使用注释方式配置 AOP 的时候并不是丢弃了对 XML 配置的支持。
   // 在这里调用父类方法加载配置文件中的 AOP 声明
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}

        而在 AnnotationAwareAspectAutoProxyCreator 的 findAdvisorBeans 方法里间接继承了 AbstractAutoProxyCreator 中的 findAdvisorBeans 方法,在实现中除了保留父类的获取配置文件中定义的增强外,同时添加了获取 Bean 的注解增强的功能,而真正实现是由 this.aspectJAdvisorsBuilder.buildAspectJAdvisors()来实现的。

        看下代码,我们就能明白其中的思路。首先,获取所有在 beanFacotry 中注册的 Bean 都会被提取出来。然后遍历所有 beanName,并找出声明 AspectJ 注释的类,进一步处理。最后,将将结果加入缓存。

BeanFactoryAspectJAdvisorsBuilder.java

public List<Advisor> buildAspectJAdvisors() {
   List<String> aspectNames = null;

   synchronized (this) {
      aspectNames = this.aspectBeanNames;
      if (aspectNames == null) {
         List<Advisor> advisors = new LinkedList<Advisor>();
         aspectNames = new LinkedList<String>();
         // 获取所有的 beanName
         String[] beanNames =
               BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
         // 循环所有的 beanName 找出对应的增强方法
         for (String beanName : beanNames) {
            // 不合法的 bean 则略过,由子类定义规则,默认返回 true
            if (!isEligibleBean(beanName)) {
               continue;
            }
            // 获取对应的 bean 的类型
            Class<?> beanType = this.beanFactory.getType(beanName);
            if (beanType == null) {
               continue;
            }
            // 如果存在 Aspect 注解
            if (this.advisorFactory.isAspect(beanType)) {
               aspectNames.add(beanName);
               AspectMetadata amd = new AspectMetadata(beanType, beanName);
               if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                  MetadataAwareAspectInstanceFactory factory =
                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                  // 解析标记 AspectJ 注解的增强方法
                  List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                  if (this.beanFactory.isSingleton(beanName)) {
                     this.advisorsCache.put(beanName, classAdvisors);
                  }
                  else {
                     this.aspectFactoryCache.put(beanName, factory);
                  }
                  advisors.addAll(classAdvisors);
               }
               else {
                  if (this.beanFactory.isSingleton(beanName)) {
                     throw new IllegalArgumentException("Bean with name '" + beanName +
                           "' is a singleton, but aspect instantiation model is not singleton");
                  }
                  MetadataAwareAspectInstanceFactory factory =
                        new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                  this.aspectFactoryCache.put(beanName, factory);
                  advisors.addAll(this.advisorFactory.getAdvisors(factory));
               }
            }
         }
         this.aspectBeanNames = aspectNames;
         return advisors;
      }
   }

   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   // 记录在缓存中
   List<Advisor> advisors = new LinkedList<Advisor>();
   for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
         advisors.addAll(cachedAdvisors);
      }
      else {
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
   return advisors;
}

        至此,我们已经完成了通知器的解析。往下我们就介绍最重要的也是最繁琐的通知器的获取。

        获取通知器

        在上面的步骤中最为重要也最为繁琐的就是通知器的获取。而这一功能看源码,是由 AspectJAdviosrFactory 接口的 getAdvisors 方法来完成的,下图是 AspectJAdviosrFactory 的继承关系图:

        我们根据 AspectJAdviosrFactory 接口的继承关系,在 ReflectiveAspectJAdvisorFactory 类中找到了他的 getAdvisors 实现类。

ReflectiveAspectJAdvisorFactory.java 

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
   // 获取标记为 AspectJ 的类
   final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
   // 获取标记为 AspectJ 的name
   final String aspectName = maaif.getAspectMetadata().getAspectName();
   // 验证
   validate(aspectClass);

   final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
         new LazySingletonAspectInstanceFactoryDecorator(maaif);

   final List<Advisor> advisors = new LinkedList<Advisor>();
   // 声明为 Pointcut 的方法不处理
   for (Method method : getAdvisorMethods(aspectClass)) {
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }

   if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
      // 如果寻找的增强器不为空而且又配置了增强延迟初始化那么需要在首位加入同步实例化增强器
      Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
      advisors.add(0, instantiationAdvisor);
   }

   // 获取 DeclareParents 注解
   for (Field field : aspectClass.getDeclaredFields()) {
      Advisor advisor = getDeclareParentsAdvisor(field);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }

   return advisors;
}

        函数中首先完成了对增强器的获取,包括获取注解以及根据注解生成增强的步骤,然后考虑到在配置中可能会将增强配置成延迟初始化,那么需要在首位加入同步实例化增强以保证增强使用之前的实例化,最后对 DeclareParents 注解的获取,下面将详细介绍一下每个步骤。

        获取普通增强

        普通增强器的获取逻辑通过 getAdvisor 方法来实现,实现步骤包括对切点的注解以及根据注解信息生成增强。

ReflectiveAspectJAdvisorFactory.java 

@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
      int declarationOrderInAspect, String aspectName) {

   validate(aif.getAspectMetadata().getAspectClass());
   
   // 切点信息的获取
   AspectJExpressionPointcut ajexp =
         getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
   if (ajexp == null) {
      return null;
   }
   // 根据切点信息生成增强器
   return new InstantiationModelAwarePointcutAdvisorImpl(
         this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
}

        (1)切点信息的获取。所谓获取切点信息就是指定注解的表达式信息的获取,如@Before("test()")。

ReflectiveAspectJAdvisorFactory.java 

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
   // 获取方法上的注解
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }
   // 使用AspectJExpressionPointcut 实例封装获取的信息
   AspectJExpressionPointcut ajexp =
         new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
   // 提取得到的注解中的表达式如:
   // @Pointcut("execution(* test.TestBean.*(..))")
   ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
   return ajexp;
}

AbstractAspectJAdvisorFactory.java

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
   // 设置敏感的注解类
   Class<?>[] classesToLookFor = new Class<?>[] {
         Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
   for (Class<?> c : classesToLookFor) {
      AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
      if (foundAnnotation != null) {
         return foundAnnotation;
      }
   }
   return null;
}

// 获取指定方法上的注解并使用 AspectJAnnotation 封装
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
   A result = AnnotationUtils.findAnnotation(method, toLookFor);
   if (result != null) {
      return new AspectJAnnotation<A>(result);
   }
   else {
      return null;
   }
}

        (2)根据切点信息生成增强。所有的增强都由 Advisor 的实现类 InstantiationModelAwarePointcutAdvisorImpl 统一封装的。

InstantiationModelAwarePointcutAdvisorImpl.java 

public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
      MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {
    
   // test()
   this.declaredPointcut = ajexp;
   // public void test.AspectJTest.beforeTest()
   this.method = method;
   
   this.atAspectJAdvisorFactory = af;
   this.aspectInstanceFactory = aif;
   this.declarationOrder = declarationOrderInAspect;
   this.aspectName = aspectName;

   if (aif.getAspectMetadata().isLazilyInstantiated()) {
      // Static part of the pointcut is a lazy type.
      Pointcut preInstantiationPointcut =
            Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

      this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
      this.lazy = true;
   }
   else {
      // A singleton aspect.
      this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
      this.pointcut = declaredPointcut;
      this.lazy = false;
   }
}

        在封装过程中只是简单地将信息封装在类的实例中而已,所有的信息单纯地赋值,在实例初始化的过程中还完成了对于增强器的初始化。因为不同的增强所体现的逻辑是不同的,而根据注解中的信息初始化对应的增强器是在 instantiateAdvice 方法中完成的。

InstantiationModelAwarePointcutAdvisorImpl.java 

private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
   return this.atAspectJAdvisorFactory.getAdvice(
         this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}

        而与 AspectJAdviosrFactory 接口的 getAdvisors 方法一样,getAdvice 方法也是在 ReflectiveAspectJAdvisorFactory 类中完成的。

ReflectiveAspectJAdvisorFactory.java

@Override
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
      MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {

   Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
   validate(candidateAspectClass);

   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }

   // If we get here, we know we have an AspectJ method.
   // Check that it's an AspectJ-annotated class
   if (!isAspect(candidateAspectClass)) {
      throw new AopConfigException("Advice must be declared inside an aspect type: " +
            "Offending method '" + candidateAdviceMethod + "' in class [" +
            candidateAspectClass.getName() + "]");
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Found AspectJ method: " + candidateAdviceMethod);
   }

   AbstractAspectJAdvice springAdvice;

   // 根据不同的注解类封装增强器
   switch (aspectJAnnotation.getAnnotationType()) {
      case AtBefore:
         springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
         break;
      case AtAfter:
         springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
         break;
      case AtAfterReturning:
         springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
         AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterReturningAnnotation.returning())) {
            springAdvice.setReturningName(afterReturningAnnotation.returning());
         }
         break;
      case AtAfterThrowing:
         springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
         AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
            springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
         }
         break;
      case AtAround:
         springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
         break;
      case AtPointcut:
         if (logger.isDebugEnabled()) {
            logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
         }
         return null;
      default:
         throw new UnsupportedOperationException(
               "Unsupported advice type on method " + candidateAdviceMethod);
   }

   // Now to configure the advice...
   springAdvice.setAspectName(aspectName);
   springAdvice.setDeclarationOrder(declarationOrderInAspect);
   String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
   if (argNames != null) {
      springAdvice.setArgumentNamesFromStringArray(argNames);
   }
   springAdvice.calculateArgumentBindings();
   return springAdvice;
}

        从函数中,我们可以看出,Spring 会根据不同的注解生成不同的增强器,因为太过庞杂了,这里就不先讲述了,笔者会在后面介绍设计方法的时候慢慢讲述的。

        

        增强同步实例化增强器

        如果寻找的增强器不为空而且又配置了增强延迟初始化,那么就需要在首位加入同步实例化增强器,具体实现如下:

ReflectiveAspectJAdvisorFactory.java

protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {

   public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {
      super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() {
         // 目标方法前调用,类似@Before
         @Override
         public void before(Method method, Object[] args, Object target) {
            // 简单初始化 aspect
            aif.getAspectInstance();
         }
      });
   }
}

        获取 DeclareParents 注解

        DeclareParents 主要用于引介增强的注解形式的实现,而其实现方式与普通增强很类似,只是做了封装。

ReflectiveAspectJAdvisorFactory.java

private Advisor getDeclareParentsAdvisor(Field introductionField) {
   DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
   if (declareParents == null) {
      // Not an introduction field
      return null;
   }

   if (DeclareParents.class.equals(declareParents.defaultImpl())) {
      // This is what comes back if it wasn't set. This seems bizarre...
      // TODO this restriction possibly should be relaxed
      throw new IllegalStateException("defaultImpl must be set on DeclareParents");
   }

   return new DeclareParentsAdvisor(
         introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}

寻找匹配的通知

        前面的函数中已经完成了所有的增强的解析,但对于所有增强来讲,并不一定都适用于当前的 Bean,还要挑选出适合的增强,也就是满足我们配置的通配符的增强器。具体实现在 findAdvisorsThatCanApply 中,而且 findAdvisorsThatCanApply 也比上面来的简单的多。

AbstractAdvisorAutoProxyCreator.java

protected List<Advisor> findAdvisorsThatCanApply(
      List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

   ProxyCreationContext.setCurrentProxiedBeanName(beanName);
   try {
      // 过滤已经得到的 advisors
      return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
   }
   finally {
      ProxyCreationContext.setCurrentProxiedBeanName(null);
   }
}

        继续看 findAdvisorsThatCanApply:

AopUtils.java

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
   if (candidateAdvisors.isEmpty()) {
      return candidateAdvisors;
   }
   List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
   // 首先处理引介增强
   for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
         eligibleAdvisors.add(candidate);
      }
   }
   boolean hasIntroductions = !eligibleAdvisors.isEmpty();
   for (Advisor candidate : candidateAdvisors) {
      // 引介增强已经处理
      if (candidate instanceof IntroductionAdvisor) {
         continue;
      }
      // 对于普通 bean 的处理
      if (canApply(candidate, clazz, hasIntroductions)) {
         eligibleAdvisors.add(candidate);
      }
   }
   return eligibleAdvisors;
}

        findAdvisorsThatCanApply 函数的主要功能是寻找所有增强器中适合于当前 class 的增强器。引介增强与普通的增强是处理不一样的,所以分开处理。而对于真正的匹配在 canApply 中实现。

AopUtils.java

public static boolean canApply(Advisor advisor, Class<?> targetClass) {
   return canApply(advisor, targetClass, false);
}

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
   if (advisor instanceof IntroductionAdvisor) {
      return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
   }
   else if (advisor instanceof PointcutAdvisor) {
      PointcutAdvisor pca = (PointcutAdvisor) advisor;
      return canApply(pca.getPointcut(), targetClass, hasIntroductions);
   }
   else {
      return true;
   }
}

        至此,getAdvicesAndAdvisorsForBean 方法要做的事就完成了,接下来就是 createProxy 方法生成代理了。

——水门(2016年3月于杭州)

猜你喜欢

转载自blog.csdn.net/u010142437/article/details/81737732