深入理解 SpringAOP(三):AspectJ支持

概述

在之前的文章中,我們已經對 SpringAOP 的運行機制有了清晰的瞭解。然而,在本文中我們將補充關於 AspectJ 切面的內容。
儘管我們可以使用 AspectJ 的註解來定義切面邏輯,但實際上它們的實現仍然基於 Advisor 和方法攔截器。我們可以在最常用的 AutoProxyCreator,也就是 AnnotationAwareAspectJAutoProxyCreator 中找到相關證據。
AnnotationAwareAspectJAutoProxyCreatorAutoProxyCreator 的最底層實現類。與另一個底層實現類 BeanNameAutoProxyCreator 相比,它主要區別在於對 AspectJ 的支持。以下是該類的部分源碼:

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

	// 正則表達式匹配器
	@Nullable
	private List<Pattern> includePatterns;
	// AspectJ 專用的通知器工廠
	@Nullable
	private AspectJAdvisorFactory aspectJAdvisorFactory;
	// AspectJ 專用的通知類構建器
	@Nullable
	private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;

}

接下來,我們將以這個類爲基礎展開討論。
通過閱讀本文,我們將深入瞭解 AnnotationAwareAspectJAutoProxyCreator 類的實現細節,包括它與 AspectJ 的集成和專用通知器工廠的使用。這將幫助我們更好地理解 SpringAOP 中 AspectJ 的支持,爲我們的 AOP 應用提供更多選擇和功能。

本專題共三篇文章,這是第三篇:

  • 深入理解 SpringAOP(一):AOP 組件概述
  • 深入理解 SpringAOP(二):AOP的執行流程;
  • 深入理解 SpringAOP(三):AspectJ支持;

一、AspectJ 通知器的獲取

對於 AnnotationAwareAspectJAutoProxyCreator ,我們只需要關注最核心的 findCandidateAdvisors 方法,它重寫自 AbstractAdvisorAutoProxyCreator ,在父類中,這一步用來找到 spring 容器中所有可用的 Advisor ,而它在父類的基礎上,又額外扔進去了用於支持 AspectJ 的額外通知器:

@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.
		if (this.aspectJAdvisorsBuilder != null) {
			// 通過 AspectJ 通知構建器創建一批通知器
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

然後是  buildAspectJAdvisors 方法:

public List<Advisor> buildAspectJAdvisors() {
	List<String> aspectNames = this.aspectBeanNames;

	// 沒有任何已加載的 AspectJ 通知類,通過雙重檢查進行初始化
	if (aspectNames == null) {
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new ArrayList<>();
				aspectNames = new ArrayList<>();
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Object.class, true, false);

				// 獲取並遍歷所有的beanName
				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, false);
					if (beanType == null) {
						continue;
					}

					// 檢查這個 bean 的類上是否有 @Aspect 註解且不由 AspectJ 編譯的類
					if (this.advisorFactory.isAspect(beanType)) {
						aspectNames.add(beanName);

						// 獲取這個切面類的元數據,即類型和各種註解
						AspectMetadata amd = new AspectMetadata(beanType, beanName);

						// 1、如果切面類是單例的
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
							// 通過切面工廠爲其創建單例的通知器
							MetadataAwareAspectInstanceFactory factory =
									new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
							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);
						}
						// 2、如果切面類是非單例的
						else {
							// 被代理的bean必須也是非單例的
							// 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");
							}
							// 爲其緩存切面工廠,每次都爲 bean 創建一個新的切面工廠
							MetadataAwareAspectInstanceFactory factory =
									new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
							this.aspectFactoryCache.put(beanName, factory);
							advisors.addAll(this.advisorFactory.getAdvisors(factory));
						}
					}
				}
				this.aspectBeanNames = aspectNames;
				return advisors;
			}
		}
	}

	// 如果已經初始化過切面緩存,則從緩存中:
	// 1、如果 bean 是單例的,那麼直接從緩存獲取通知器;
	// 2、如果 bean 是非單例的,那麼先從緩存獲取切面工廠,再創建通知器
	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;
}

這邊主要做了這麼些工作:

  • 從 Spring 容器中獲得所有類上帶有 @Aspect 註解且類不由 AspectJ 編譯 bean,這些 bean 就是切面對象;
  • 檢查切面類是否是單例的:
    1. 如果是切面類是單例的,那麼就創建切面/通知器工廠 MetadataAwareAspectInstanceFactory ,根據其生成一批通知器。假如要代理的 bean ,那就直接將通知器們註冊到緩存,否則就直接緩存該工廠;
    2. 如果切面類不是單例的,要代理的 bean 也不能是單例的。爲其創建工廠後,直接緩存該工廠;
  • 上述獲取切面,生成並緩存通知器工廠、通知器的操作僅進行一次,後續操作將直接進行後續步驟;
  • 當根據 bean 獲取通知器的時候,先嚐試直接從 advisorsCache 獲取通知器緩存,如果沒有再從獲取 aspectFactoryCache 獲取工廠,然後再創建通知器;

二、切面工廠

在上述代碼中,在爲切面創建了 MetadataAwareAspectInstanceFactory 後,通過 AspectJ 通知器工廠 AspectJAdvisorFactory 真正的創建了通知器。我們暫不關注 MetadataAwareAspectInstanceFactory 的實現邏輯,而關注從通知器工廠獲取通知器的方法 getAdvisors

// 來自 ReflectiveAspectJAdvisorFactory,其爲 AspectJAdvisorFactory 唯一一個實現類
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
	Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
	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<>();
	// 獲取切面類中所有不被 @Pointcut 註解、由用戶在類中直接聲明方法
	for (Method method : getAdvisorMethods(aspectClass)) {
		Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, 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()) {
		Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
		advisors.add(0, instantiationAdvisor);
	}

	// 掃描帶有 @DeclareParents 註解的屬性,並根據註解生成 DeclareParentsAdvisor
	// Find introduction fields.
	for (Field field : aspectClass.getDeclaredFields()) {
		Advisor advisor = getDeclareParentsAdvisor(field);
		if (advisor != null) {
			advisors.add(advisor);
		}
	}

	return advisors;
}

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

private List<Method> getAdvisorMethods(Class<?> aspectClass) {
	// MethodFilter adviceMethodFilter = ReflectionUtils.USER_DECLARED_METHODS
	//			.and(method -> (AnnotationUtils.getAnnotation(method, Pointcut.class) == null));
	List<Method> methods = new ArrayList<>();
	ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);
	if (methods.size() > 1) {
		methods.sort(adviceMethodComparator);
	}
	return methods;
}

這個方法主要幹兩件事:

  • 遍歷所有直接在類中聲明的、不帶有 @Pointcut 註解的切點方法,調用 getAdvisor 方法將其適配爲通知器;
  • 遍歷所有直接在切面聲明的,帶有 @DeclareParents 註解的接口引入屬性,將其適配爲 DeclareParentsAdvisor

三、適配切點方法

我們關注一下 getAdvisor 方法,我們熟悉的那些切點方法,比如 @Around, @Before, @After, @AfterReturning, @AfterThrowing 都在這裏解析爲通知器:

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

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
	// 獲取方法上的 AspectJ 註解,比如 @Around, @Before, @After, @AfterReturning, @AfterThrowing
	AspectJAnnotation<?> aspectJAnnotation =
			AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
	if (aspectJAnnotation == null) {
		return null;
	}

	AspectJExpressionPointcut ajexp =
			new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
	ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
	if (this.beanFactory != null) {
		ajexp.setBeanFactory(this.beanFactory);
	}
	return ajexp;
}

這裏主要是兩步邏輯:

  • 獲取切點,即 getPointcut 方法,裏面的邏輯就不細究了,大體就是創建一個 AspectJExpressionPointcut ,它支持根據 AspectJ 的各種配置去匹配類型或者方法;
  • 創建切面,即創建一個 InstantiationModelAwarePointcutAdvisorImpl ,它就是最終的 AspectJ 通知器;

當調用 InstantiationModelAwarePointcutAdvisorImpl  的 getAdvice 方法的時候,會根據傳入的 AspectJAspectFactory (一般是 ReflectiveAspectJAdvisorFactory )去基於切點方法創建具體的 MethodMethodInterceptor

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

	// 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 AtPointcut -> {
			if (logger.isDebugEnabled()) {
				logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
			}
			return null;
		}
		case AtAround -> springAdvice = new AspectJAroundAdvice(
				candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		case AtBefore -> springAdvice = new AspectJMethodBeforeAdvice(
				candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		case AtAfter -> springAdvice = new AspectJAfterAdvice(
				candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		case AtAfterReturning -> {
			springAdvice = new AspectJAfterReturningAdvice(
					candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
			if (StringUtils.hasText(afterReturningAnnotation.returning())) {
				springAdvice.setReturningName(afterReturningAnnotation.returning());
			}
		}
		case AtAfterThrowing -> {
			springAdvice = new AspectJAfterThrowingAdvice(
					candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
			AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
			if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
				springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
			}
		}
		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;
}

這個方法具體的實現不必深究,我們知道它是怎麼回事即可。

四、適配接口引入

上面我們提到了 @DeclareParents 註解,它是用來做接口引入的。其實這是個很強大但是也挺冷門的功能。
首先,在上文我們會注意到,Advisor 還有一個分支,叫做 IntroductionAdvisorAspectJ 和 Spring 都支持接口引入(Introduction),也就是讓一個類憑空實現額外的接口,從而具備這些可調用的方法,和 kotlin 的擴展函數有點像。
舉個例子:

@Aspect
public class GreetingAspect {

		// 使 com.example.Person 這個類實現 Foo 接口,並將方法委託到當前屬性對應的 Foo 上
    @DeclareParents(value = "com.example.Person", defaultImpl = FooImpl.class)
    private Foo foo;
}

在上述例子中,我們爲 com.example.Person 這個類的代理類引入了 Foo  接口,並且會通過反射爲其創建一個 FooImpl 實例,當我們通過 Person 的代理對象調用 Foo 接口中的方法時,就會委託到 FooImpl 實例上。
不過由於在編譯期無法顯式的調用這個方法,且完全依賴 Spring 代理,因此這個功能的存在感並不高。

總結

用於支持 AspectJ 切面的 AnnotationAwareAspectJAutoProxyCreator 重寫了父類 findCandidateAdvisors 的方法,並通過 buildAspectJAdvisors 解析並獲取了所有基於 AspectJ 實現的通知器並將其添加進了通知器列表,在這個過程中:

  • 從 Spring 容器中獲得所有類上帶有 @Aspect 註解且類不由 AspectJ 編譯 bean,這些 bean 就是切面對象;
  • 爲這些類創建對應的切面工廠 MetadataAwareAspectInstanceFactory ,這些工廠將用於創建對應的 AspectJ 通知器;
  • 當通過切面工廠的 getAdvisors 方法獲得 AspectJ 通知器時,將會:
    1. 遍歷所有直接在切面類中聲明的、不帶有 @Pointcut 註解的切點方法,調用 getAdvisor 方法將其適配爲通知器:
      1. @Around, @Before, @After, @AfterReturning, @AfterThrowing 註解將會被解析爲相應的 AspectJ 表達式切點  AspectJExpressionPointcut
      2. 獲得切點後,將會創建一個 AspectJ 通知器 InstantiationModelAwarePointcutAdvisorImpl
    2. 遍歷所有直接在切面聲明的,帶有 @DeclareParents 註解的接口引入屬性,將其適配爲 DeclareParentsAdvisor

其中,當我們通過 AspectJ 通知器 InstantiationModelAwarePointcutAdvisorImplgetAdvice 獲得 Advice 時,將會:

  • 通過內部持有的 AspectJAspectFactory (一般爲 ReflectiveAspectJAdvisorFactory )創建方法攔截器 MethodMethodInterceptor
  • 不同類型的 AspectJ 增強方法將會被解析爲對應的 AbstractAspectJAdvice 實現,比如 AspectJAroundAdviceAspectJAfterAdvice 等等;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章