一.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 方法不属于实例方法, 无法拦截
- 异常处理问题:
异常被吞掉,导致不回滚:事务是靠方法抛出异常回滚的, catch住异常后, 对代理来说方法正常返回
默认受检异常不回滚:只对RuntimeExeception 和Error 回滚,抛SQLException ,IOException 这种受检查异常事务照常提交, 需要制定回滚的异常类型。
3.传播行为问题:
内层方法用REQUIRES_NEW 想开独立事务 , 但有是自调用
外层用NOT_SUPPORTED/NEVER ,内层还在写数据-->无事务
内层事务抛异常被外层 try-cache 吞掉,内层标记rollback-only,外层提交时抛UnexpectedRollbackException :内存回滚, 外层误提交,不是失效,容易误判: