Bean创建的生命周期
UserService.clas–>推断构造方法–>对象–>依赖注入—初始化前(@Postconstruct)—>初始化(@AfterPropertiesSet)—>初始化后(AOP)—>代理对象–>放入Map(单例池)—Bean对象
推断构造方法
推断构造方法底层原理
先观察几个现象
这里UserService中并没有无参的构造方法,它 就会推断出 使用这个 构造方法,这时候 这个test()调用是会有值的。spring会去找一个(先 ByType,再ByName) orderService对象给它赋值(判断其是否为一个bean? 是 ,为单例时:从单例池中获取,有就获取,无就会创建 否,直接创建一个新的)
这里可能会产生一个循环依赖的问题:当UserService构造函数中需要注入orderService,而orderService的构造方法需要UserService
通过 ByType也可能会产生问题,可能在beanfactory中含有key值不同类型一致的 Bean
当我们指一个bean为单例时,是指他的名字是单例,而不是类型。同一个类型有不同名的单例对象,比如说resource就可以指定注入对象名
当无参加入时,就会用使用无参的构造方法。
当注释无参构造方法,只有一个参数与两个参数的构造方法时,就会报错。
spring判断构造方法:
1、当只有一个构造方法,就会使用那个构造方法
2、当有多个构造方法时,会去找无参的 构造方法,没有无参就会报错。
想要加入多个构造方法时,可以在构造方法上加入 @Autowired注解
spring依赖注入时,一般是需要 采用@Autoware注解。
总结
1、选择构造方法
2、选择构造方法的参数
依赖注入
找到类中被@Autowired等注解的属性,再注入(先ByType,再ByName)
初始化前
处理@Postconstruct注解。
@PostConstruct是Java自带的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。
作用:在依赖注入后对属性进行一些其他的操作(加载数据库对象等)。
初始化
处理@AfterPropertiesSet(),初始化非Bean属性。
初始化后
AOP,代理类中的@Autowired不会生效?为什么?其实之前对象 就 已经 进行依赖注入了。
BeanFactory中返回的代理对象中没有依赖注入,但是调用 时又会有值?
AOP动态代理
事实上,普通对象已经传入给 target了
Spring事务
事务也是 产生 一个代理对象,调用对应的方法。
不开启@configuration会生效的原因(涉及了 spring bean的机制与方法的多态)
当 调用方法 上加入@Transaction 时才会开启事务(正常的思路)
注解 @Transaction 原理
这里 a()上@Transactional(propagation = Propagation.NEVER) 有事务存在就会报错 ,没有生效。
这里通过 test()方法去调用a()方式。此时a()上的注解@Transactional(propagation)会失效,原因是 :由于事务是基于动态代理的 ,执行test()方法的是 target对象 ,在执行之前方法 上的@Transaction会被spring识别(从而生成代理对象),而target对象就是普通的bean对象,在执行test()中a()方法时,就不会去识别a()上的@Transaction注解了。
这里考虑的是@Transaction注解什么时候会生效,是什么对象去调用它
但是我们想要去调用这个a()方法,有什么方法 可以去处理呢?
1、可以把a()方法 从本类中 删除,加入到另一个类(A)中,则另一个类(B)也会被spring识别为 代理对象,再把B类 作为属性 注入A中,在通过B属性调用a()方法。
2、在本类(A)把 本类作为属性(AA)注入,在使用 作为属性(AA)去调用a()。[本质上就是获取当前的 AOP对象 去调用方法 ]
SpringTestService o = (SpringTestService)AopContext.currentProxy();
o.a();
需要注意的是 :
1)、把作为属性注入会产生循环依赖的问题。
2)、这里会报错
Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.
目前并没有解决这个错误。
不开启@configuration会生效的原因(涉及了 spring bean的机制与方法的多态
当不使用@Configuration时
JdbcTemplate 与Transaction中 保存的dataSource并不一致,都是新new出来 的。而它们保存在ThreadLocal中
当开启事务时,事务管理器会新建一个数据库连接 ,当使用JdbcTemplate时,它会 根据自己的DataSource去寻找ThreadLocal中相等的DataSource,从而获取conn。此时就获取 不到事务管理器中的 conn。此时获取的conn.autocommit=true,就会运行一句sql执行。
当开启@Configuration时,AppConfig也会 被代理,它基于动态代理【AOP,@Lazy 也基于动态代理 ,他们是基于动态代理而不是基于AOP】
方法的多态性:当子类调用super.方法A(这个父类方法中调用了方法B,这个方法B也被子类实现了),此时super.方法A中调用的方法B就会时子类的方法B。可以看做就近原则。因为此时是在 子类中运行的方法。
所以这里的 dataSource也是代理对象调用的,它会先去判断spring容器中是否有bean。
循环依赖
如何出现?
1、构造方法 循环依赖(加 @Lazy注解才可以解决)
在真正执行bService方法时才会去找到单例池中的Bean。就相当于换了另一个临时对象在这里等所有初始化完后再把临时对象挂载到真正的对象
2、依赖注入 循环依赖
如何解决?
我们自己如何解决?即 打破循环,保证单例
1、把普通对象存放在一个Map中
存在的问题:当AService使用AOP时,就会出现到单例池与注入属性不一致的问题
2、创建一个Set,采用earlySingletonObjects
问题:这里并没有打破循环依赖。
3、采用三级依赖 singletonFactories 打破循环。
但spring源码中并不是采用一个简单的Map对象,存入的是一个Map<beanName,对象>
,但这个对象采用Lamb表达式的形式。
Spring中使用 三级缓存(三个 Map)来解决
第一级缓存
: singletonObjects (单例池 ),只存储生命周期完整的Bean
第二级缓存
: earlySingletonObjects(用于缓存AOP对象、普通对象、代理对象,保证代理对象单例)
第三级缓存
: singletonFactories(推断构造方法后,即newInstance后把map<beanName,lamb表达式> 加入此 Map,出现循环依赖且二级缓存没有时查找该Map)当需要使用AOP时每次执行这个Lamb表达式时就会创建一个代理对象
,不需要时,就会创建普通对象
,把代理对象加入到二级缓存,从三级删除。打破循环
bean要么在二级缓存,要么在三级缓存,不能同时存在。
事务基于AOP,@Async 不基于AOP
从三级删除。打破循环
bean要么在二级缓存,要么在三级缓存,不能同时存在。
[外链图片转存中…(img-WWT5PFTj-1680020930145)]
[外链图片转存中…(img-bHaLALE5-1680020930145)]
事务基于AOP,@Async 不基于AOP