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

发布时间:2026/10/2 1:44:05
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 注解切面提供四项核心能力解析 AspectJ 注解实现类负责解析Aspect、Before、After、Around、AfterReturning、AfterThrowing、Pointcut等注解以及切点表达式等相关内容。创建 Advisor 对象根据解析得到的 AspectJ 注解信息实现类生成对应的Advisor对象其中包含切面的通知Advice和切入点Pointcut。注册 Advisor 对象生成的Advisor对象可以被注册到 Spring AOP 框架中以便在运行时实现面向切面编程的功能。支持与 AspectJ 注解风格的结合通过AspectJAdvisorFactorySpring 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 */ ListAdvisor 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表示跳过。四、主要实现ReflectiveAspectJAdvisorFactoryAspectJAdvisorFactory接口的主要实现类是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 ListAdvisor 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()]; perClauseKindSINGLETON InstantiationModelAwarePointcutAdvisor: expression [execution(* com.xcs.spring.MyService.doSomething(..))]; advice method [public void com.xcs.spring.MyAspect.after()]; perClauseKindSINGLETON说明仓库源码AspectJAdvisorFactoryDemo.java中的切面目标方法实际写作MyService.foo(..)示例代码与运行结果之间仅方法名不同执行原理完全一致。运行前请确认模块的pom.xmlspring-aop-aspectJAdvisorFactory/pom.xml已引入父模块中声明的 Spring AOP 与 AspectJ 依赖。两个输出行的关键信息解读InstantiationModelAwarePointcutAdvisorgetAdvisor返回的实际 Advisor 类型它同时实现了PointcutAdvisor与InstantiationModelAwarePointcutAdvisor既能提供切点与通知又能感知切面的实例化模型expression [...]由Before/After注解中的切点表达式解析而来advice method [...]切面类中的原始通知方法perClauseKindSINGLETON切面的实例化模型为单例对应 AspectJ 的默认perthis之外的单例切面语义。六、源码分析注解切面到 Advisor 的完整转换链路6.1 入口getAdvisors 组织切面内所有通知在org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors方法中根据给定的切面实例工厂获取切面类中的通知器列表。流程如下先验证切面类的有效性再用元数据判断是否需要延迟实例化切面实例工厂随后遍历切面类中的方法获取通知器并加入列表若切面是面向目标的且为延迟实例化则添加一个虚拟实例化通知器最后查找切面类中的引入字段DeclareParents获取相应通知器并加入列表最终返回该列表。/** * 获取通知器列表。 * param aspectInstanceFactory 切面实例工厂 * return 通知器列表 */ Override public ListAdvisor getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 获取切面类和切面名称 Class? aspectClass aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName aspectInstanceFactory.getAspectMetadata().getAspectName(); // 验证切面类的有效性 validate(aspectClass); // 将MetadataAwareAspectInstanceFactory包装成装饰器以保证只实例化一次 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); // 创建通知器列表 ListAdvisor 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声明顺序固定为 0JDK 反射不再保证方法按源码声明顺序返回固定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 ListMethod getAdvisorMethods(Class? aspectClass) { // 创建一个空的方法列表 ListMethod 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 ComparatorMethod adviceMethodComparator; static { // 注意尽管 After 排在 AfterReturning 和 AfterThrowing 之前 // 但实际上 After 通知方法会在 AfterReturning 和 AfterThrowing 方法之后被调用 // 这是因为 AspectJAfterAdvice.invoke(MethodInvocation) 方法在 try 块中调用了 proceed() 方法 // 并且只有在相应的 finally 块中才调用了 After 方法。 // 定义一个方法比较器按照通知的类型依次排列Around、Before、After、AfterReturning、AfterThrowing。 ComparatorMethod adviceKindComparator new ConvertingComparator( new InstanceComparator( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), (ConverterMethod, Annotation) method - { // 使用 AspectJAnnotation 查找方法上的 AspectJ 注解 AspectJAnnotation? ann AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); return (ann ! null ? ann.getAnnotation() : null); }); // 定义一个方法名比较器 ComparatorMethod methodNameComparator new ConvertingComparator(Method::getName); // 将两个比较器按顺序组合首先按照通知类型排序然后按照方法名称排序 adviceMethodComparator adviceKindComparator.thenComparing(methodNameComparator); }排序策略可总结为先按通知注解类型排序Around→Before→After→AfterReturning→AfterThrowing同一类型下再按方法名字典序排序最终执行顺序由各 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); } }理解这一构造逻辑的关键点单例切面lazyfalse直接使用声明的表达式切点并在构造期就通过instantiateAdvice完成通知实例化运行时直接使用延迟实例化切面lazytrue用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属性绑定异常参数方法末尾还完成三项重要的配置收尾工作设置aspectName与declarationOrder保证多切面、多通知间的排序可追溯通过parameterNameDiscoverer获取通知方法的参数名并调用setArgumentNamesFromStringArray设置调用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这条链路解释了为什么我们只需要写AspectBefore等注解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点击查看免费下载相关推荐Spring AOP 源码解析BeanFactoryAspectJAdvisorsBuilder 如何把 AspectJ 切面转换为 AdvisorSpring AOP 源码解析BeanFactoryAspectJAdvisorsBuilder 如何把 AspectJ 切面转换为 Advisor 本文以示例工程文档flue/twilioFlue 框架的 Twilio 消息通道接入指南flue/twilioFlue 框架的 Twilio 消息通道接入指南 flue/twilio 是 Flue 沙箱 Agent 框架sloganThe示例工程文档Spring 格式化体系源码解析Printer 接口如何将对象转换为字符串Spring 格式化体系源码解析Printer 接口如何将对象转换为字符串 本文围绕 Spring 框架 org.springframework.format文档教程知识库上一篇BevyError 上下文机制用 context 与 with_context 为 Bevy 错误附加人类可读的追溯信息下一篇完美解决跨浏览器渲染差异Maple Mono字体一致性指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询