深入理解 SpringAOP(三):AspectJ支持
概述#
在之前的文章中,我们已经对 SpringAOP 的运行机制有了清晰的了解。然而,在本文中我们将补充关于 AspectJ
切面的内容。
尽管我们可以使用 AspectJ
的注解来定义切面逻辑,但实际上它们的实现仍然基于 Advisor
和方法拦截器。我们可以在最常用的 AutoProxyCreator
,也就是 AnnotationAwareAspectJAutoProxyCreator
中找到相关证据。
AnnotationAwareAspectJAutoProxyCreator
是 AutoProxyCreator
的最底层实现类。与另一个底层实现类 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 就是切面对象; - 检查切面类是否是单例的:
- 如果是切面类是单例的,那么就创建切面/通知器工厂
MetadataAwareAspectInstanceFactory
,根据其生成一批通知器。假如要代理的bean
,那就直接将通知器们注册到缓存,否则就直接缓存该工厂; - 如果切面类不是单例的,要代理的
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
还有一个分支,叫做 IntroductionAdvisor
, AspectJ
和 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 通知器时,将会:- 遍历所有直接在切面类中声明的、不带有
@Pointcut
注解的切点方法,调用getAdvisor
方法将其适配为通知器:@Around
,@Before
,@After
,@AfterReturning
,@AfterThrowing
注解将会被解析为相应的 AspectJ 表达式切点AspectJExpressionPointcut
;- 获得切点后,将会创建一个 AspectJ 通知器
InstantiationModelAwarePointcutAdvisorImpl
;
- 遍历所有直接在切面声明的,带有
@DeclareParents
注解的接口引入属性,将其适配为DeclareParentsAdvisor
;
- 遍历所有直接在切面类中声明的、不带有
其中,当我们通过 AspectJ 通知器 InstantiationModelAwarePointcutAdvisorImpl
的 getAdvice
获得 Advice
时,将会:
- 通过内部持有的
AspectJAspectFactory
(一般为ReflectiveAspectJAdvisorFactory
)创建方法拦截器MethodMethodInterceptor
; - 不同类型的 AspectJ 增强方法将会被解析为对应的
AbstractAspectJAdvice
实现,比如AspectJAroundAdvice
或AspectJAfterAdvice
等等;
作者:Createsequence
出处:https://www.cnblogs.com/Createsequence/p/18086413
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?