深入理解 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 等等;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章