Spring 源碼分析(三) —— AOP(三)實現思路

核心邏輯  

        上文中提到了 AOP 建立代理等等的具體操做都是在 AnnotationAwareAspectAutoProxyCreator 類中來成的,經過上文的自動註冊,下面讓咱們看 AnnotationAwareAspectAutoProxyCreator 是如何工做的,首先是 AnnotationAwareAspectAutoProxyCreator 的繼承關係圖:
java

        而後是 AnnotationAwareAspectAutoProxyCreator 的層次結構圖:
web

        這裏須要特別注意的是 BeanPostProcessor 接口,咱們知道實際運用中,若是你須要對項目中的 Bean 進行代理,在 Spring 的 xml 的配置一個 BeanPostProcessor 就行。由此,咱們能夠知道 AnnotationAwareAspectAutoProxyCreator 實現代理也是經過 BeanPostProcessor 接口來完成的,因此咱們對於 AOP 邏輯分析也是由 BeanPostProcessor 實例化前的 postProcessAfterInitialization 方法開始,而 AnnotationAwareAspectAutoProxyCreator 的 postProcessAfterInitialization 具體實現是在其父類 AbstractAutoProxyCreator 中完成的。咱們對 AOP 邏輯的分析也由此開始。
正則表達式

AbstractAutoProxyCreator.javaexpress

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   if (bean != null) {
      // 根據給定的 bean 的 class 和 name 構建出個 key,格式:beanClassName_beanName
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         // 若是它適合被代理,則須要封裝指定 bean。
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   // 是否已經處理過
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   // 無需加強
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   // 給定的 bean 類是否表明一個基礎設施類,基礎設施類不該代理,或者配置了指定 bean 不須要自動代理
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // 若是存在加強方法則建立代理
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   // 若是獲取到了加強則須要針對加強建立代理
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      // 建立代理
      Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

        以上兩個方法構成了建立代理的雛形,固然,開始前還有些判斷工做。而建立代理的核心邏輯部分是在 AbstractAutoProxyCreator 類中完成的,而建立代理前的準備工做主要分爲兩步:(1)獲取加強方法或者加強器。(2)根據獲取的加強進行代理。下面是 AbstractAutoProxyCreator 的時序圖:數組

        結合時序圖,咱們知道真正建立代理地方是從 getAdvicesAndAdvisorsForBean 開始的。雖然看起來很簡單,但其實每一步都有大量複雜的邏輯。但在分析源碼前,咱們必須先對加強以及其具體邏輯有所瞭解。app


設計的基石

        加強

        Advice(也翻做 通知)定義了鏈接點作什麼,爲切面加強提供了織入的接口。在 Spring AOP 中,它主要描述 Spring AOP 圍繞方法調用而注入的切面行爲。Advice 是 AOP 聯盟定義的一個接口,具體的接口定義在 org.aopalliance.aop.Advice 中。在 Spring AOP 的實現中,使用了這個統一接口,並經過這個接口,爲 AOP 切面加強的注入功能作了更多的細化和擴展,好比前面提到的具體通知類型,如BeforeAdvice、AfterAdvice、ThrowsAdvice等。做爲 Spring AOP 定義的接口類,具體的切面加強能夠經過這些接口集成到 AOP 框架中去發揮做用。對於這些接口類,下面是他的主要接口繼承圖:
框架

        下面是 Advice 接口繼承的層次圖:
ide

        看一個程序的具體設計思路沒有比看接口來的更直接的了,下面咱們就從第一個 BeforeAdvice 的繼承接口 MethodBeforeAdvice 開始(BeforeAdvice 裏沒有任何東西)函數

MthodInterceptor.java源碼分析

public interface MethodInterceptor extends Interceptor {
    void before(Method method, Object[] args, Object target) throws Throwable;
}

        明顯可以看出這是一個回調函數,他的具體參數有:Method 對象,這個參數是目標方法的反射對象,Object[]對象數組,這個對象數組中包含沒辦法發的輸入參數。而咱們根據繼承關係看他的具體實現類 AspectJMethodBeforeAdvice。

AspectJMethodBeforeAdvice.java 

@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
    invokeAdviceMethod(getJoinPointMatch(), null, null);
}

AbstractAspectJAdvice.java

protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
   return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
   Object[] actualArgs = args;
   if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
      actualArgs = null;
   }
   try {
      ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
      // 激活加強
      return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
   }
   catch (IllegalArgumentException ex) {
      throw new AopInvocationException("Mismatch on arguments to advice method [" +
            this.aspectJAdviceMethod + "]; pointcut expression [" +
            this.pointcut.getPointcutExpression() + "]", ex);
   }
   catch (InvocationTargetException ex) {
      throw ex.getTargetException();
   }
}

        invokeAdviceMethodWithGivenArgs 方法中的 aspectJAdviceMethod 正是對於前置加強的方法,在這裏實行了調用。下面是 AfterAdvice 的繼承接口 AfterReturningAdvice 接口:

AfterReturningAdvice.java 

public interface AfterReturningAdvice extends AfterAdvice {
   void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

        而 AfterReturningAdvice 接口的核心邏輯是在其實現父類 AspectJAfterReturningAdvice 中完成的。

AspectJAfterReturningAdvice.java 

@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
   if (shouldInvokeOnReturnValueOf(method, returnValue)) {
      invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
   }
}

        後面的和 BeforeAdvice 同樣。根據分析咱們能夠知道 BeforeAdvice 和 AfterAdvice。儘管,這兩個加強行爲一致,旦由於它實現的 AOP 通知不一樣,因此就被 AOP 編織到不一樣的調用場合中了。而其餘的加強基本思路都是如此,這裏就不展開了。


        切點

        Pointcut(關注點,也稱 切點)用於決定 Advice 加強做用於哪一個鏈接點,也就是說經過 Pointcut 來定義須要加強的方法集合,而這些集合的選取能夠經過必定的規則來完成,例如:這些須要加強的地方能夠由某個正則表達式來進行標識,或根據某個方法名來進行匹配等。下面是 Pointcut 的層次結構圖:

        下面是 Pointcut 接口:

Pointcut.java

public interface Pointcut {
   ClassFilter getClassFilter();
   MethodMatcher getMethodMatcher();
   Pointcut TRUE = TruePointcut.INSTANCE;
}

        經過 Pointcut 接口的基本定義咱們能夠看到,須要返回一個 MethodMatcher。對於 Point 的匹配判斷功能,具體是由這個返回的 MethodMatcher 來完成的,也就是說,有這個 MethodMatcher 來判斷是否須要對當前方法調用進行加強或者配置應用。我接着對 MethodMatcher 接口進行分析:

MethodMatcher.java

public interface MethodMatcher {
   boolean matches(Method method, Class<?> targetClass);
   boolean isRuntime();
   boolean matches(Method method, Class<?> targetClass, Object[] args);
   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

        經過閱讀註釋咱們知道了 Pointcut 的核心邏輯是在 matches 方法中完成的,我就以經過方法名匹配的 NameMatchMetchMethodPointcut 類的 matches 方法來講明:

NameMatchMetchMethodPointcut.java 

@Override
public boolean matches(Method method, Class<?> targetClass) {
   for (String mappedName : this.mappedNames) {
      if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
         return true;
      }
   }
   return false;
}

        從源碼咱們看到他的實現是很是簡單的,匹配條件就是方法名相同或者方法名匹配。


        通知器

        Advisor通知器)用一個對象將對目標方法的切面加強設計(Advice)和關注點的設計(Pointcut)結合起來。下面是 Advisor 接口的層次繼承圖:

        這是一個 Advisor 接口的設計,咱們從中能夠看出經過 Advisor,能夠定義應該使用哪一個加強而且在哪一個關注點使用它,也就是經過 Advisor,把 Advice 和 Pointcut 結合起來,這個結合爲使用 IOC 容器配置 AOP應用,提供了便利。下面是 Advisor 接口:

Advisor.java

public interface Advisor {
   Advice getAdvice();
   boolean isPerInstance();
}

        下面咱們以一個 Advisor 的實現(DefaultPointcutAdvisor)爲例,進而瞭解 Advisor 的工做原理。

DefaultPointcutAdvisor.java

public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {

   private Pointcut pointcut = Pointcut.TRUE;

   public DefaultPointcutAdvisor() {
   }

   public DefaultPointcutAdvisor(Advice advice) {
      this(Pointcut.TRUE, advice);
   }

   public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
      this.pointcut = pointcut;
      setAdvice(advice);
   }

   public void setPointcut(Pointcut pointcut) {
      this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
   }

   @Override
   public Pointcut getPointcut() {
      return this.pointcut;
   }

   @Override
   public String toString() {
      return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
   }
}

        這個類主要職責是 Pointcut 的設置或者獲取,在 DefaultPointcutAdvisor 中,Pointcut 默認被設置爲 Pointcut.True,這個 Pointcut.True 接口被定義爲 Pointcut True = TruePointcut.INSTANCE。而關於 Advice 部分的設置與獲取是由其父類 AbstractGenericPointcutAdvisor 來完成的。


指定Bean的加強方法

        下面就讓咱們回到源碼分析,前面已經提到了通知器,這裏就不冗述了,他的主要做用是用來整合切面加強設計(Advice)和切入點設計(Pointcut)。對於指定 bean 的加強方法的獲取,通常包含獲取全部加強以及尋找全部加強中適合於 bean 的加強並應用這兩個步驟。

AnnotationAwareAspectAutoProxyCreator.java

@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

        從源碼看,這兩個步驟必定是由 findCandidateAdvisors 和 findAdvisorsThatCanApply 來完成。還有值得注意的是,若是沒法找到對應的通知器便會返回 DO_NOT_PROXY(null)。



——水門(2016年3月於杭州)

相關文章
相關標籤/搜索