简单聊聊 Spring 框架源码

一.Spring Bean 的生命周期

1) 实例化: 推断构造器, new 出对象,属性全空

2)属性填充: populateBean, @Autowired,@Resource,@Value 注入 ;

3)Aware 回调:BeanNameAware -> BeanClassLoaderAware->BeanFactoryAware

4)BeanPostProcessor.postProcessBeforeInitialization

二.Spring IOC 是什么?

Spring IOC是什么:IOC (Inversion of control,控制反转):把创建对象,组装依赖,的控制权从业务代码手里反转给容器, 从编译期硬编码、对象自己主动获取,变成运行时由容器统一装配。

Spring IOC 做了什么:容器的本质就是注册表 + 工厂

beanDefinitionMap (图纸库): 扫描包/配置解析得到 BeanDefinition:类名、作用域、依赖关系

singletonObjects(成品库):按图纸创建好的实例,单例复用

getBean 主链路:获取对象-〉先查成品-〉没有就按图纸造(实例化-〉注入-〉初始化)

生命周期回调: 创建/销毁时机交给容器统一管理 (@PostConstruct 等)

总结:一个对象制造与调度中心,你只需要描述我需要什么, 容器负责在合适的时机造出来;

为什么要用IOC:

**解藕:**面向接口编程成为可能,依赖接口 , 具体实现由容器实现, 换实现不用改业务代码;

装配复杂度转移: 大项目的对象手动new 不现实;

生命周期与作用域管理: 对象不再用一次new 一次,单例服用, 按需创建,初始化/销毁回调,由容器统一调度,你能拿到的是管理良好的共享实例,而不是自生自灭的对象;

AOP能力的基石头: 事务,AOP 都靠代理,代理的前提是对象由容器创建,如果是业务代码new的, Spring 没机会包装,IOC 让对象的出生经过容器之手,容器才能在出生时做增强。

三.SpringAOP 是什么?

AOP 是什么:AOP(Aspect-Oriented Programming ,面向切面编程):把横切在各个方法里的公共逻辑(事务,日志,权限,缓存)从业务代码中抽取出来,集中定义,运行时动态织入;让业务代码和非业务代码的公共行为,解藕,公共行为集中在一处定义,一次编写、处处生效。

AOP 怎么做:BeanPostProcessor 创建代理 + Advisor 列表 + 拦截器链递归执行

--时机:Bean 生命周期 postProcessAfterInitialization,wrapIfNecessary 判断这个Bean 的方法也没有被切点命中, 命中就包代理替换原Bean

3.为什么使用SOP

事务- Spring 内置切面

日志- 统一记录操作日志

权限校验- 自定义权限校验逻辑

缓存- @Cacheable

限流- Sentinel 注解的本质都是AOP

四.Spring循坏依赖是怎么解决的

三级缓存是什么:

一级 singletonObjects: 成品单例池,对外唯一入口,可以直接使用;

二级 earlySingletonObjects:提前暴露的早起引用(过了代理处理):保证多次索取拿到同一个引用;

三级 singletonFactories:ObjectFactory 工厂,懒执行,没人要就不跑,有人要才执行 getEarlyBeanReference

三级缓存怎么做的:A->B->A

getBean(A)-> 没有->创建A -> 实例化完成(半成品) ->A 的工厂放进三级缓存

populateBean(B) -> getBean(B) ->创建B ->实例化 ->B的工厂也进三级缓存 ->populationBean(B)

->getSingleleton(A,true) -> 一级缓存没有 ->二级缓存没有 ->三级取出工厂执行 ->getEarlyBeanRefenrece(A) ->若A需要代理,这里提前包上 ->结果放进二级缓存,三级移除

->B注入早期的A -> B完成,放进一级缓存

->A 注入成品B ->完成初始化 ->进一级缓存,移除二级缓存

总结: 实例化后先把"半成品的引用工厂"挂出去, 循坏依赖方从三级缓存把它捞出来提前用,完成后再各自归位到一级缓存。

为什么必须是三级缓存:

一级缓存放的是成品,不能放半成品, 三级缓存存的是ObjectFactory,是 如何制造早期引用的配方,而不是引用本身;

getEarlyRefenrence 提前包AOP代理是个昂贵的操作,没有发生循环依赖时,这工厂永远不会被执行, 成本为零,一旦执行了,

结果升级到二级缓存--保证唯一性,如果每次都从三级取现执行,getEarlyReference 每次都会执行一个代理, 那么B、C拿到的A代理就不是同一个对象了,升级后二级里始终是那一个引用。

四.Spring 事务执行原理

执行过程:

**解析属性 :**从类上解析@Transactional ->TransactionAttribute(传播行为、隔离级别、超时、回滚规则)

**开启事务 :**平台事务管理器开启事务,设置事务自动提交关闭

业务方法执行: 执行业务代码和sql

异常判决: 正常commit ,异常rollbackOn(exception)

**收尾:**解绑ThreadLocal,恢复自动提交,归还连接池

五.Spring 编程式事务和注解式事务有什么区别?怎样会导致事务失效

Spring 编程式事务 是将事务管理器硬编码在业务代码中, 可以根据自己想要的场景手动提交或回滚,完全自主控制;

Spring 注解式事务 是通过@Transactional注解 + AOP 代理实现的, 业务代码中无须编写事务相关的代码,默认只对RuntimeException 和 Error回滚。

失效场景:

1.代理失效:

同类 自调用:直接调用目标对象的方法, 根本没经过Spring生成的代理对象

非public方法:代理只对public 方法生效

方法被static / final 修饰:final 修饰的方法无法被重写, static 方法不属于实例方法, 无法拦截

  1. 异常处理问题:

异常被吞掉,导致不回滚:事务是靠方法抛出异常回滚的, catch住异常后, 对代理来说方法正常返回

默认受检异常不回滚:只对RuntimeExeception 和Error 回滚,抛SQLException ,IOException 这种受检查异常事务照常提交, 需要制定回滚的异常类型。

3.传播行为问题:

内层方法用REQUIRES_NEW 想开独立事务 , 但有是自调用

外层用NOT_SUPPORTED/NEVER ,内层还在写数据-->无事务

内层事务抛异常被外层 try-cache 吞掉,内层标记rollback-only,外层提交时抛UnexpectedRollbackException :内存回滚, 外层误提交,不是失效,容易误判:

相关推荐
一木 之林1 小时前
七、一-AI 工程实践、插件化调试与软件交付
java·linux·c++
复方金银花颗粒1 小时前
reactor和proactor的好处和坏处。为什么要用reactor而不用 proactor?
后端·信号处理
边境悍匪1 小时前
蜗牛学苑 Java 智能体学习 Day38|Spring AI Alibaba2 思维导图复盘
java·学习·spring
Co_Hui2 小时前
Java 静态内部类、非静态内部类、匿名内部类的区别?
java
IT_陈寒2 小时前
JavaScript的这个隐式转换特性差点让我加班到凌晨
前端·人工智能·后端
Wang's Blog2 小时前
Java框架快速入门: Spring Security+OAuth2之自定义认证过滤器实现JSON登录
java·spring·json
用户8356290780512 小时前
使用 Python 在 Excel 中添加和编辑形状
后端·python
用户204937554952 小时前
端侧语音部署踩坑:模型能跑不等于终端真的能用
后端·算法