Spring的底层原理

news/2024/10/23 15:00:05/

文章目录

  • 1. Bean的生命周期
  • 2. 推断构造方法
  • 3. 依赖注入
  • 4. 初始化前
  • 5. 初始化
  • 6. 初始化后
  • 7. AOP
  • 8. Spring事务
  • 9. Spring事务失效
  • 10. @Configuration
  • 11. 循环依赖
  • 12. @Lazy

在这里插入图片描述

在这里插入图片描述

1. Bean的生命周期

UserService.class --> 推断构造方法 --> 普通对象 --> 依赖注入 --> 初始化前 --> 初始化 --> 初始化后(AOP)--> 代理对象 --> 放入Map(单例池)--> Bean对象

2. 推断构造方法

  • 对于bean类中定义了一个构造器(带参数或不带参数),或者不手动定义构造器(此时java默认会隐式的创建一个空参构造器),Spring在创建该bean时使用唯一的构造器。
  • 对于bean类中定义了多个构造器,Spring在创建该bean时会默认去找空参构造器,若没有则报错;
  • 对于bean类中定义了多个构造器,可以 为指定的构造器添加 @Autowire 注解,此时Spring在创建该bean时会使用指定的构造器。
  • 特别的:当Spring使用带参数的构造器创建bean时会在IOC容器中找形参对应的bean(先类型匹配,再名称匹配)并传入,找不到或不是bean则会报错
    • 对应找到过程,首先如果形参对应的bean是单例的则会先在 单例池 中寻找,找到则传入(先类型匹配,若有多个该类型的bean则再通过名称匹配,此时名称唯一匹配上则注入,没有则报错;若类型匹配只有一个时则直接注入无需匹配名称);若找不到则直接通过反射创建形参对应的bean(可能有多个),将其保存至 单例池,再查找。(在这个创建过程中可能出现循环依赖问题)
    • 如果形参对应的bean是多例的,则直接创建一个新的bean实例并注入
java">@Component
public class UserService{OrderService orderService;public UserService(){}@Autowirepublic UserService(OrderService orderService){}
}

3. 依赖注入

  • 对于bean的类中使用 @Autowire 等注解标注的属性,通过与构造器参数注入相同的方法进行依赖注入
  • @Autowire是先按类型匹配,再按名称。只匹配到一个该类型的对象则直接注入,对于匹配到多个该类型的对象再通过名称匹配,有则注入,没有则报错

4. 初始化前

  • 执行 @PostConstruct 所标注的方法
  • 执行 实现了 BeanPostPocessor 接口的bean 中的 前置处理方法

5. 初始化

  • 若当前创建的 bean 实现了 initializationBean 接口的 afterPropertiesSet 方法,则会执行

6. 初始化后

  • 执行 实现了 BeanPostPocessor 接口的bean 中的 后置处理方法

7. AOP

  • Spring的AOP基于Cglib的动态代理实现,其原理如下
    在这里插入图片描述

8. Spring事务

在这里插入图片描述

9. Spring事务失效

在这里插入图片描述

  • 可以自己注入自己,使用代理对象的a()方法,使得事务生效
    在这里插入图片描述

10. @Configuration

  • 对于自定义的配置类中 @Configuration 为当前配置类对象创建动态代理类(注意:是基于动态代理实现,不是基于AOP实现)其动态代理实现是基于继承
  • 由于 Spring 的事务中,需要使 transactionManagerjdbcTemplate 均使用一个dataSource(同一个对象),才能使得 ThreadLocal 中的 Map 中顺利传递,以保证事务的顺利执行
java">class AppConfigProxy extends AppConfig{public JDBCTemplate jdbcTemplate(){// 1. 此处会有代理逻辑代码,会先去 IOC 容器中其找是否已存在当前名(方法名)的bean,如果有则终止创建// 2. 使用被代理类的实现创建jdbcTemplate对象/**注意:super.jdbcTemplate()中所包含的 dataSource() 由于动态绑定机制,在调用时是使用当前代理类中的dataSource()方法,则该方法的逻辑是会先去 IOC 容器中去找对应的 Bean 没有再创建,对应多个方法中都使用 dataSource() 其实质是使用了同一个 dataSource 对象(单例的情况下)*/JDBCTemplate jt = super.jdbcTemplate();// 3. 将新创建的 JDBCTemplate 加入 IOC 容器....}public DataSource dataSource(){// 1. ...// 2. ...DataSource ds = super.dataSource()// 3. ...}
}
java">// 扫描组件
@ComponentScan("com.gyh")// 开启事务
@EnableTransactionManagement// 为当前对象创建动态代理类(注意:是基于动态代理实现,不是基于AOP实现)
@Configuration
public class AppConfig{@Beanpublic JdbcTemplate jdbcTemplate(){return enw JdbcTemplate(dataSource()); // 使用动态代理后的dataSource()}@Beanpublic PlatformTransactionManager transactionManager(){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource());  // 使用动态代理后的dataSource()}@Beanpublic DataSource dataSource(){DriverManagerDataSource dataSource = new DriverManagerDataSource();dataSource.setUrl("...");dataSource.setUsername("...");dataSource.setPassword("...");return dataSource;}}

11. 循环依赖

  • 一个循环依赖问题
    在这里插入图片描述
java">// 循环依赖的场景
@Component
public class AService{@Autowireprivate BService bService;@Autowireprivate CService cService;
}@Component
public class BService{@Autowireprivate AService aService;
}@Component
public class CService{@Autowireprivate AService aService;
}

在这里插入图片描述

  • 利用三级缓存解决循环依赖
    • 第一级缓存:singletonObjects ---- 在依赖注入时会首先在单例池中查找是否有经完整周期创建的bean,有则直接注入
    • 第二级缓存:earlySingletonObjects — 如果单例池中没有则判断是否存在循环依赖,若存在循环依赖,则会在二级缓存中查找是否有还未经完成创建的bean,有则直接获取并注入;没有则进入下一级缓存(二级缓存可以保证多个循环依赖中的单例情况)
    • 第三级缓存:singletonFactories — 该缓存会在所有 bean 创建对象(普通bean对象)后进行保存,待到某个bean经过二级缓存后需要,则从中寻找该beanName对应的普通对象,若需要提前AOP则进行动态代理,生成该普通对象的代理对象后保存至二级缓存;若没有AOP过程直接将普通bean对象保存至二级缓存。

12. @Lazy

  • 对延迟对指定的属性或构造器参数的注入,在 bean 的属性设置等环节中直接构造它的代理对象并赋值(不执行循环注入逻辑),而在实际使用它的时候再去创建该@Lazy标注的属性或构造器参数的bean二级缓存;若没有AOP过程直接将普通bean对象保存至二级缓存。

http://www.ppmy.cn/news/1541360.html

相关文章

nosql课本习题

nosql题目 1. 文档数据库相比其他 NoSQL 的突出优势和特点是什么? 答案: 文档数据库的突出优势在于它的灵活性和可扩展性。不同于传统的关系型数据库,文档数据库允许存储半结构化和非结构化数据,每个文档可以有不同的字段&#x…

简述微服务高可用之Sentinel、Seate

简述微服务高可用之Sentinel、Seate使用 下文主要讲述使用sentinel,如何降级限流熔断及如何使用seata管理分布式事务 sentinel服务端安装与使用 1、下载 进入https://github.com/alibaba/Sentinel/releases 根据你的需求进行下载对应版本 我这里是JDK17 下载的1.8.8版本&am…

网络安全——防火墙技术

目录 前言基本概念常见防火墙技术防火墙的主要功能防火墙的不足之处相关题目1.组织外部未授权用户访问内部网络2.DMZ区3.包过滤防火墙和代理服务防火墙 前言 这是在软件设计师备考时编写的资料文章,相关内容偏向软件设计师 基本概念 防火墙技术是网络安全领域中的…

Java | Leetcode Java题解之第491题非递减子序列

题目&#xff1a; 题解&#xff1a; class Solution {List<Integer> temp new ArrayList<Integer>();List<List<Integer>> ans new ArrayList<List<Integer>>();public List<List<Integer>> findSubsequences(int[] nums) …

走廊泼水节——求维持最小生成树的完全图的最小边权和

题目 思考 代码 #include <bits/stdc.h> using namespace std; const int N 6010; const int M N; int p[N], sz[N]; struct edge{int a;int b;int c;bool operator < (const edge& v) const{return c < v.c;} }e[M]; int find(int x) {if(p[x] ! x) p[x] …

vscode 配置构建、调试QT项目

1. 背景是已经安装好了QT和QT相关的所有依赖包 https://mp.csdn.net/mp_blog/creation/editor/142974086 2. 配置环境变量 找不到或者忘记在哪里可以使用指令查询 3.插件安装Qt Support、 Qt tools、Qt UI等 Cmake、Cmake Tools 等 C/C等 settings.json 配置 此部分是根据一些报…

c语言基础程序——经典100道实例。

c语言基础程序——经典100道实例 001&#xff0c; 组无重复数字的数002&#xff0c;企业发放的奖金根据利润提成003&#xff0c;完全平方数004&#xff0c;判断当天是这一年的第几天005&#xff0c;三个数由小到大输出006&#xff0c;输出字母C图案007&#xff0c;特殊图案008&…

ReactNative项目构建目录找不到问题解决

要检查你的 Expo 项目中 TypeScript 的配置&#xff0c;你需要查看 tsconfig.json 文件。这个文件位于项目的根目录&#xff0c;并且包含了 TypeScript 编译器的所有配置选项。以下是一些基本步骤来检查和理解你的 tsconfig.json 文件&#xff1a; 定位 tsconfig.json 文件&…