news 2026/10/2 1:43:59

Spring AOP 源码解析:AspectJAdvisorFactory 接口如何将注解切面转换为 Advisor

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AOP 源码解析:AspectJAdvisorFactory 接口如何将注解切面转换为 Advisor
  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载

AspectJAdvisorFactory是 Spring AOP 中连接 AspectJ 注解风格与 Spring AOP 代理机制的关键桥梁:它负责把使用@Aspect、@Before、@After等注解标记的切面类解析为 Spring AOP 可以消费的Advisor对象。本文以 spring-reading 仓库中 spring-aop-aspectJAdvisorFactory 模块为核心,结合仓库源码与可运行示例,完整讲解该接口的方法契约、核心实现类ReflectiveAspectJAdvisorFactory的工作流程,以及从注解方法到通知(Advice)与切点(Pointcut)的完整转换链路,帮助你深入理解 Spring AOP 注解驱动的底层原理。

一、AspectJAdvisorFactory 是什么

AspectJAdvisorFactory是 Spring AOP 中负责将 AspectJ 注解标记的切面类转换为Advisor对象的关键接口。实现类解析注解并生成Advisor,使得 Spring AOP 能够与 AspectJ 注解风格结合,提供灵活的面向切面编程能力。

在日常开发中,我们通常只关心@Aspect切面类里的@Before、@After等注解,却很少追问这些注解是如何变成 Spring AOP 真正使用的Advisor的。答案正是AspectJAdvisorFactory:它在@Aspect注解类(静态维度)与 Spring AOP 运行时通知器(动态维度)之间扮演翻译官的角色。

二、核心功能总览

AspectJAdvisorFactory围绕 AspectJ 注解切面提供四项核心能力:

  1. 解析 AspectJ 注解:实现类负责解析@Aspect、@Before、@After、@Around、@AfterReturning、@AfterThrowing、@Pointcut等注解,以及切点表达式等相关内容。

  2. 创建 Advisor 对象:根据解析得到的 AspectJ 注解信息,实现类生成对应的Advisor对象,其中包含切面的通知(Advice)和切入点(Pointcut)。

  3. 注册 Advisor 对象:生成的Advisor对象可以被注册到 Spring AOP 框架中,以便在运行时实现面向切面编程的功能。

  4. 支持与 AspectJ 注解风格的结合:通过AspectJAdvisorFactory,Spring AOP 可以与 AspectJ 注解风格结合使用,为开发者提供更为灵活和方便的 AOP 编程方式。

三、接口契约:五个方法逐一看

AspectJAdvisorFactory接口用于创建 Spring AOP Advisors,其中 Advisors 是根据 AspectJ 注解语法标记的类来生成的。该接口包含了判断类是否为切面、验证切面类的有效性、构建切面实例的 Advisors,以及为给定的 AspectJ advice 方法构建 Spring AOP Advisor 和 Advice 的方法。接口源码如下:

/** * 用于从用 AspectJ 注解语法注释的类中创建 Spring AOP Advisor 的工厂接口。 * * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 * @see AspectMetadata * @see org.aspectj.lang.reflect.AjTypeSystem */ public interface AspectJAdvisorFactory { /** * 确定给定的类是否是一个切面,由 AspectJ 的 {@link org.aspectj.lang.reflect.AjTypeSystem} 报告。 * <p>如果所谓的切面无效(例如扩展了具体切面类),则简单地返回 {@code false}。 * 对于一些 Spring AOP 无法处理的切面,例如具有不受支持的实例化模型,将返回 true。 * 如果需要处理这些情况,请使用 {@link #validate} 方法。 * @param clazz 所谓的注解式 AspectJ 类 * @return 此类是否被 AspectJ 识别为切面类 */ boolean isAspect(Class<?> clazz); /** * 给定的类是否是有效的 AspectJ 切面类? * @param aspectClass 要验证的所谓的 AspectJ 注解式类 * @throws AopConfigException 如果类是无效的切面(永远不合法) * @throws NotAnAtAspectException 如果类根本不是一个切面(根据上下文的不同可能合法也可能不合法) */ void validate(Class<?> aspectClass) throws AopConfigException; /** * 为指定的切面实例上的所有带有注解的 At-AspectJ 方法构建 Spring AOP Advisors。 * @param aspectInstanceFactory 切面实例工厂 * (而不是切面实例本身,以避免过早实例化) * @return 此类的一组 advisors */ List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory); /** * 为给定的 AspectJ advice 方法构建 Spring AOP Advisor。 * @param candidateAdviceMethod 候选的 advice 方法 * @param aspectInstanceFactory 切面实例工厂 * @param declarationOrder 在切面内的声明顺序 * @param aspectName 切面的名称 * @return 如果方法不是 AspectJ advice 方法,或者是将被其他 advice 使用但不会单独创建 Spring advice 的切入点,则返回 {@code null} */ @Nullable Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName); /** * 为给定的 AspectJ advice 方法构建 Spring AOP Advice。 * @param candidateAdviceMethod 候选的 advice 方法 * @param expressionPointcut AspectJ 表达式切入点 * @param aspectInstanceFactory 切面实例工厂 * @param declarationOrder 在切面内的声明顺序 * @param aspectName 切面的名称 * @return 如果方法不是 AspectJ advice 方法,或者是将被其他 advice 使用但不会单独创建 Spring advice 的切入点,则返回 {@code null} * @see org.springframework.aop.aspectj.AspectJAroundAdvice * @see org.springframework.aop.aspectj.AspectJMethodBeforeAdvice * @see org.springframework.aop.aspectj.AspectJAfterAdvice * @see org.springframework.aop.aspectj.AspectJAfterReturningAdvice * @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice */ @Nullable Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName); }

接口各方法职责梳理:

方法入参要点返回值语义典型使用场景
isAspect(Class)注解式 AspectJ 类布尔值,是否被 AspectJ 识别为切面BeanFactoryAspectJAdvisorsBuilder扫描候选 Bean 时做初筛
validate(Class)待验证的切面类无返回值,非法切面抛出异常getAdvisors/getAdvisor/getAdvice内部统一校验
getAdvisors(factory)切面实例工厂一组Advisor,涵盖全部通知方法与@DeclareParents引入一次性构建某个切面的全部通知器
getAdvisor(method, factory, order, name)单个候选通知方法单个Advisor或null逐方法构建通知器
getAdvice(method, pointcut, factory, order, name)通知方法 + 表达式切点对应通知实例或null(如@Pointcut方法)将 AspectJ 注解翻译成 Spring AOP Advice 实现类

注意getAdvisor与getAdvice均允许返回null:当候选方法根本不是通知方法,或是仅被其他 advice 引用、无需单独创建 Spring advice 的切入点(如@Pointcut标记的方法)时,返回null表示跳过。

四、主要实现:ReflectiveAspectJAdvisorFactory

AspectJAdvisorFactory接口的主要实现类是ReflectiveAspectJAdvisorFactory:它利用反射机制解析 AspectJ 注解,并创建相应的Advisor对象,支持注解风格的 AspectJ 切面,为我们提供了灵活而强大的面向切面编程能力。

从类关系上看,ReflectiveAspectJAdvisorFactory继承自抽象基类AbstractAspectJAdvisorFactory,后者直接实现了接口AspectJAdvisorFactory,并在基类中提供了isAspect、findAspectJAnnotationOnMethod、getPointcut等共享能力(详见仓库 spring-aop-aspectJAdvisorFactory/README.md 中的类关系图):

五、最佳实践:直接驱动工厂产出 Advisor

仓库 spring-aop-aspectJAdvisorFactory 提供了一个不依赖完整 Spring 容器、直接驱动ReflectiveAspectJAdvisorFactory的示例,便于观察工厂最纯粹的产出结果。

使用AspectJAdvisorFactory实现类ReflectiveAspectJAdvisorFactory以创建 Advisors 并打印它们:首先通过DefaultListableBeanFactory创建默认的 Bean 工厂,并在其中注册名为"myAspect"的单例 Bean,类型为MyAspect;然后创建MetadataAwareAspectInstanceFactory实例factory用于实例化切面;接着创建ReflectiveAspectJAdvisorFactory实例,并用它获取所有注解式 AspectJ 方法的 Advisors;最后遍历打印这些 Advisors。

public class AspectJAdvisorFactoryDemo { public static void main(String[] args) { // 创建一个默认的 Bean 工厂 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 在 Bean 工厂中注册一个名为 "myAspect" 的单例 Bean,类型为 MyAspect beanFactory.registerSingleton("myAspect", new MyAspect()); // 创建一个 Aspect 实例工厂,用于实例化切面 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(beanFactory, "myAspect"); // 创建 ReflectiveAspectJAdvisorFactory 实例,用于创建 Advisor ReflectiveAspectJAdvisorFactory aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory); // 获取所有注解式 AspectJ 方法的 Advisors List<Advisor> advisors = aspectJAdvisorFactory.getAdvisors(factory); // 打印 Advisors advisors.forEach(System.out::println); } }

示例中的切面类使用@Aspect注解标记,包含两个通知方法before()与after(),分别使用@Before和@After注解标记,在目标方法com.xcs.spring.MyService.doSomething()执行之前和之后输出日志:

@Aspect class MyAspect { @Before("execution(* com.xcs.spring.MyService.doSomething(..))") public void before() { System.out.println("Before executing the method..." ); } @After("execution(* com.xcs.spring.MyService.doSomething(..))") public void after() { System.out.println("After executing the method..." ); } }

目标服务类MyService只包含一个打印日志的doSomething()方法,用于演示 AOP 中如何应用切面逻辑:

public class MyService { public void doSomething() { System.out.println("Doing something..."); } }

运行结果输出了两个Advisor对象的信息,分别对应切面类MyAspect中的before()和after()方法,并针对相同的切点表达式execution(* com.xcs.spring.MyService.doSomething(..)):

InstantiationModelAwarePointcutAdvisor: expression [execution(* com.xcs.spring.MyService.doSomething(..))]; advice method [public void com.xcs.spring.MyAspect.before()]; perClauseKind=SINGLETON InstantiationModelAwarePointcutAdvisor: expression [execution(* com.xcs.spring.MyService.doSomething(..))]; advice method [public void com.xcs.spring.MyAspect.after()]; perClauseKind=SINGLETON

说明:仓库源码(AspectJAdvisorFactoryDemo.java)中的切面目标方法实际写作MyService.foo(..),示例代码与运行结果之间仅方法名不同,执行原理完全一致。运行前请确认模块的pom.xml(spring-aop-aspectJAdvisorFactory/pom.xml)已引入父模块中声明的 Spring AOP 与 AspectJ 依赖。

两个输出行的关键信息解读:

  • InstantiationModelAwarePointcutAdvisor:getAdvisor返回的实际 Advisor 类型,它同时实现了PointcutAdvisor与InstantiationModelAwarePointcutAdvisor,既能提供切点与通知,又能感知切面的实例化模型;
  • expression [...]:由@Before/@After注解中的切点表达式解析而来;
  • advice method [...]:切面类中的原始通知方法;
  • perClauseKind=SINGLETON:切面的实例化模型为单例(对应 AspectJ 的默认perthis之外的单例切面语义)。

六、源码分析:注解切面到 Advisor 的完整转换链路

6.1 入口:getAdvisors 组织切面内所有通知

在org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors方法中,根据给定的切面实例工厂获取切面类中的通知器列表。流程如下:先验证切面类的有效性,再用元数据判断是否需要延迟实例化切面实例工厂;随后遍历切面类中的方法获取通知器并加入列表;若切面是面向目标的且为延迟实例化,则添加一个虚拟实例化通知器;最后查找切面类中的引入字段(@DeclareParents),获取相应通知器并加入列表,最终返回该列表。

/** * 获取通知器列表。 * @param aspectInstanceFactory 切面实例工厂 * @return 通知器列表 */ @Override public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 获取切面类和切面名称 Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); // 验证切面类的有效性 validate(aspectClass); // 将MetadataAwareAspectInstanceFactory包装成装饰器,以保证只实例化一次 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); // 创建通知器列表 List<Advisor> advisors = new ArrayList<>(); // 遍历切面类中的方法,获取通知器 for (Method method : getAdvisorMethods(aspectClass)) { // 由于JDK不再以源代码中的声明顺序返回方法,因此固定declarationOrderInAspect为0以支持跨JVM启动的可靠通知顺序 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { advisors.add(advisor); } } // 如果是针对目标的切面,则添加一个虚拟实例化通知器 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 查找切面类中的引入字段 for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }

几个值得注意的实现细节:

  • 延迟包装:LazySingletonAspectInstanceFactoryDecorator确保切面实例只被实例化一次,同时避免在构建 Advisor 阶段就过早创建切面 Bean;
  • 声明顺序固定为 0:JDK 反射不再保证方法按源码声明顺序返回,固定declarationOrderInAspect为 0 是为了跨 JVM 启动获得可靠的通知执行顺序;
  • 虚拟实例化通知器:仅针对perthis/pertarget这类延迟实例化切面,在列表头部插入SyntheticInstantiationAdvisor,保证目标首次命中切点时先实例化切面;
  • @DeclareParents 引入:通过扫描切面类中带有@DeclareParents注解的字段,生成引入型 Advisor,实现为已有 Bean 动态增加接口实现。

6.2 候选方法收集:getAdvisorMethods 与 adviceMethodFilter

在org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorMethods方法中,获取切面类中作为通知的方法列表:通过反射遍历切面类的方法,用adviceMethodFilter过滤出通知方法,再按方法数量排序后返回。

/** * 获取切面类中用作通知的方法列表。 * * @param aspectClass 切面类 * @return 切面类中的通知方法列表 */ private List<Method> getAdvisorMethods(Class<?> aspectClass) { // 创建一个空的方法列表 List<Method> methods = new ArrayList<>(); // 使用 ReflectionUtils 遍历切面类中的方法,将符合条件的方法添加到方法列表中 ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter); // 如果方法数量大于1,即存在多个通知方法,则按照指定的比较器对方法列表进行排序 if (methods.size() > 1) { methods.sort(adviceMethodComparator); } // 返回方法列表 return methods; }

adviceMethodFilter用于筛选切面类中的通知方法:通过ReflectionUtils.USER_DECLARED_METHODS筛出用户自定义方法(排除equals、hashCode等 Object 方法与合成方法),并排除带有@Pointcut注解的方法(它们不产生独立 Advisor)。

private static final MethodFilter adviceMethodFilter = ReflectionUtils.USER_DECLARED_METHODS .and(method -> (AnnotationUtils.getAnnotation(method, Pointcut.class) == null));

6.3 通知排序:adviceMethodComparator 与 @After 的"反直觉"顺序

adviceMethodComparator字段按照通知的类型依次排列:Around、Before、After、AfterReturning、AfterThrowing。其中有一个容易误解的特殊情况:尽管@After在排序上位于@AfterReturning和@AfterThrowing之前,但实际上@After方法会在@AfterReturning和@AfterThrowing方法之后被调用。这是因为AspectJAfterAdvice.invoke(MethodInvocation)方法在try块中调用了proceed()方法,并且只有在相应的finally块中才调用@After方法——换言之,@After语义是"最终通知"(无论正常返回还是抛出异常都会执行),因此它排在被 try 包裹的后置/异常通知之后。

private static final Comparator<Method> adviceMethodComparator; static { // 注意:尽管 @After 排在 @AfterReturning 和 @AfterThrowing 之前, // 但实际上 @After 通知方法会在 @AfterReturning 和 @AfterThrowing 方法之后被调用, // 这是因为 AspectJAfterAdvice.invoke(MethodInvocation) 方法在 `try` 块中调用了 proceed() 方法, // 并且只有在相应的 `finally` 块中才调用了 @After 方法。 // 定义一个方法比较器,按照通知的类型依次排列:Around、Before、After、AfterReturning、AfterThrowing。 Comparator<Method> adviceKindComparator = new ConvertingComparator<>( new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), (Converter<Method, Annotation>) method -> { // 使用 AspectJAnnotation 查找方法上的 AspectJ 注解 AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); return (ann != null ? ann.getAnnotation() : null); }); // 定义一个方法名比较器 Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName); // 将两个比较器按顺序组合,首先按照通知类型排序,然后按照方法名称排序 adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator); }

排序策略可总结为:

  1. 先按通知注解类型排序:@Around→@Before→@After→@AfterReturning→@AfterThrowing;
  2. 同一类型下再按方法名字典序排序;
  3. 最终执行顺序由各 Advice 的invoke实现(尤其是AspectJAfterAdvice的 try/finally 结构)决定,而非简单地依赖排序结果。

6.4 单个 Advisor 构建:getAdvisor 与 InstantiationModelAwarePointcutAdvisorImpl

在org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisor方法中,根据候选通知方法获取Advisor:先验证 Aspect 类有效性,再获取切点表达式;若切点表达式为空则返回null,否则创建InstantiationModelAwarePointcutAdvisorImpl包装切点表达式、通知方法等信息。

/** * 根据候选的通知方法获取 Advisor 对象。 * * @param candidateAdviceMethod 候选的通知方法 * @param aspectInstanceFactory Aspect 实例工厂,用于创建 Aspect 实例 * @param declarationOrderInAspect 在 Aspect 中的声明顺序 * @param aspectName Aspect 的名称 * @return Advisor 对象,如果候选方法不是有效的切点,则返回 null */ @Override @Nullable public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { // 验证 Aspect 类的有效性 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 获取切点表达式 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 如果切点表达式为空,则返回 null if (expressionPointcut == null) { return null; } // 创建 InstantiationModelAwarePointcutAdvisorImpl 对象,用于管理切点表达式、通知方法等信息 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }

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(); // 设置 AspectJ 的通知方法 this.aspectJAdviceMethod = aspectJAdviceMethod; // 设置 AspectJ 的 AdvisorFactory this.aspectJAdvisorFactory = aspectJAdvisorFactory; // 设置 Aspect 实例工厂 this.aspectInstanceFactory = aspectInstanceFactory; // 设置声明顺序 this.declarationOrder = declarationOrder; // 设置切面名称 this.aspectName = aspectName; // 如果切面是延迟实例化的 if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // 切点的静态部分是一个延迟类型 Pointcut preInstantiationPointcut = Pointcuts.union( aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); // 使之动态:必须从预实例化状态变为后实例化状态 // 如果它不是动态切点,则可能会在第一次评估后被 Spring AOP 基础设施优化掉 this.pointcut = new PerTargetInstantiationModelPointcut( this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); // 设置为延迟 this.lazy = true; } else { // 单例切面 this.pointcut = this.declaredPointcut; this.lazy = false; // 实例化通知 this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); } }

理解这一构造逻辑的关键点:

  • 单例切面(lazy=false):直接使用声明的表达式切点,并在构造期就通过instantiateAdvice完成通知实例化,运行时直接使用;
  • 延迟实例化切面(lazy=true):用Pointcuts.union把 per-clause 切点与声明切点联合,再包装为PerTargetInstantiationModelPointcut动态切点,保证切面实例化前后切点状态正确转换。源码注释明确指出:如果不将其变成动态切点,可能会在第一次评估后被 Spring AOP 基础设施优化掉(例如静态切点缓存导致的提前实例化失效)。

instantiateAdvice方法使用AspectJAdvisorFactory获取通知实例,若为空则返回空通知EMPTY_ADVICE:

/** * 根据给定的切点实例化通知。 * * @param pointcut 切点表达式 * @return 实例化的通知 */ private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) { // 使用AspectJAdvisorFactory获取通知实例 Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); // 如果获取的通知实例为空,则返回空通知 return (advice != null ? advice : EMPTY_ADVICE); }

6.5 通知翻译:getAdvice 按注解类型实例化 Advice

在org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice方法中,根据给定的候选通知方法、切点表达式、切面实例工厂等信息获取对应的 Spring AOP 通知实例:先验证切面类的有效性,再根据候选通知方法的 AspectJ 注解类型,实例化相应的 Spring AOP 通知(环绕、前置、后置等),并配置相关属性,最后返回。

@Override @Nullable public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { // 获取候选通知方法所在的切面类 Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 验证切面类的有效性 validate(candidateAspectClass); // 获取候选通知方法的 AspectJ 注解 AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); // 如果候选通知方法没有 AspectJ 注解,则返回 null if (aspectJAnnotation == null) { return null; } // 判断切面类是否为 AspectJ 注解的类 if (!isAspect(candidateAspectClass)) { // 如果不是,则抛出异常 throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } // 如果日志级别为 DEBUG,则打印找到的 AspectJ 方法信息 if (logger.isDebugEnabled()) { logger.debug("Found AspectJ method: " + candidateAdviceMethod); } // 声明 Spring AOP 通知实例 AbstractAspectJAdvice springAdvice; // 根据注解类型实例化相应的 Spring AOP 通知 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); } // 配置通知的相关属性 springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); // 获取通知方法的参数名数组 String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); // 如果参数名数组不为空,则设置到通知中 if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } // 计算参数绑定 springAdvice.calculateArgumentBindings(); return springAdvice; }

从源码可以确认 AspectJ 注解到 Spring AOP Advice 实现的一一映射关系:

AspectJ 注解生成的 Spring AOP Advice 实现类说明
@Pointcut返回null仅作为切点声明,不生成独立通知
@AroundAspectJAroundAdvice环绕通知,可控制目标方法调用
@BeforeAspectJMethodBeforeAdvice前置通知
@AfterAspectJAfterAdvice最终通知,基于 finally 语义实现
@AfterReturningAspectJAfterReturningAdvice返回后通知,并解析returning属性绑定返回值参数
@AfterThrowingAspectJAfterThrowingAdvice异常后通知,并解析throwing属性绑定异常参数

方法末尾还完成三项重要的"配置收尾"工作:

  1. 设置aspectName与declarationOrder,保证多切面、多通知间的排序可追溯;
  2. 通过parameterNameDiscoverer获取通知方法的参数名,并调用setArgumentNamesFromStringArray设置;
  3. 调用calculateArgumentBindings()计算参数绑定(将JoinPoint、returning返回值、throwing异常等与通知方法参数一一对应)。

七、AspectJAdvisorFactory 在 Spring AOP 整体流程中的位置

单独使用AspectJAdvisorFactory的场景较少,它更多是被上层的BeanFactoryAspectJAdvisorsBuilder在自动代理流程中驱动。仓库 spring-aop-beanFactoryAspectJAdvisorsBuilder 给出了这一上下游关系的完整示例:配置类通过@EnableAspectJAutoProxy开启 AspectJ 自动代理,配合@ComponentScan扫描@Aspect切面 Bean(参见 AppConfig.java)。

从BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors的源码可以看出其调用链:遍历 Bean 工厂中所有 Bean 名称 → 用advisorFactory.isAspect(beanType)初筛切面 → 按切面的实例化模型(单例/多例)分别创建BeanFactoryAspectInstanceFactory或PrototypeAspectInstanceFactory→ 调用advisorFactory.getAdvisors(factory)获取 Advisors,并缓存单例切面的 Advisors 以提升性能。这里advisorFactory的类型正是AspectJAdvisorFactory(默认实现即ReflectiveAspectJAdvisorFactory),而isAspect的判定逻辑在抽象基类中实现:类上带有@Aspect注解(hasAspectAnnotation通过AnnotationUtils.findAnnotation(clazz, Aspect.class)查找),且不是由 AspectJ 编译器(AJC)编译的(compiledByAjc排除)。

完整的运行时链路可以概括为:

AnnotationAwareAspectJAutoProxyCreator │ 查找候选 Advisors ▼ BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() │ 调用 advisorFactory.isAspect / getAdvisors ▼ ReflectiveAspectJAdvisorFactory │ getAdvisorMethods → getAdvisor → InstantiationModelAwarePointcutAdvisorImpl │ └─ instantiateAdvice → getAdvice ▼ AspectJAroundAdvice / AspectJMethodBeforeAdvice / AspectJAfterAdvice / AspectJAfterReturningAdvice / AspectJAfterThrowingAdvice

这条链路解释了为什么我们只需要写@Aspect+@Before等注解,Spring 就能自动织入代理逻辑:AnnotationAwareAspectJAutoProxyCreator在 Bean 初始化后处理阶段发现候选 Advisors,最终由ReflectiveAspectJAdvisorFactory把注解方法翻译成携带切点表达式与通知逻辑的标准 Advisor,再由 AOP 基础设施据此生成代理。

八、小结

AspectJAdvisorFactory是 Spring AOP 注解驱动机制的核心工厂接口,本文围绕仓库 spring-aop-aspectJAdvisorFactory 模块梳理了以下要点:

  • 接口五个方法的职责分工,以及@Nullable返回值的语义(非通知方法或@Pointcut方法返回null);
  • 主要实现ReflectiveAspectJAdvisorFactory借助反射与AbstractAspectJAdvisorFactory的公共能力完成注解解析;
  • getAdvisors的整体编排:方法遍历、@DeclareParents引入、延迟实例化虚拟 Advisor 的插入;
  • 通知排序的"反直觉"细节:@After因 finally 语义在@AfterReturning/@AfterThrowing之后执行;
  • InstantiationModelAwarePointcutAdvisorImpl对单例与延迟实例化切面的差异化处理;
  • getAdvice中五种 AspectJ 注解与 Spring AOP Advice 实现类的精确映射及参数绑定配置。

如果想进一步验证工厂在实际自动代理流程中的作用,可以继续阅读仓库中的 spring-aop-beanFactoryAspectJAdvisorsBuilder、spring-aop-annotationAwareAspectJAutoProxyCreator 与 spring-aop-metadataAwareAspectInstanceFactory 模块,它们与本文主题同属一条完整的注解 AOP 链路。

  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载
上一篇:BevyError 上下文机制:用 context 与 with_context 为 Bevy 错误附加人类可读的追溯信息
下一篇:完美解决跨浏览器渲染差异:Maple Mono字体一致性指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 1:40:42

STM32CubeMX从入门到实战:配置、点灯、SPI Flash与FreeRTOS

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 1:40:09

EMC预测试:从电流路径建模到Layout级EMI扼杀

1. 为什么“预测试”不是加个探头测一测那么简单&#xff1f;EMC预测试这个词&#xff0c;现在被很多工程师挂在嘴边&#xff0c;但真正把它当成本职工作来做的团队&#xff0c;不到三成。我见过太多项目——原理图刚定稿&#xff0c;PCB还在画&#xff0c;大家就忙着讨论“等板…

作者头像 李华
网站建设 2026/10/2 1:38:51

编译原理课设实战:C++手写词法分析器与LL(1)语法分析器

简介&#xff1a;这是一份面向计算机专业学生与编译器爱好者的编译原理前端实践资源&#xff0c;聚焦词法分析与语法分析两大核心模块的C实现。资源包共9个文件&#xff0c;以cpp源码、txt文法与token说明、exe可执行程序及md说明文档为主&#xff0c;压缩包约937KB&#xff0c…

作者头像 李华
网站建设 2026/10/2 1:38:14

Linux 命令详解:mktemp 安全创建临时文件(linux-command 速查手册)

文档教程 【免费下载链接】linux-command Linux命令大全搜索工具&#xff0c;内容包含Linux命令手册、详解、学习、搜集。https://git.io/linux 项目地址&#xff1a; https://gitcode.com/GitHub_Trending/linux/linux-command 点击查看 免费下载 本篇以 command/mktemp.md 为…

作者头像 李华
网站建设 2026/10/2 1:38:09

PL0编译器功能扩充:重建教学型编译器的可扩展骨架

简介&#xff1a;本资源是一份面向计算机专业高年级学生与编译原理初学者的PL/0编译器功能扩充实验报告&#xff0c;聚焦事业编考试中常涉及的系统底层与语言实现能力考查场景。文档完整呈现了在经典教学编译器PL/0基础上扩展整型一维数组、IF-THEN-ELSE条件分支及REPEAT循环语…

作者头像 李华