1. 程式人生 > 其它 >老生常談系列之Aop--Spring Aop原始碼解析(一)

老生常談系列之Aop--Spring Aop原始碼解析(一)

老生常談系列之Aop--Spring Aop原始碼解析(一)

前言

上一篇文章老生常談系列之Aop--Spring Aop原理淺析大概闡述了動態代理的相關知識,並且最後的圖給了一個Spring Aop實現的邏輯猜測,這是一種很普通的情形,如下圖:

那下面,我們就通過原始碼分析來看Spring到底是怎麼操作的,Spring是如何識別切面並且生成動態代理的。

在講解Spring Aop實現之前,插兩句Spring IOC的實現,因為Aop是依賴了IOC的實現的。可以參考Spring官網的原文如下:

One of the key components of Spring is the AOP framework. 
While the Spring IoC container does not depend on AOP (meaning you do not need to use AOP if you don’t want to), 
AOP complements Spring IoC to provide a very capable middleware solution.

翻譯一下:算了,懶得翻了,這應該都能看懂。唉,還是翻譯一下吧。Aop是Spring框架的一個重要元件,Ioc容器是不依賴Aop的,如果你不需要使用Aop你可以不管它。Aop完善了Spring Ioc的功能,提供了一個強大的中介軟體解決方案。中介軟體解決方案這要說一下了,Sentinel就是用了Aop去實現的,有興趣可以去看一下它的程式碼SentinelResourceAspect

再看一句抄自5.2. Spring AOP Capabilities and Goals的:

Spring AOP’s approach to AOP differs from that of most other AOP frameworks. 
The aim is not to provide the most complete AOP implementation (although Spring AOP is quite capable).
Rather, the aim is to provide a close integration between AOP implementation and Spring IoC, to help solve common problems in enterprise applications.

翻譯一下:Spring Aop實現的思路跟其他的Aop框架是不一樣的。它的目標不是提供完整的Aop功能(儘管Spring Aop已經相當完整了),相反,它意在提供Aop和Spring Ioc容器間的整合以更好地解決企業級應用中普遍存在的問題。

好了,那麼Aop依賴了Ioc,這話從何談起,哪裡產生了依賴。相信大家如果看過Spring原始碼,都知道BeanPostProcessor這個處理器,它是用於在Bean初始化的前後提供定製化的操作的。摘抄註釋看一下:

 Factory hook that allows for custom modification of new bean instances —
for example, checking for marker interfaces or wrapping beans with proxies.

翻譯一下:BeanPostProcessor允許自定義修改bean例項,例如檢查標記介面或者用代理包裝bean。看了BeanPostProcessor註釋後,那麼反過來再看一下上圖Spring收集好切面後,是不是就是要在Bean初始化的時候判斷,這個Bean是不是被切了,是否需要進行代理?這很符合Spring的尿性,毫無疑問,Spring就是這樣實現的。

原始碼分析

以下程式碼分析基於Spring版本5.2.x,另外部分地方我摘抄保留了英文註釋,希望看官可以用心體會。

從上面的分析可知,入口在BeanPostProcessor這個介面,那麼Aop功能裡實現了BeanPostProcessor介面的類,就是連線Ioc和Aop的橋樑的,這裡直接把幕後真凶揪出來,就是AbstractAutoProxyCreator,可以先記住這玩意,這是我們建立Aop代理的核心類。

接下來的分析是假設你已經對Spring Ioc有一定了解的基礎上進行的,否則形如BeanPostProcessor看起來都非常懵逼的話可能效果會不太好。建議先看Spring Ioc 原始碼分析系列

那接下來,我們開始Aop部分的原始碼解析之旅,限於文章的篇幅,這篇只會討論Spring獲取切面增強的部分,生產動態代理的在下一篇老生常談系列之Aop--Spring Aop原始碼解析(二)闡述。

AspectJAutoProxyRegistrar註冊器註冊AnnotationAwareAspectJAutoProxyCreator

先來看AspectJAutoProxyRegistrar,回憶上一篇文章的例子,我們是通過下列程式碼

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}

來開啟Aop支援的,這裡的關鍵是@EnableAspectJAutoProxy註解,我們跟進檢視@EnableAspectJAutoProxy的程式碼。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * 是否強制使用CGLIB代理
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * 是否暴露代理到上下文,這裡用來處理同一個類間方法呼叫代理不生效的問題
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

可以看到有兩個屬性,註釋如上,但是這裡重點在類上的註解@Import(AspectJAutoProxyRegistrar.class),這裡引入了AspectJAutoProxyRegistrar類,這個類實現了ImportBeanDefinitionRegistrar,只有一個實現方法,在註冊BeanDefinition的時候會呼叫其registerBeanDefinitions()方法註冊想要的BeanDefinition。這裡涉及Spring Ioc 部分的@Import註解實現原理,可以參考Spring容器中@Import註解的實現原理。下面把AspectJAutoProxyRegistrar的程式碼貼出來。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		//向容器註冊一個基於註解的自動代理建立器
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			//表示強制指定了要使用CGLIB
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			//表示強制暴露bean的代理物件到AopContext
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

這個類的作用是註冊一個AnnotationAwareAspectJAutoProxyCreator,並且根據proxyTargetClassexposeProxy屬性進行了設定。通過xml方式<aop:aspectJ-autoproxy/> 開啟Aop也是註冊了這個類,所以到了註冊AnnotationAwareAspectJAutoProxyCreator後,xml的實現和註解的實現殊途同歸。

一路跟進AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);方法的實現

	@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

		// 如果已經存在了自動代理建立器且存在的自動代理建立器與現在的不一致,則要根據優先順序來判斷到底需要使用哪個
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					// 改變bean最重要的就是改變bean所對應的className屬性
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			// 如果已經存在了自動代理建立器且存在的自動代理建立器與現在的一致,那麼無需再建立
			return null;
		}

		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

以上程式碼中實現了自動註冊AnnotationAwareAspectJAutoProxyCreator類的功能,同時這裡還涉及了一個優先順序的問題,如果已經存在了自動代理建立器,而且存在的自動代理建立器與現在的不一致,那麼需要根據優先順序來判斷到底需要使用哪個。

註冊好了AnnotationAwareAspectJAutoProxyCreator後,Spring註冊了AnnotationAwareAspectJAutoProxyCreator,那麼肯定就是用到了它提供的功能,接下來就分析AnnotationAwareAspectJAutoProxyCreator的功能。先來看一下AnnotationAwareAspectJAutoProxyCreator的繼承關係。

可以看到繼承的層次還是比較多的,但是這裡可以重點關注AbstractAutoProxyCreator類,這個父類完成了絕大數的邏輯,這也符合Spring的尿性,Spring一般是由抽象父類實現大部分通用的邏輯。Spring裡面的例子比比皆是,例如AbstractApplicationContextAbstractBeanFactoryAbstractHandlerMethodMapping等等。

到這裡,分析進入了一個關鍵點,註冊了AnnotationAwareAspectJAutoProxyCreator,而AnnotationAwareAspectJAutoProxyCreator實現了AbstractAutoProxyCreator,那下面的重點邏輯就落入了AbstractAutoProxyCreator裡面,接下來分析AbstractAutoProxyCreator的實現。

AbstractAutoProxyCreator實現Aop和Ioc整合

上面我們說了,Aop與Ioc的整合是通過BeanPostProcessor來實現的,我們精簡一下AbstractAutoProxyCreator的繼承結構如下。

AbstractAutoProxyCreator實現了三個介面,其中關鍵的是BeanPostProcessor,BeanPostProcessor裡有兩個方法postProcessBeforeInitialization(Object bean, String beanName)postProcessAfterInitialization(Object bean, String beanName)。這兩個方法分別提供了再Bean初始之前和之後的回撥操作,Aop的核心奧義在AbstractAutoProxyCreator#postProcessAfterInitialization(@Nullable Object bean, String beanName),Spring在這裡判斷是否需要代理,那麼接下來我們就分析AbstractAutoProxyCreator的實現。

	/**
	 * Create a proxy with the configured interceptors if the bean is
	 * identified as one to proxy by the subclass.
	 * @see #getAdvicesAndAdvisorsForBean
	 */
	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			// /根據給定的 bean的class和name構建一個key,格式:beanClassName_beanName
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			/**
			 * 當bean被迴圈引用,並且被暴露了
			 * 則會通過getEarlyBeanReference來建立代理類
			 * 通過判斷earlyProxyReferences中是否存在beanName來決定是否需要對target進行動態代理
			 */
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				//該方法將會返回代理類
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

我們來看一下到達wrapIfNecessary()的呼叫鏈,這部分是Ioc的邏輯,所以這裡簡單貼出個呼叫鏈,不深入分析。

AbstractApplicationContext#refresh() ->
AbstractApplicationContext#finishBeanFactoryInitialization() ->
DefaultListableBeanFactory#preInstantiateSingletons() ->
AbstractBeanFactory#getBean() ->
AbstractBeanFactory#doGetBean() ->
DefaultSingletonBeanRegistry#getSingleton() ->
AbstractAutowireCapableBeanFactory#createBean() ->
AbstractAutowireCapableBeanFactory#doCreateBean() ->
AbstractAutowireCapableBeanFactory#initializeBean() ->
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization() ->
AbstractAutoProxyCreator#postProcessAfterInitialization()

以上是Spring Ioc獲取Bean的經典呼叫鏈,可以看到Aop的實現是整合進入了這個呼叫鏈裡,所以關鍵點在於wrapIfNecessary(bean, beanName, cacheKey)方法裡,先來看一下它的時序圖。

通過時序圖可以清晰看到呼叫的過程,那麼接下來就分析一下這個過程。首先看getAdvicesAndAdvisorsForBean(),看方法名就是獲取所有的AdvicesAdvisors,那我們就從這開始獲取切面增強方法之路。

	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

對於指定 bean 的增強方法的獲取一定是包含兩個步驟的, 獲取所有的增強以及尋找所有增強中適用於 bean 的增強並應用, 那麼findCandidateAdvisors findAdvisorsThatCanApply 便是做了這兩件事情。 當然,如果無法找到對應的增強器便返回 DO_NOT_PROXY,其中DO_NOT_PROXY=null

	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;
	}

由於我們分析的是使用註解進行的 AOP,所以對於 自findCandidateAdvisors的實現其實是由 AnnotationAwareAspectJAutoProxyCreator 類完成的,子類的實現覆蓋了父類的實現,我們繼續跟蹤 AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors ()方法。

	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
        // 當使用註解方式配置 AOP 的時候並不是丟棄了對 XML 配置的支援,
        // 在這裡呼叫父類方法載入配置檔案中的 AOP 宣告
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
            // 這裡獲取當前BeanFactory中所有被@Aspect註釋的bean,並且獲取bean中定義的切面
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

AnnotationAwareAspectJAutoProxyCreator 間接繼承了 AbstractAdvisorAutoProxyCreator, 在實現獲取切面方法中除了保留父類的獲取配置檔案中定義的增強外,同時添加了獲取 Bean 的註解增強的功能,那麼其實現正是由 this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 來實現的。

這一步是相當繁雜的,這裡獲取當前BeanFactory中所有被@Aspect註釋的bean,並且獲取bean中定義的切面,想一下,如果你是來實現,會怎麼做?這裡思考一下,可能有以下幾步:

  • 獲取當前BeanFactory中的所有的Bean
  • 獲取所有被@Aspect註解標註的Bean
  • 解析提取出其中的切面增強方法
  • 放入快取advisorsCacheaspectFactoryCache

那麼下面來分析一下Spring Aop是不是這樣實現的。

BeanFactoryAspectJAdvisorsBuilder構建切面增強方法

先來看buildAspectJAdvisors()方法,方法略長,為了能夠展示所有細節,這裡不做程式碼縮減,將程式碼全部貼出,可以跟著註釋看一下,這裡實現的邏輯順序,就是我們上面分析的幾點,看來Spring也是平常人的思路,所以不要慌,問題不大。

	/**
	 * Look for AspectJ-annotated aspect beans in the current bean factory,
	 * and return to a list of Spring AOP Advisors representing them.
	 * <p>Creates a Spring Advisor for each AspectJ advice method.
	 * @return the list of {@link org.springframework.aop.Advisor} beans
	 * @see #isEligibleBean
	 */
	public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;

		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					List<Advisor> advisors = new ArrayList<>();
					aspectNames = new ArrayList<>();
					// 獲取所有beanName
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					// 迴圈所有beanName找到對應的增強方法
					for (String beanName : beanNames) {
						// 如果bean不合法則略過,由子類定義規則,預設返回true
						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.
						// 獲取對應的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 {
								// 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 ArrayList<>();
		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;
	}

這裡比較重要也是最為繁雜的方法是List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory),這裡會進行Advisor的提取,具體實現類為ReflectiveAspectJAdvisorFactory

ReflectiveAspectJAdvisorFactory獲取Advisor

獲取Advisor

接下來的邏輯在ReflectiveAspectJAdvisorFactory類裡,首先跟著註釋看一下getAdvisors()方法。

	@Override
	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
		// 獲取標記為AspectJ的類
		Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
		// 獲取標記為AspectJ的name
		String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
		// 驗證
		validate(aspectClass);

		// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
		// so that it will only instantiate once.
		MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
				new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

		List<Advisor> advisors = new ArrayList<>();
		for (Method method : getAdvisorMethods(aspectClass)) {
			Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
			if (advisor != null) {
				advisors.add(advisor);
			}
		}

		// If it's a per target aspect, emit the dummy instantiating aspect.
		if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
			// 如果尋找到的增強器不為空而且又配置了增強延遲初始化,那麼需要在首位加入同步例項化增強器,這個SyntheticInstantiationAdvisor會在切面方法例如類似@Before執行前呼叫aif.getAspectInstance()提前初始化切面
			Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
			advisors.add(0, instantiationAdvisor);
		}

		// Find introduction fields. 獲取DeclareParent註解
		for (Field field : aspectClass.getDeclaredFields()) {
			Advisor advisor = getDeclareParentsAdvisor(field);
			if (advisor != null) {
				advisors.add(advisor);
			}
		}

		return advisors;
	}

這一段邏輯還是簡單明瞭的,首先獲取被@Aspect註解的class物件,驗證類是否合法,然後通過getAdvisorMethods(aspectClass)獲取所有的切面方法,接著用getAdvisor()生成advisor,考慮到配置中可能存在將增強配置成延遲初始化,那麼需要在首位加入同步例項化增強器以保證增強使用之前的例項化,最後是對 @DeclareParents 註解的獲取。

獲取所有的增強方法getAdvisorMethods().

	private List<Method> getAdvisorMethods(Class<?> aspectClass) {
		final List<Method> methods = new ArrayList<>();
		ReflectionUtils.doWithMethods(aspectClass, method -> {
			// Exclude pointcuts
			if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
				methods.add(method);
			}
		}, ReflectionUtils.USER_DECLARED_METHODS);
		methods.sort(METHOD_COMPARATOR);
		return methods;
	}

根據上面獲取的method生成advisor,統一封裝成InstantiationModelAwarePointcutAdvisorImpl,這是Advisor的內建實現。

1.首先看切點資訊的獲取

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

		validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

		// 切點資訊的獲取
		AspectJExpressionPointcut expressionPointcut = getPointcut(
				candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
		if (expressionPointcut == null) {
			return null;
		}

		// 根據切點資訊生成增強器
		return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
				this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
	}

我們看一下切點資訊獲取的方法getPointcut(),這裡獲取的標註了切面的註解的方法,生成一個AspectJExpressionPointcut設定表示式後返回。

	private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
        // 獲取所有的標記了切面註解的方法,例如Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class等
		AspectJAnnotation<?> aspectJAnnotation =
				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
		if (aspectJAnnotation == null) {
			return null;
		}

        // 生成一個AspectJExpressionPointcut
		AspectJExpressionPointcut ajexp =
				new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
        // 提取得到的註解中的表示式,如:
        // @Pointcut("execution(* io.codegitz.aop.demo.service.LoginService.login(..))")中的execution(* io.codegitz.aop.demo.service.LoginService.login(..))
		ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
		if (this.beanFactory != null) {
			ajexp.setBeanFactory(this.beanFactory);
		}
		return ajexp;
	}

獲取所有的標記了切面註解的方法AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod).

	private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
			Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
	
	protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
		for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
			AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
			if (foundAnnotation != null) {
				return foundAnnotation;
			}
		}
		return null;
	}

返回物件AspectJExpressionPointcut的建構函式如下,只是簡單的賦值,沒有特別的操作。

	public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
		this.pointcutDeclarationScope = declarationScope;
		if (paramNames.length != paramTypes.length) {
			throw new IllegalStateException(
					"Number of pointcut parameter names must match number of pointcut parameter types");
		}
		this.pointcutParameterNames = paramNames;
		this.pointcutParameterTypes = paramTypes;
	}

2.根據切點資訊生成切面增強。

上面已經獲取了切點資訊,接下來就可以根據切點資訊生成切面增強方法,所有的增強都由 Advisor的實現類 InstantiationModelAwarePointcutAdvisorImpl 統一封裝的。

	public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
			Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

		this.declaredPointcut = declaredPointcut;
		this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
		this.methodName = aspectJAdviceMethod.getName();
		this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
		this.aspectJAdviceMethod = aspectJAdviceMethod;
		this.aspectJAdvisorFactory = aspectJAdvisorFactory;
		this.aspectInstanceFactory = aspectInstanceFactory;
		this.declarationOrder = declarationOrder;
		this.aspectName = aspectName;

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

			// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
			// If it's not a dynamic pointcut, it may be optimized out
			// by the Spring AOP infrastructure after the first evaluation.
			this.pointcut = new PerTargetInstantiationModelPointcut(
					this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
			this.lazy = true;
		}
		else {
			// A singleton aspect.
			this.pointcut = this.declaredPointcut;
			this.lazy = false;
            // 重點關注,對切面增強器進行初始化
			this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
		}
	}

在封裝的過程中,所有的資訊都是進行簡單的賦值,在賦值的同時會instantiatedAdvice進行初始化,因為不同的增強所體現的邏輯是不同的,比如 @Before("test()")@After("test()")標籤的不同就是增強器增強的位置不同 ,所以就需要不同的增強器來完成不同的邏輯,而根據註解中的資訊初始化對應的增強器就是在 instantiateAdvice() 函式中實現的。

	private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
		Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
				this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
		return (advice != null ? advice : EMPTY_ADVICE);
	}

這裡會根據不同切面型別返回不同的實現。

	@Override
	@Nullable
	public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

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

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

		// 省略日誌...

		AbstractAspectJAdvice springAdvice;

		// 根據不同的型別生成不同的增強
		switch (aspectJAnnotation.getAnnotationType()) {
			case AtPointcut:
				if (logger.isDebugEnabled()) {
					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
				}
				return null;
			case AtAround:
				springAdvice = new AspectJAroundAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtBefore:
				springAdvice = new AspectJMethodBeforeAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfter:
				springAdvice = new AspectJAfterAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfterReturning:
				springAdvice = new AspectJAfterReturningAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
					springAdvice.setReturningName(afterReturningAnnotation.returning());
				}
				break;
			case AtAfterThrowing:
				springAdvice = new AspectJAfterThrowingAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
				}
				break;
			default:
				throw new UnsupportedOperationException(
						"Unsupported advice type on method: " + candidateAdviceMethod);
		}

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

		return springAdvice;
	}

從上面的程式碼可以看到,Spring會根據不同的型別生成不同的實現,其中不同型別間的實現會有些差異。AtBefore會對應AspectJMethodBeforeAdvice,而在 AspectJMethodBeforeAdvice中完成了增強方法的邏輯。 我們嘗試分析幾個常用的增強器實現。

MethodBeforeAdviceInterceptor前置攔截--最常用的攔截器之一

如果看了上一篇,會發現上一篇實現CGLIB代理用到的就是MethodBeforeAdviceInterceptor,這裡再次出現,基本可以驗證了我們文章開頭猜測圖的邏輯

看下類上註釋,這個類就是包裝了一個MethodBeforeAdvice,也就是我們上面說的AspectJMethodBeforeAdvice,然後把這個MethodBeforeAdviceInterceptor放到呼叫鏈裡,在執行的時候會呼叫invoke()方法,這裡會通過this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis())真實呼叫到切面增強方法,從而實現了在業務程式碼裡嵌入切面的邏輯。

/**
 * Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}.
 * Used internally by the AOP framework; application developers should not need
 * to use this class directly.
 *
 * @author Rod Johnson
 * @see AfterReturningAdviceInterceptor
 * @see ThrowsAdviceInterceptor
 */
@SuppressWarnings("serial")
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

	private final MethodBeforeAdvice advice;

	/**
	 * Create a new MethodBeforeAdviceInterceptor for the given advice.
	 * @param advice the MethodBeforeAdvice to wrap
	 */
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}

}

進入到AspectJMethodBeforeAdvice#before()方法,這一段邏輯沒啥好說的,最後就是呼叫this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs)執行了切面增強方法。

	@Override
	public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
	/**
	 * Invoke the advice method.
	 * @param jpMatch the JoinPointMatch that matched this execution join point
	 * @param returnValue the return value from the method execution (may be null)
	 * @param ex the exception thrown by the method execution (may be null)
	 * @return the invocation result
	 * @throws Throwable in case of invocation failure
	 */
	protected Object invokeAdviceMethod(
			@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
			throws Throwable {

		return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
	}

執行增強方法。

	protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
		Object[] actualArgs = args;
		if (this.aspectJAdviceMethod.getParameterCount() == 0) {
			actualArgs = null;
		}
		try {
			ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
			// TODO AopUtils.invokeJoinpointUsingReflection
			// 啟用增強方法
			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();
		}
	}
AspectJAfterAdvice後置攔截

後置增強與前置增強有稍許不一致的地方。 回顧之前講過的前置增強,大致的結構是在攔截器鏈中放置 MethodBeforeAdviceInterceptor,而在MethodBeforeAdviceInterceptor中又放置了 AspectJMethodBeforeAdvice,並在呼叫 invoke 時首先串聯呼叫。 但是在後置增強的時候卻不一 樣,沒有提供中間的類,而是直接在攔截器鏈中使用了中間的AspectJAfterAdvice

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		finally {
            // 啟用切面增強方法
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}

AbstractAdvisorAutoProxyCreator返回可以應用的切面增強方法

上一小節ReflectiveAspectJAdvisorFactory獲取Advisor的邏輯解析已經獲取到了所有的切面方法增強,並且已經封裝成統一的格式,但是這裡返回的是所有的切面增強方法,並不一定適合當前的Bean,所以下一步我們需要找出合適的切面增強器,那麼我們回到AbstractAdvisorAutoProxyCreator#findEligibleAdvisors()方法裡

這裡最難的一步findCandidateAdvisors()已經完成了,接下來是挑取出適合的增強器,也就是滿足我們配置的萬用字元的增強器。

	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方法,

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
		List<Advisor> eligibleAdvisors = new ArrayList<>();
		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) {
				// already processed
				continue;
			}
            // 對於普通 bean 的處理
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

findAdvisors ThatCanApply 函式的主要功能是尋找所有增強器中適用於當前 class 的增強 器。引介增強與普通的增強處理是不一樣的 , 所以分開處理。 而對於真正的匹配在 canApply 中實現。

	/**
	 * Can the given advisor apply at all on the given class?
	 * This is an important test as it can be used to optimize
	 * out a advisor for a class.
	 * @param advisor the advisor to check
	 * @param targetClass class we're testing
	 * @return whether the pointcut can apply on any method
	 */
	public static boolean canApply(Advisor advisor, Class<?> targetClass) {
		return canApply(advisor, targetClass, false);
	}

	/**
	 * Can the given advisor apply at all on the given class?
	 * <p>This is an important test as it can be used to optimize out a advisor for a class.
	 * This version also takes into account introductions (for IntroductionAwareMethodMatchers).
	 * @param advisor the advisor to check
	 * @param targetClass class we're testing
	 * @param hasIntroductions whether or not the advisor chain for this bean includes
	 * any introductions
	 * @return whether the pointcut can apply on any method
	 */
	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 {
			// It doesn't have a pointcut so we assume it applies.
			return true;
		}
	}

這段邏輯區分了IntroductionAdvisorPointcutAdvisor,然後再去執行不同的邏輯去匹配是否能執行。advisor是Spring Aop裡面一個重要的概念,IntroductionAdvisorPointcutAdvisor其中的兩個重要的實現,這兩者有什麼區別呢❓ 簡單來說主要是粒度不一樣,前者是作用來類上的,後者是作用在方法上,粒度更細,也更常用。更多advisor的概念和Spring的實現體系可以檢視Spring Aop中advisor概念以及實現詳解

總結

這一篇主要介紹了Spring Aop獲取所有的切面增強方法的程式碼邏輯,總得來說思路是清晰的,從時序圖看著比較明瞭,但是裡面涉及的細節邏輯比較多,一篇下來程式碼量也很大,看起來可能會頭疼。如果看著看著就暈車了,可以返回去看時序圖,看當前是執行到了哪一步,做到心中有數。或者可以寫個例子跟著程式碼除錯,效果會更好。

再次回顧這個圖,這篇文章回答了一部分問題,下一部分建立動態代理,老生常談系列之Aop--Spring Aop原始碼解析(二)分析。

如果有人看到這裡,那在這裡老話重提。與君共勉,路漫漫其修遠兮,吾將上下而求索。