Spring 源碼分析(三) —— AOP(四)獲取指定加強

獲取指定加強

        上文中已經提到了獲取指定加強方法的兩個步驟:(1)獲取全部的加強,(2)尋找全部加強中適用於 bean 的加強並應用,而這兩個步驟是由 findCandidateAdvisors 和 findAdvisorsThatCanApply 來完成的。下圖是相關的時序圖:java

        但在介紹源碼前,我必須先了解一下加強以及其相關邏輯。ios


獲取加強

        通知器的解析

        好了,讓咱們從新回到源碼分析,值得注意的是 咱們分析的是使用註釋的 AOP,咱們對於 findCandidateAdvisors 方法的實現進行追蹤,在 AnnotationAwareAspectAutoProxyCreator 中發現了他的實現方法。
web

AnnotationAwareAspectAutoProxyCreator.javaspring

@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 註釋的類,進一步處理。最後,將將結果加入緩存。
ide

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 的繼承關係圖:ui

        咱們根據 AspectJAdviosrFactory 接口的繼承關係,在 ReflectiveAspectJAdvisorFactory 類中找到了他的 getAdvisors 實現類。this

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月於杭州

相關文章
相關標籤/搜索