当前位置: 首页 > news >正文

Spring之AOP源码解析(上)

Aop相关注解

  • @EnableTransactionManagement
  • @EnableAspectJAutoProxy
  • @EnableAsync
  • ...

从注解切入来看看这些注解都干了什么

@Import注解作用简述

注入的类一般继承ImportSelector或者ImportBeanDefinitionRegistrar接口

  • 继承ImportSelector接口:selectImports方法返回的类名会被解析成bean
  • 继承ImportBeanDefinitionRegistrar接口:会在解析阶段执行registerBeanDefinitions方法

Spring解析流程可以看我之前写的博文 《Spring之ConfigurationClassPostProcessor解析流程》重点讲解了@Import等注解是如何解析的

@EnableAsync注解

@EnableAsync注解作用
  • 注入一个类型为AsyncAnnotationBeanPostProcessor的BeanPostProcessor

@EnableTransactionManagement注解

@EnableTransactionManagement注解作用
  • 注入一个类型为InfrastructureAdvisorAutoProxyCreator的BeanPostProcessor
  • 注入事务相关的bean

@EnableAspectJAutoProxy

@EnableAspectJAutoProxy注解作用
  • 注入一个类型为AnnotationAwareAspectJAutoProxyCreator的bpp

@EnableTransactionManagement和@EnableAspectJAutoProxy注解的渊源

从方法的角度

两个类都是通过AopConfigUtils.registerXXX方法注入一个bpp到spring中

注意点:AopConfigUtils的register方法存在三个优先级,如果spring调用了多次register方法,spring会保留优先级最高的bpp(beanName为org.springframework.aop.config.internalAutoProxyCreator)。比如Spring中同时存在@EnableTransactionManagement和@EnableAspectJAutoProxy注解,但因为@EnableAspectJAutoProxy注解注入的bpp的优先级高于@EnableTransactionManagement注解注入的bpp,所以spring只有一个类型为AnnotationAwareAspectJAutoProxyCreator的bean,而不存在类型为InfrastructureAdvisorAutoProxyCreator的bean

相关源码AopConfigUtils#registerOrEscalateApcAsRequired

从类的的角度

两个bpp都继承AbstractAutoProxyCreator,然而Spring动态代理相关的逻辑都是在这个类中处理的,所有我们可以认为@EnableTransactionManagement和@EnableAspectJAutoProxy注解实现动态代理的逻辑是一样的。

注解实现动态代理的时机

@EnableAsync注解

我们从前文中了解到@EnableAsync注解会注入一个类型为AsyncAnnotationBeanPostProcessor的bpp,我们来查看源码

我们通过类的继承关系,关注其祖父类(AbstractAdvisingBeanPostProcessor)的postProcessAfterInitialization方法

@EnableTransactionManagement和@EnableAspectJAutoProxy注解

通过上文的分析,我们主要查看AbstractAutoProxyCreator的postProcessAfterInitialization方法

通过源码,我们得出以下几个结论

  • 几个注解注入的bpp都是通过postProcessAfterInitialization方法进行动态代理
  • Spring是通过ProxyFactory这个类完成动态代理的

Spring是如何解决多个bpp对bean进行处理的顺序问题

我们回过头查看@EnableAsync注解注入的bpp的postProcessAfterInitialization方法

从码义上来说,@EnableAsync注解进行AOP动态代理优先级是比较低的,那Spring是怎么处理,让优先级比较低的bpp稍后执行呢?

我们查看源码PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors

Spring会对所有的bpp进行排序,然后按顺序加入到列表中,优先级较高的优先加入,优先处理

这里的比较器使用的是AnnotationAwareOrderComparator,这个比较器大致分成以下步骤

  1. bpp是否继承PriorityOrdered接口,如果都继承PriorityOrdered接口,比较getOrder方法返回的值,值越小,优先级越高
  2. bpp是否继承Ordered接口,如果都继承Ordered接口,比较getOrder方法返回的值,值越小,优先级越高
  3. bpp所属class上是否存在@Order注解,如果存在,比较@Order注解设置的值,值越小,优先级越高
  4. 其他

这里需要注意的是,如果一个bpp继承PriorityOrdered接口,getOrder方法返回的值为100,另外一个bpp继承Ordered接口,getOrder方法返回的值为1,按照第一优先级规则,还是继承PriorityOrdered接口的bpp优先级高

我们查看三个注解相关源码

@EnableAsync注解order默认值是Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE)

@EnableTransactionManagement注解order默认值是Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE)

@EnableAspectJAutoProxy注解未开启设置

默认的情况下,@EnableAsync和@EnableTransactionManagement注解注入的bpp都继承Ordered接口,并且getOrder方法返回的值都是Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE),默认情况下优先级一致,但是@EnableTransactionManagement注解注入bpp的时候,对order值进行了手动设置,保证了如果同时存在@EnableAsync和@EnableTransactionManagement注解的情况下,@EnableTransactionManagement注解注入的bpp进行AOP动态代理,@EnableAsync注解注入的bpp进行增强。如果只存在@EnableAsync注解,则相关bpp进行AOP动态代理

相关源码AopConfigUtils#registerOrEscalateApcAsRequired

这里需要说明一下,虽然@EnableAspectJAutoProxy注解不能指定order值,但是有默认值,默认值也是Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE),@EnableTransactionManagement和@EnableAspectJAutoProxy注解从一定程度上来说,这些可以指定的参数是互补的,因为它们进行AOP动态代理的是操作是通过共同父类AbstractAutoProxyCreator完成的

总结

  • @EnableTransactionManagement,@EnableAspectJAutoProxy,@EnableAsync注解都有可能产生AOP动态代理
  • @EnableTransactionManagement,@EnableAspectJAutoProxy注解注入的bpp,都是依靠AbstractAutoProxyCreator这个类完成动态代理的。如果它们同时存在,Spring中只会存在一个优先级更高的bpp。这两个注解指定的参数是互补的,对于特定需求,可以进行扩展
  • 几个注解注入的bpp,都是Spring执行到postProcessAfterInitialization方法阶段,完成AOP动态代理
  • Spring通过registerOrEscalateApcAsRequired方法手动修改了@EnableTransactionManagement,@EnableAspectJAutoProxy注入的bpp的order值,所以执行顺序优先于@EnableAsync注入的bpp
  • 几个注解都是通过ProxyFactory这个类,以策略模式完成AOP动态代理(我们下一篇文章将重点讲解这个类)

相关文章:

  • 鸿蒙原生应用元服务实战-发布时多设备选择注意事项
  • 9、内网安全-横向移动Exchange服务有账户CVE漏洞无账户口令爆破
  • MacBook的nginx出现13: Permission denied 的问题分析和解决办法
  • 蓝桥杯备赛系列——倒计时50天!
  • Neo4j导入数据之JAVA JDBC
  • 消息中间件之RocketMQ为什么写文件这么快?
  • R语言【raster】——projectRaster():映射一个Raster对象
  • Laravel02 路由基本概念和用法 给视图传递请求参数
  • Docker Image(镜像)
  • sentinel的资源数据指标是如何采集
  • Vue3 + Ts (使用lodash)
  • electron学习和新建窗口
  • 强化学习入门(Matlab2021b)-创建环境【2】
  • [TCP] TCP/IP 基础知识词典(2)
  • Putty中运行matlab文件
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • 2017年终总结、随想
  • 3.7、@ResponseBody 和 @RestController
  • ECS应用管理最佳实践
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • in typeof instanceof ===这些运算符有什么作用
  • Laravel Mix运行时关于es2015报错解决方案
  • TCP拥塞控制
  • 初探 Vue 生命周期和钩子函数
  • 简单易用的leetcode开发测试工具(npm)
  • 漂亮刷新控件-iOS
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 如何在GitHub上创建个人博客
  • 数据可视化之 Sankey 桑基图的实现
  • 跳前端坑前,先看看这个!!
  • UI设计初学者应该如何入门?
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (全注解开发)学习Spring-MVC的第三天
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Micro Framework 4.2 beta 源码探析
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • /var/log/cvslog 太大
  • [1] 平面(Plane)图形的生成算法
  • [Angular] 笔记 6:ngStyle
  • [APIO2012] 派遣 dispatching
  • [CISCN2019 华东南赛区]Web4
  • [JavaWeb]—前端篇
  • [LeetCode] Verify Preorder Sequence in Binary Search Tree 验证二叉搜索树的先序序列
  • [Linux] MySQL数据库之索引
  • [node] Node.js的Web 模块
  • [office] excel如何计算毛重和皮重的时间间隔 excel计算毛重和皮重时间间隔方法 #笔记#学习方法
  • [office] 在Excel2010中设定某些单元格数据不参与排序的方法介绍 #其他#知识分享#笔记
  • [one_demo_14]一个简单的easyui的demo
  • [python] logging输出到控制台(标准输出)