首頁 > 軟體

spring原始碼閱讀--aop實現原理講解

2021-09-28 13:00:14

aop實現原理簡介

首先我們都知道aop的基本原理就是動態代理思想,在設計模式之代理模式中有介紹過這兩種動態代理的使用與基本原理,再次不再敘述。

這裡分析的是,在spring中是如何基於動態代理的思想實現aop的。為了方便了解接下來的原始碼分析,這裡簡單化了一個流程圖分析aop的基本實現思想。

so,基於上面的流程,一步步分析spring原始碼中的aop實現方式。

採用一個簡單的aop例子,利用基於註解設定方式的切面設定,分析一個簡單的Before AOP例子。在spring boot下執行以下簡單例子。

AOP的advisor和advice設定。

@Component
@Aspect
public class AopConfig {
    @Pointcut("execution(* com.garine.debug.testcase.model.AopObject..*(..))")
    public void mypoint(){
        //切面定義
    }
    @Before("mypoint()")
    public void doAround() throws Throwable {
        System.out.println("before logic");
    }
}

AopObject,被代理攔截物件。

@Component
public class AopObject {
    public void aoped(){
        System.out.println("logic");
    }
}

代理實現的處理器(BeanPostProcessor)

首先是第一步內容,對我們在AopConfig中的AOP設定內容進行解析並且儲存到BeanFactory中,這個過程就是解析儲存切面資訊。

代理實現的源頭–AnnotationAwareAspectJAutoProxyCreator

經過一遍的程式碼跟蹤,我瞭解到註解方式的AOP設定,都離不開一個類–AnnotationAwareAspectJAutoProxyCreator,這個類繼承了BeanPostProcessor介面,我們都知道BeanPostProcessor的實現類有多個執行處理節點,其中一個執行節點就是在Bean範例化之後。也就是在這個時機AnnotationAwareAspectJAutoProxyCreator攔截bean的初始化過程,根據提前解析得到的切面資訊,對bean的方法進行嘗試適配,如果有匹配則需要進行代理建立。

這裡先分析的就是AnnotationAwareAspectJAutoProxyCreator,在bean範例化第一次查詢所有切面資訊時,就會解析儲存Aop的資訊到範例中,跟蹤以下程式碼。

AbstractApplicationContext#refresh (上下文初始化主幹方法)

AbstractApplicationContext#registerBeanPostProcessors (執行範例化並儲存所有實現BeanPostProcessor介面的類)

按照上面的邏輯,registerBeanPostProcessors 會比一般的bean範例化邏輯要早執行,因此我們接下來只需要分析AnnotationAwareAspectJAutoProxyCreator的初始化過程。

AnnotationAwareAspectJAutoProxyCreator的繼承結構

通過上圖可以知道,AnnotationAwareAspectJAutoProxyCreator是繼承了BeanfactoryAware介面,所以在範例化時,會執行setFactory方法。而所有切面資訊解析的執行者BeanFactoryAspectJAdvisorsBuilderAdapter初始化的時機也是在setFactory方法。

跟蹤程式碼如下。

AbstractAdvisorAutoProxyCreator#setBeanFactory

AnnotationAwareAspectJAutoProxyCreator#initBeanFactory

在這個方法裡面會新建一個BeanFactoryAspectJAdvisorsBuilderAdapter,這個物件會根據Beanfactory內的aop設定資訊,進行解析儲存。但是需要注意,此時雖然新建了BeanFactoryAspectJAdvisorsBuilderAdapter物件.但是此時還不會馬上解析aop設定,需要在第一次個普通bean範例化時才執行解析aop設定。解析的方法就是

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors,會在初次執行AnnotationAwareAspectJAutoProxyCreator呼叫postProcessBeforeInitialization時開始執行。

protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   super.initBeanFactory(beanFactory);
    //aspectJAdvisorsBuilder#buildAspectJAdvisors就是解析設定入口
   this.aspectJAdvisorsBuilder =
         new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}

代理物件(Proxy)的建立

解析並快取切面

上面提到繼承結構圖中,AnnotationAwareAspectJAutoProxyCreator是實現了InstantiationAwareBeanPostProcessor介面的,InstantiationAwareBeanPostProcessor介面定義的postProcessBeforeInitialization方法是一個可以對已經注入依賴屬性的bean物件範例進行編輯操作的介面,會在

AbstractAutowireCapableBeanFactory#doCreateBean

AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation

方法中執行InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation,初次初始化快取切面資訊的話就是在這個方法裡面。。

具體的呼叫鏈如上所示。這裡的postProcessBeforeInstantiation方法實際上是AnnotationAwareAspectJAutoProxyCreator的範例進行呼叫,AnnotationAwareAspectJAutoProxyCreator實現InstantiationAwareBeanPostProcessor介面。

下面進入InstantiationAwareBeanPostProcessor#postProcessBeforeInitialization方法分析程式碼。

AbstractAutoProxyCreator#postProcessBeforeInstantiation

AspectJAwareAdvisorAutoProxyCreator#shouldSkip (關鍵程式碼)

進入如下程式碼AbstractAutoProxyCreator,這個範例也就是之前一開始初始化的AnnotationAwareAspectJAutoProxyCreator範例,進入範例的shouldSkip 方法

	@Override
	protected boolean shouldSkip(Class<?> beanClass, String beanName) {
		// TODO: Consider optimization by caching the list of the aspect names
        //預先解析快取切面資訊
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		for (Advisor advisor : candidateAdvisors) {
			if (advisor instanceof AspectJPointcutAdvisor) {
				if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
					return true;
				}
			}
		}
		return super.shouldSkip(beanClass, beanName);
	}

AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

方法findCandidateAdvisors程式碼如下,這裡是預先解析快取所有切面advisor資訊,注意這一步操作是在AbstractAutoProxyCreator#postProcessBeforeInitialization處理,也就是開頭提到的切面解析操作,解析完成就進行快取。

@Override
protected List<Advisor> findCandidateAdvisors() {
   // Add all the Spring advisors found according to superclass rules.
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
    //這裡就是前面提到的BeanFactoryAspectJAdvisorsBuilder解析所有切面資訊的呼叫點
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}

然後繼續在這裡先提前看一下是如何解析aop設定的。跟蹤BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors

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>();
          //查詢出Beanfactory中所有已經註冊的BeanName
         String[] beanNames =
               BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
         for (String beanName : beanNames) {
            if (!isEligibleBean(beanName)) {
               continue;
            }
            // We must be careful not to instantiate beans eagerly as in this
            // case they would be cached by the Spring container but would not
            // have been weaved
            Class<?> beanType = this.beanFactory.getType(beanName);
            if (beanType == null) {
               continue;
            }
             //判斷Bean是否是切面Bean,isAspect方法判斷[標註1]
            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);
                   //解析aop class的設定,包返回Advisor物件[標註2]
                  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 {
                  // Per target or per this.
                  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;
}

**[標註1]如何判斷類是否是aop切面設定類? **

通過以下程式碼。

@Override
public boolean isAspect(Class<?> clazz) {
   return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
    //包含@Aspect註解
   return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}

[標註2]如何解析為Advisor物件?

ReflectiveAspectJAdvisorFactory#getAdvisors 遍歷所有沒被@PointCut註解標註的方法,也就是遍歷切面內容方法

ReflectiveAspectJAdvisorFactory#getAdvisor 處理所有沒被@PointCut註解標註的方法,候選切面內容方法

程式碼如下。

@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName) {
   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
   //解析判斷候選方法是否有@Before,@After,@Around等註解,如果有,就繼續執行新建Advisor物件。
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }
//建立advisor
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

最終迴圈解析,@Before,@After,@Around等標註的方法都會新建一個Advisor物件。新建的Advisor物件都儲存在BeanFactoryAspectJAdvisorsBuilder#advisorsCache中,當AnnotationAwareAspectJAutoProxyCreator攔截bean的建立過程時,從這裡面適配是否有切面可用。

這裡解析得到的Advisor,大概有以下資訊。下面的資訊中,並沒有對@PointCut註解做處理,pointCut屬性只得出一個"mypoint()",此時還不知道Advisor實際對應的攔截表示式。

攔截表示式還是空的,會在AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInstantiation第一次執行時解析攔截表示式。

適配切面

在AbstractAutoProxyCreator#postProcessAfterInitialization執行時,找到上面AbstractAutoProxyCreator#postProcessBeforeInitialization快取的所有的切面資訊,之後是如何進行切面適配,從而決定是否需要進行代理物件的建立呢?

在呼叫AbstractAutoProxyCreator#postProcessAfterInitialization方法時,進行切面適配,並且會根據適配建立代理物件。根據以下呼叫鏈。

AbstractAutoProxyCreator#postProcessAfterInitialization

AbstractAutoProxyCreator#wrapIfNecessary

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;
   }
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }
   // Create proxy if we have advice.
    //查詢匹配切面
   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;
}

AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    //從快取取出所有切面資訊
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //根據advisor資訊中的表示式進行方法對class的匹配
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

此時如果是第一次執行適配方法findAdvisorsThatCanApply的話,candidateAdvisors中的攔截表示式還是空的,需要進行表示式獲取,也就是@Pointcut的value。spring的操作的在第一次執行findAdvisorsThatCanApply時解析獲取攔截表示式的值,獲得攔截表示式值之後就跟當前class的方法進行匹配看是否需要進行代理。

繼續往下跟蹤程式碼

AopUtils#canApply(org.springframework.aop.Advisor, java.lang.Class<?>, boolean)

AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)

AspectJExpressionPointcut#getClassFilter

AspectJExpressionPointcut#checkReadyToMatch

private void checkReadyToMatch() {
   if (getExpression() == null) {
      throw new IllegalStateException("Must set property 'expression' before attempting to match");
   }
   if (this.pointcutExpression == null) {
      this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
            ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
            ClassUtils.getDefaultClassLoader());
       //解析得到攔截表示式,例如根據@Before的value來關聯查詢出對應的表示式
      this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
   }
}

最終解析完之後,advisor中的表示式資訊結構如下圖。包含在pointcut屬性中,匹配時就根據pointcutExpression迴圈進行匹配class的方法。有興趣的可以繼續偵錯看看是如何實現匹配表示式的。

##  建立代理物件

如果在上面的匹配切面過程中,發現適配的切面,那就需要進行代理物件的建立了。

我們回到上面的AbstractAutoProxyCreator#wrapIfNecessary,主要看程式碼如下。

  // Create proxy if we have advice.
    //查詢匹配切面
   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;
   }

所以,繼續看

AbstractAutoProxyCreator#createProxy

的建立代理物件方法。設定ProxyFactory建立Proxy需要的一切資訊。

protected Object createProxy(
      Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }
    //新建代理物件工廠
   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);
    //設定工廠代理類
   if (!proxyFactory.isProxyTargetClass()) {
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
    //設定攔截切面
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   for (Advisor advisor : advisors) {
      proxyFactory.addAdvisor(advisor);
   }
    //設定被代理物件
   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);
   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
//建立代理物件
   return proxyFactory.getProxy(getProxyClassLoader());
}

下面看ProxyFactory是如何建立代理物件,繼續跟蹤proxyFactory.getProxy(getProxyClassLoader());

public Object getProxy(ClassLoader classLoader) {
   return createAopProxy().getProxy(classLoader);
}

createAopProxy()作用是根據class的種類判斷採用的代理方式,看如下實現

DefaultAopProxyFactory#createAopProxy

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            //採用jdk動態代理必須基於介面
			return new JdkDynamicAopProxy(config);
		}
        //基於cglib實現代理不需要介面
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

所以在當前偵錯的例子中,使用cglib代理。所以執行如下代理。

@Override
public Object getProxy(ClassLoader classLoader) {
     //。。。。。。
      // Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
	//獲取攔截回撥函數
      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);
      // Generate the proxy class and create a proxy instance.
    //返回一個cglib代理物件
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException ex) {
       //、、、、、、
   }
}

getCallbacks(rootClass);在這個獲取回撥函數的方法中,普通的aop採用的回撥函數是如下的方式。

// Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

cglib 的aop回撥函數如下。

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Class<?> targetClass = null;
   Object target = null;
   try {
       //這裡注入的advised就是之前建立的ProxyFactory物件
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // May be null. Get as late as possible to minimize the time we
      // "own" the target, in case it comes from a pool...
      target = getTarget();
      if (target != null) {
         targetClass = target.getClass();
      }
       //根據切面資訊建立切面內容呼叫鏈
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
         // We need to create a method invocation...
          //建立一個方法呼叫物件,具體呼叫實現沒分析,Before邏輯大概是先呼叫切面,在反射呼叫目標方法
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null) {
         releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com