Spring基本原理学习笔记

Spring基本原理学习笔记

普通类 -> Bean的整个过程

Spring把一个普通类放入IOC容器的整个过程(默认为单例Bean):

1、构造方法

2、依赖注入

3、初始化前

4、初始化

5、初始化后

6、对象代理(如果有AOP)

7、对象放入单例池Map

构造方法

  • Spring默认会调用无参构造

  • 只有一个有参构造会调用这个有参构造,参数的值会从IOC中按类型(byType)寻找

    • 找到:赋值给参数
      • IOC中找到一个,直接赋值(这个过程可能会产生循环依赖)
      • 找到多个,按形参名匹配(byName),匹配不上则报错
    • 没找到:创建对象,赋值为参数
  • 多个参数,需要告诉Spring使用哪一个,使用@Autowire指定,不指定会报错

依赖注入

Spring会遍历类的所有字段,发现有注解@Autowire,按按类型(byType)到Map寻找,没找到再按名字(ByName)找,再没找到就不管了,业务代码使用的时候回报NPE(空指针异常)

初始化前

如果想在Bean的初始化前做处理的话,可以随便写一个返回值为Void的方法,加上注解@PostConstruct

在初始化前这一阶段,Spring会遍历方法,发现方法有该注解,会执行该方法

初始化

在初始化阶段做处理的话,需要实现 InitializingBean 接口
(img-bvBaTjOT-1668317196002)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20221111205210640.png)]
在afterPropertiesSet方法中写初始化的业务

初始化后

AOP是在初始化后这个阶段执行的。SpringBoot默认使用的是cglib代理(基于继承),Spring默认使用的是JDK代理(基于接口)

下面是cglib的代理实现

在这里插入图片描述

target是目标类,位置在IOC容器

事务失效原理

1、普通对象调用事务方法会导致事务失效,只有代理对象调用事务方法才会生效

2、数据库配置类没有加@Configuration,导致JdbcTemplate 和 事务管理器 用的不是同一个DataSource对象,事务管理器 在JdbcTemplate 执行SQL时 会创建新的Connection , 而保证事务正常运行,需要所有SQL运行在同一个Connection;

加上@Configuration的话,JdbcTemplate 和 事务管理器用的是同一个DataSource,这时事务管理器创建连接存到ThreadLocal<Map<DataSource,Connection>> 中,JdbcTemplate 执行时通过自身的DataSource直接从Map中拿到连接,这个连接和事务管理器是同一个连接,从而实现事务功能。在这里插入图片描述

上面的代码a方法的事务失效,传播属性NEVER作用:有事务的方法执行a方法时抛异常,都是实际并没有抛异常,也就是说事务失效了。

原因:调用a方法的对象并不是代理对象而是普通对象即UserService而不是UserServiceProxy,故并没有开启事务,所以不会抛异常

为什么test()方法事务没失效:因为test方法执行的是 UserServiceProxy里面重写的test方法

在这里插入图片描述

怎么执行的a方法: target.test().a() 而target类型是UserService,所以a方法执行之前并没有关闭自动提交,自然事务没起作用

整个过程: 开启事务 -> Test() -> a() -> 关闭事务

设想:开启事务A -> Test() ->开启事务B -> a() ->关闭事务B -> 关闭事务A

解决方案1:把a方法拆到另外一个Bean中

解决方案2:自己注入自己(也是一种循环依赖)

在这里插入图片描述

解决方案3:拿到当前代理对象
在这里插入图片描述

@Configuration实现原理

@Configuration 、 AOP 、 @Lazy 都是基于动态代理,是平级的。

加上@Configuration表示这个类是代理类。

@Configuration是如何完成jdbcTemplate 和 事务管理器拿到同一个DataSource的?
在这里插入图片描述

按顺序执行:

1、执行JdbcTemplate 调用 dataSource 方法

2、dataSource 方法会先去IOC看一下有没有DataSource对象,没有则创建DataSource对象到IOC容器中

3、执行事务管理器,调用dataSource 方法,去IOC看一下有没有DataSource对象,有则直接拿来用,不dataSource 方法里面的创建动作

执行方法之前去IOC容器看一下有没有这个动作在哪做的?

因为@Configuration是一个代理类,所以说这个动作是代理类做的,在执行 super.dataSource() 方法之前做的判断。

Spring循环依赖

什么是循环依赖现象:A依赖B,B依赖A -> 循环依赖 、 A 依赖 A 、A 依赖 B ,B依赖C ,C依赖 A 、或者更长的一个闭环等。

Spring解决循环依赖:1、三级缓存 2、@Lazy注解

在这里插入图片描述

怎么判断出现了循环依赖

在这里插入图片描述

1、AService这个Bean创建的时候会把AService的名字放入一个creatingSet中

2、AService依赖BService

3、创建Bservice,发现依赖AService

4、BService去IOC容器找,没有 ,看一下是不是在创建中,是,出现循环依赖

三级缓存

第一级缓存:单例池 singletonObjects,存放经过完整生命周期的Bean

第二级缓存:earlySingletonObjects,存放代理对象,作用:保证代理对象单例,只有出现循环依赖时才需要用到,这个代理对象的target属性还没有经过完整的生命周期

第三级缓存: singletonFactories:保存实例化之后的普通对象(实际上存的是一个lambda,执行lambda返回代代理对象),供AOP使用,作用:打破循环

如果BService需要AOP时,会从earlySingletonObjects 二级缓存看有没有,没有的话进行AOP放入二级缓存,这时其他Bean依赖BService时,可以直接从 二级缓存拿到代理对象,就不会导致创建多个Bean的情况
在这里插入图片描述

怎么打破循环

2.2步骤的AOP是需要拿到AService普通对象的,因为代理对象的target属性需要赋值。这时就需要 第三级缓存 来保存普通对象,让代理对象拿,拿到之后从三级缓存移除,返回的代理对象添加到二级缓存。
在这里插入图片描述

@Lazy为什么能解决循环依赖

在这里插入图片描述

@Lazy为什么能解决循环依赖:因为BService会被赋值一个@Lazy产生的代理对象,所以AServic能正常创建到单例池,用到BService时,再从单例池找到AService注入BService,这样就解决了循环依赖。

下面的BService通过构造注入,AService也是构造注入的话,这时产生的循环依赖Spring解决不了的,因为在实例化的时候产生的循环依赖,此时普通对象都还没创建出来,三级缓存还没起作用。需要使用@Lazy注解才能解决。

在这里插入图片描述

加上@Lazy注解,BService会赋值一个代理对象,自然能正常创建Bean
在这里插入图片描述

Spring整合Mybatis底层原理

如何创建Mapper代理对象放入IOC?

Service层使用Mapper接口,需要注入Mapper,但是Mapper是接口,是不能创建成Bean的,只能通过动态代理的方式,生成代理对象放入IOC容器,供Service层使用。

如何创建代理对象呢?

可以通过FactoryBean接口实现,实现FactoryBean接口的类,Spring会为它创建两个Bean,一个是FactoryBean对象、一个是getObject方法返回的对象。

getObject方法会返回代理对象。

在这里插入图片描述

这时实现FactoryBean的类需要加@component注解,FactoryBean类其实可以用其他方法使用的。

比如说BeanDefinition

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJCNIZZp-1668317196013)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20221112195325455.png)]

BeanDefinition位置需要写在Spring容器下面,非常的麻烦,可以用其他的方法简化

创建一个类实现ImportBeanDefinitionRegister·接口,在这个类里面注册BeanDefinition

在这里插入图片描述

使用的时候直接通过@Import注解导入即可
在这里插入图片描述

这时一个BeanDefinition对应一个Mapper,怎么动态的获取Mapper并注册呢?

Mapper扫描自己怎么实现?

实现ClassPathBeanDefinitionScanner接口,因为Spring扫描会默认忽略接口,需要重写isCandidateComponent方法,设置为只扫描接口。还需要重写DoScan,因为扫描之后为Mapper接口,但是我们需要的是BeanDefinition,所以需要把Mapper转换为BeanDefinition

在这里插入图片描述

怎么用呢?

通过注解拿到扫描路径,创建扫描器传入Spring容器,添加扫描过滤器。

在这里插入图片描述

@LockUp

在这里插入图片描述

这里调用test方法会返回三个不同的orderService,如果不加@LockUp,UserService里面调用无数次test方法,使用的是同一个orderService,这不符合orderService原型的定义,加上@LockUp注解就能每调用一次test返回一个新的orderService。

在这里插入图片描述

Spring怎么扫描的

在这里插入图片描述

笔记参考B站视频

https://www.bilibili.com/video/BV1tR4y1F75R/?vd_source=587abd5e4b9054694d34a59f50f4e3c6

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值