新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring AOP 源码解析:BeanFactoryAspectJAdvisorsBuilder 如何把 AspectJ 切面转换为 Advisor

发布时间:2026/10/2 1:59:13来源:尧图网络
Spring AOP 源码解析:BeanFactoryAspectJAdvisorsBuilder 如何把 AspectJ 切面转换为 Advisor
示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载本文以 spring-reading 仓库中 spring-aop-beanFactoryAspectJAdvisorsBuilder 模块为核心深入剖析BeanFactoryAspectJAdvisorsBuilder在 Spring AOP 自动代理链路中的职责它负责扫描 Bean 工厂中所有带 AspectJ 注解如Aspect的切面类将其解析并转换为 Spring AOP 的Advisor列表是注解式 AOP 从切面定义到可执行代理逻辑的关键桥梁。读完本文你将掌握该类的核心方法buildAspectJAdvisors()的实现细节、单例/多例切面的差异化处理与缓存策略以及它和AnnotationAwareAspectJAutoProxyCreator、ReflectiveAspectJAdvisorFactory之间的完整协作调用链。一、基本信息作者Lexxuchengsheng所属模块spring-aop/spring-aop-beanFactoryAspectJAdvisorsBuilder是 spring-aop 聚合模块packaging 为pom的成员之一模块坐标com.xcs.spring:spring-aop-beanFactoryAspectJAdvisorsBuilder其父模块为spring-aopJava 编译版本 11详见 pom.xml运行示例入口BeanFactoryAspectJAdvisorsBuilderDemo.java二、基本描述BeanFactoryAspectJAdvisorsBuilder是 Spring AOP 中的一个重要类负责将应用中使用 AspectJ 注解标记的切面解析并转换为 Spring AOP 中的通知器Advisor从而实现基于注解的切面编程。它位于org.springframework.aop.aspectj.annotation包下是注解式 AOP 实现AnnotationAwareAspectJAutoProxyCreator底层的核心构建器。从职责划分上看Spring AOP 的自动代理体系中有两类 Advisor 来源来源负责类说明手动注册的 Advisor BeanBeanFactoryAdvisorRetrievalHelper从 Bean 工厂中检索所有Advisor类型的 BeanAspectJ 注解切面BeanFactoryAspectJAdvisorsBuilder扫描Aspect注解类动态解析出 Advisor本文的主角负责后者把用注解写的切面翻译成Spring AOP 可执行的 Advisor。三、主要功能BeanFactoryAspectJAdvisorsBuilder的核心能力可归纳为四个步骤扫描 AspectJ 注解扫描应用中的类查找带有 AspectJ 注解如Aspect的类识别并准备将它们转换成 Spring AOP 中的通知器。解析切面和通知一旦发现带 AspectJ 注解的类解析这些类找到其中定义的切面以及切面中的通知Advice方法。创建通知器Advisors基于解析得到的切面和通知信息创建对应的Advisor。通知器封装了切面逻辑以及连接点切入点信息它们将被应用到目标对象的方法调用中。注册通知器将创建的通知器交给上层AnnotationAwareAspectJAutoProxyCreator合并到候选 Advisor 列表后参与自动代理决策使切面逻辑在运行时生效。从源码结构看该类的核心协作对象有两个AdvisorFactory实际实现为ReflectiveAspectJAdvisorFactory负责真正解析切面类中的通知方法并生成 Advisor对应仓库中的 spring-aop-aspectJAdvisorFactory 模块MetadataAwareAspectInstanceFactory如BeanFactoryAspectInstanceFactory、PrototypeAspectInstanceFactory负责按切面的实例化模型提供切面实例对应仓库中的 spring-aop-metadataAwareAspectInstanceFactory 模块。四、最佳实践4.1 运行入口使用基于注解的应用上下文来获取并调用MyServiceBean 的foo()方法。首先创建一个AnnotationConfigApplicationContext实例通过传入AppConfig.class初始化基于注解的应用上下文然后通过context.getBean(MyService.class)获取MyServiceBean 的实例并调用其foo()方法。完整代码见 BeanFactoryAspectJAdvisorsBuilderDemo.java。public class BeanFactoryAspectJAdvisorsBuilderDemo { public static void main(String[] args) { // 创建基于注解的应用上下文 AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class); // 从应用上下文中获取MyService bean MyService myService context.getBean(MyService.class); // 调用MyService的方法 myService.foo(); } }4.2 配置类 AppConfigAppConfig是一个使用Configuration注解标记的配置类通过EnableAspectJAutoProxy开启 AspectJ 自动代理功能并通过ComponentScan启用组件扫描用于自动发现和注册 Spring 组件。源码见 AppConfig.java。Configuration EnableAspectJAutoProxy ComponentScan public class AppConfig { }关于EnableAspectJAutoProxy的底层机制可以参考仓库中的 spring-aop-enableAspectJAutoProxy 模块——它通过Import(AspectJAutoProxyRegistrar.class)注册AnnotationAwareAspectJAutoProxyCreator而正是这个后处理器在 Bean 初始化后调用buildAspectJAdvisors()。4.3 切面定义 MyAspect通过Aspect和Component注解将切面标记为 Spring 组件并定义了一个在com.xcs.spring.MyService类的foo方法执行前执行的前置通知Before advice。源码见 MyAspect.java。Aspect Component class MyAspect { Before(execution(* com.xcs.spring.MyService.foo(..))) public void before() { System.out.println(Before method execution); } }4.4 目标服务 MyServiceMyService是一个使用Service注解标记的服务类提供foo()方法调用时打印消息 foo...。源码见 MyService.java。Service public class MyService { public void foo() { System.out.println(foo...); } }4.5 运行结果调用MyService的foo()方法之前成功地执行了切面通知输出 Before method execution随后执行foo()方法输出 foo...Before method execution foo...五、时序图下面的时序图展示了从 Bean 初始化后处理到BeanFactoryAspectJAdvisorsBuilder构建 Advisors 的完整调用链。它揭示了该类在整个自动代理流程中的位置AbstractAutoProxyCreator在 Bean 初始化后触发postProcessAfterInitialization()经由wrapIfNecessary()→getAdvicesAndAdvisorsForBean()→findEligibleAdvisors()层层下探最后由AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()调用buildAspectJAdvisors()获取 AspectJ 注解切面生成的 Advisors。结合仓库中 spring-aop-annotationAwareAspectJAutoProxyCreator/README.md 的源码分析可以确认AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors()先调用父类的findCandidateAdvisors()经由BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()获取 Spring 手动注册的 Advisor再将aspectJAdvisorsBuilder.buildAspectJAdvisors()的结果合并进列表返回二者共同构成候选 Advisor 全集。六、源码分析6.1 核心方法 buildAspectJAdvisors()org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors主要负责在当前 Bean 工厂中查找使用 AspectJ 注解标记的切面 Bean并将其转换为 Spring AOP Advisors 的列表。它遍历所有 Bean 名称识别切面 Bean并根据其实例化模型单例或多例创建对应的 AspectJ Advisors处理过程中还会缓存单例切面的 Advisors 以提高性能。/** * 在当前 Bean 工厂中查找使用 AspectJ 注解标记的切面 Bean并返回表示它们的 Spring AOP Advisors 列表。 * p为每个 AspectJ 的通知方法创建一个 Spring Advisor。 * return 包含 {link org.springframework.aop.Advisor} beans 的列表 * see #isEligibleBean */ public ListAdvisor buildAspectJAdvisors() { // 如果切面 Bean 名称列表为空则进行查找 ListString aspectNames this.aspectBeanNames; if (aspectNames null) { synchronized (this) { aspectNames this.aspectBeanNames; if (aspectNames null) { // 初始化切面 Advisors 列表和切面 Bean 名称列表 ListAdvisor advisors new ArrayList(); aspectNames new ArrayList(); // 获取当前 Bean 工厂中的所有 Bean 名称 String[] beanNames BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); // 遍历所有 Bean 名称 for (String beanName : beanNames) { // 检查 Bean 是否符合条件 if (!isEligibleBean(beanName)) { continue; } // 获取 Bean 的类型 Class? beanType this.beanFactory.getType(beanName, false); // 如果无法获取类型则跳过 if (beanType null) { continue; } // 判断 Bean 是否是切面 if (this.advisorFactory.isAspect(beanType)) { // 将切面 Bean 名称加入列表 aspectNames.add(beanName); // 获取切面元数据 AspectMetadata amd new AspectMetadata(beanType, beanName); // 判断切面的实例化模型 if (amd.getAjType().getPerClause().getKind() PerClauseKind.SINGLETON) { // 单例模式 // 创建单例模式的切面实例工厂 MetadataAwareAspectInstanceFactory factory new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 获取切面的 Advisors ListAdvisor classAdvisors this.advisorFactory.getAdvisors(factory); // 缓存单例切面的 Advisors if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // 多例模式 if (this.beanFactory.isSingleton(beanName)) { // 如果切面实例化模型为多例但 Bean 是单例则抛出异常 throw new IllegalArgumentException(Bean with name beanName is a singleton, but aspect instantiation model is not singleton); } // 创建多例模式的切面实例工厂 MetadataAwareAspectInstanceFactory factory new PrototypeAspectInstanceFactory(this.beanFactory, beanName); // 缓存切面实例工厂 this.aspectFactoryCache.put(beanName, factory); // 获取切面的 Advisors advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } // 将切面 Bean 名称列表缓存起来 this.aspectBeanNames aspectNames; return advisors; } } } // 如果切面 Bean 名称列表为空则返回空列表 if (aspectNames.isEmpty()) { return Collections.emptyList(); } // 创建用于存储所有 Advisors 的列表 ListAdvisor advisors new ArrayList(); // 遍历切面 Bean 名称列表 for (String aspectName : aspectNames) { // 从缓存中获取 Advisors ListAdvisor cachedAdvisors this.advisorsCache.get(aspectName); if (cachedAdvisors ! null) { // 如果缓存中有 Advisors则加入到结果列表中 advisors.addAll(cachedAdvisors); } else { // 如果缓存中没有 Advisors则从切面实例工厂中获取 Advisors MetadataAwareAspectInstanceFactory factory this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }对上述代码的逻辑要点逐层拆解如下双重检查加锁DCL通过synchronized (this)保证首次扫描只执行一次之后所有调用都命中缓存的aspectBeanNames直接走缓存分支避免重复扫描 Bean 工厂。全量 Bean 扫描使用BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Object.class, true, false)获取当前工厂及祖先工厂中的所有 Bean 名称含尚未实例化的 Bean。候选过滤isEligibleBean(beanName)提供钩子方法默认实现允许所有 Bean子类可覆盖以做定向过滤如仅处理以指定前缀命名的 Bean。切面判定调用advisorFactory.isAspect(beanType)判断类是否带有Aspect注解。实例化模型分流单例SINGLETON切面使用BeanFactoryAspectInstanceFactory若 Bean 本身是单例则把解析出的 Advisor 列表整体放入advisorsCache按 Bean 名缓存否则放入aspectFactoryCache延迟到运行时再解析非单例切面如 perthis、pertarget要求 Bean 不能是单例否则抛出IllegalArgumentExceptionBean with name xxx is a singleton, but aspect instantiation model is not singleton并使用PrototypeAspectInstanceFactory每次从工厂获取新实例以保证实例化模型语义。缓存与二次获取首次扫描完成后记录aspectBeanNames后续调用直接遍历该列表优先从advisorsCache取结果未命中则经aspectFactoryCache中的工厂重新解析保证每个切面的 Advisor 只被完整解析一次。6.2 切面判定isAspect 与 hasAspectAnnotationorg.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect判断给定类是否为切面首先检查类是否带 AspectJ 注解再确认该类不是由 AspectJ 编译器ajc编译的。只有两者都满足才返回true。/** * 判断给定的类是否是切面。 * param clazz 要检查的类 * return 如果类是切面则返回 true否则返回 false */ Override public boolean isAspect(Class? clazz) { // 判断类是否带有 AspectJ 注解并且不是由 AspectJ 编译器编译的 return (hasAspectAnnotation(clazz) !compiledByAjc(clazz)); }AbstractAspectJAdvisorFactory#hasAspectAnnotation负责具体的注解查找/** * 判断给定的类是否带有 AspectJ 注解。 * param clazz 要检查的类 * return 如果类带有 AspectJ 注解则返回 true否则返回 false */ private boolean hasAspectAnnotation(Class? clazz) { // 使用 AnnotationUtils.findAnnotation 方法查找类上的 Aspect 注解 return (AnnotationUtils.findAnnotation(clazz, Aspect.class) ! null); }这里使用AnnotationUtils.findAnnotation而非简单的clazz.isAnnotationPresent(...)意味着Aspect可以以元注解meta-annotation的形式出现在自定义注解上findAnnotation会沿着注解层级递归查找兼容性更强。而compiledByAjc(clazz)用于排除已经由 AspectJ 编译器织入过的类避免重复处理。七、与 Advisor 解析工厂的协作buildAspectJAdvisors()中反复出现的this.advisorFactory.getAdvisors(factory)委托给了ReflectiveAspectJAdvisorFactory实现了AspectJAdvisorFactory接口。该工厂的核心职责包括验证切面类合法性遍历切面类中的方法识别带Before、After、Around、AfterReturning、AfterThrowing、Pointcut等注解的方法为每个通知方法生成对应的InstantiationModelAwarePointcutAdvisorImpl内部封装切点表达式与通知逻辑查找DeclareParents引入字段并生成引入型 Advisor。接口定义参见仓库中的 spring-aop-aspectJAdvisorFactory 模块其AspectJAdvisorFactory接口包含getAdvisors(MetadataAwareAspectInstanceFactory)、getAdvice(...)等核心方法。可以说BeanFactoryAspectJAdvisorsBuilder负责找到并管理切面 Bean而ReflectiveAspectJAdvisorFactory负责把切面类翻译成 Advisor 对象二者配合才完成了注解式 AOP 的完整解析。八、小结BeanFactoryAspectJAdvisorsBuilder是 Spring 注解式 AOP 的切面收集器位于自动代理链路AbstractAutoProxyCreator→AnnotationAwareAspectJAutoProxyCreator→BeanFactoryAspectJAdvisorsBuilder的末端。其核心方法buildAspectJAdvisors()通过双重检查加锁保证单次扫描遍历 Bean 工厂中的全部 Bean经isAspect()判定后按单例/多例实例化模型分别构建 Advisor并用advisorsCache/aspectFactoryCache两个缓存避免重复解析。切面判定依赖Aspect注解支持元注解且排除 ajc 编译类Advisor 的最终生成委托给ReflectiveAspectJAdvisorFactory。运行时效果已验证于示例模块切面通知在MyService.foo()执行前被正确触发输出 Before method execution 后再输出 foo...完整示例代码与配置文件均可在 spring-aop-beanFactoryAspectJAdvisorsBuilder 目录下找到。赞分享示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载相关推荐DevOps-Bash-tools与Logstash日志处理管道自动化DevOps Bash tools与Logstash日志处理管道自动化 日志处理的现代挑战与自动化机遇 在分布式系统架构下日志数据呈现 爆发式增长 与 碎片示例工程文档flue/twilioFlue 框架的 Twilio 消息通道接入指南flue/twilioFlue 框架的 Twilio 消息通道接入指南 flue/twilio 是 Flue 沙箱 Agent 框架sloganThe示例工程文档终极指南Spring AOP与AspectJ对比如何选择最适合的AOP实现方案终极指南Spring AOP与AspectJ对比如何选择最适合的AOP实现方案 在Java开发中AOP面向切面编程是实现横切关注点如日志、事务、安全文档教程后端上一篇ustcthesis参考文献管理详解BibTeX与BibLaTeX对比分析下一篇5分钟上手最简单的私有Docker仓库管理界面Docker-Registry-UI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

YOLOv5小麦麦穗检测全流程:从标注训练到Flask网页部署 2026/10/2 2:47:55

YOLOv5小麦麦穗检测全流程:从标注训练到Flask网页部署

简介:本资源是一套面向计算机视觉初学者与农业AI应用开发者的毕业设计级项目,聚焦小麦麦穗的自动化检测需求,解决农业生产中作物生长状态评估、产量预估与智能监测等实际问题。压缩包共37个文件,含14个Python源码(覆盖…

阅读更多 →
RHCSA作业实操指南:从环境准备到重启验证的核心技能 2026/10/2 2:47:55

RHCSA作业实操指南:从环境准备到重启验证的核心技能

如果你手头恰好有一份RHCSA认证相关的实操作业,或者你正在备考红帽系统管理员认证,却总觉得知识点零零散散、不知道从哪下手,那这篇文章你值得看完再动手。RHCSA全称Red Hat Certified System Administrator,是红帽体系里最基础的…

阅读更多 →
汇报总结PPT来不及做?6款AI工具深度横评,手把手教你选对那一款 2026/10/2 2:47:48

汇报总结PPT来不及做?6款AI工具深度横评,手把手教你选对那一款

每到月底、季度末或者年底,不少职场人都会经历一个共同的“噩梦”——汇报总结PPT。加班到深夜,对着空白幻灯片发呆,选题没思路、排版没灵感、内容不知道如何提炼……更令人头大的是,手头攒了不少之前的PPT截图、会议记录照片、各…

阅读更多 →
基于Spark+Hive的交通智能研判系统:离线链路与典型算法实践 2026/10/2 2:47:42

基于Spark+Hive的交通智能研判系统:离线链路与典型算法实践

简介:基于Spark与Hive的交通智能研判系统是一套完整的大数据工程源码,面向毕业设计与课程设计场景,帮助学习者掌握从交通流数据采集、处理到智能研判的完整流程。压缩包共58个文件,以42个Java源文件为核心,负责数据读取…

阅读更多 →
SpringBoot+Vue+MySQL:线上历史馆藏系统可直接运行实操全记录 2026/10/2 2:47:42

SpringBoot+Vue+MySQL:线上历史馆藏系统可直接运行实操全记录

这个项目我实际跑过不少次,刚好最近又给一个文博类的毕设项目做二次开发,索性把整套线上历史馆藏系统掰开揉碎写一篇完整的实操记录。前后端分离、SpringBoot 做后端服务、Vue 做管理界面、MySQL 存业务数据,这种组合在馆藏、档案、展览管理这…

阅读更多 →
LLM驱动渗透测试:自然语言CLI攻击框架的设计与落地实践 2026/10/2 2:47:42

LLM驱动渗透测试:自然语言CLI攻击框架的设计与落地实践

做渗透测试这些年,最烦的事清单里,记命令一定排前三。后来我试着把LLM接进日常工作流,做了一个用自然语言下指令的CLI攻击框架——输入“扫一下子域”,它自己决定调哪个工具、填什么参数、把结果整理成结构化报告。这个项目我已经…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉