Spring相关知识
Spring相关知识
Spring
说说对Spring的理解
- Spring 是一个以 IoC(控制反转)和 AOP(面向切面编程)为核心思想的 Java 企业级开发基础框架
说说Spring IoC和AOP
IoC
- 控制反转。传统开发过程中,我们需要通过new关键字来创建对象。使用IoC思想开发方式的话,我们不通过new关键字创建对象,而是通过IoC容器来帮我们实例化对象。 通过IoC的方式,可以大大降低对象之间的耦合度。
AOP
- 是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,以减少系统的重复代码,降低模块间的耦合度。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。
- AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
二者结合
- 通过 IOC 容器管理对象的依赖关系,然后通过 AOP 将横切关注点统一切入到需要的业务逻辑中。
- 使用 IOC 容器管理 Service 层和 DAO 层的依赖关系,然后通过 AOP 在 Service 层实现事务管理、日志记录等横切功能,使得业务逻辑更加清晰和可维护。
IOC和AOP是通过什么机制来实现的?
IoC
- 反射:Spring IOC容器利用Java的反射机制动态地加载类、创建对象实例及调用对象方法,反射允许在运行时检查类、方法、属性等信息,从而实现灵活的对象实例化和管理。
- 依赖注入:IOC的核心概念是依赖注入,即容器负责管理应用程序组件之间的依赖关系。Spring通过构造函数注入、属性注入或方法注入,将组件之间的依赖关系描述在配置文件中或使用注解。
- 容器实现:Spring IOC容器是实现IOC的核心,通常使用BeanFactory或ApplicationContext来管理Bean。BeanFactory是IOC容器的基本形式,提供基本的IOC功能;ApplicationContext是BeanFactory的扩展,并提供更多企业级功能。
- 设计模式:工厂模式,Spring IOC容器通常采用工厂模式来管理对象的创建和生命周期。容器作为工厂负责实例化Bean并管理它们的生命周期,将Bean的实例化过程交给容器来管理。
AOP
- 动态代理技术
- 基于JDK的动态代理:使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。这种方式需要代理的类实现一个或多个接口。
- 基于CGLIB的动态代理:当被代理的类没有实现接口时,Spring会使用CGLIB库生成一个被代理类的子类作为代理。CGLIB(Code Generation Library)是一个第三方代码生成库,通过继承方式实现代理。
依赖倒置,依赖注入,控制反转分别是什么?
- 控制反转:“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。
- 依赖注入:依赖注入和控制反转恰恰相反,它是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
- 依赖倒置:这条原则跟控制反转有点类似,主要用来指导框架层面的设计。高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。
如果让你设计一个SpringIoc,你觉得会从哪些方面考虑这个设计?
- Bean的生命周期管理:需要设计Bean的创建、初始化、销毁等生命周期管理机制,可以考虑使用工厂模式和单例模式来实现。
- 依赖注入:需要实现依赖注入的功能,包括属性注入、构造函数注入、方法注入等,可以考虑使用反射机制和XML配置文件来实现。
- Bean的作用域:需要支持多种Bean作用域,比如单例、原型、会话、请求等,可以考虑使用Map来存储不同作用域的Bean实例。
- AOP功能的支持:需要支持AOP功能,可以考虑使用动态代理机制和切面编程来实现。
- 异常处理:需要考虑异常处理机制,包括Bean创建异常、依赖注入异常等,可以考虑使用try-catch机制来处理异常。
- 配置文件加载:需要支持从不同的配置文件中加载Bean的相关信息,可以考虑使用XML、注解或者Java配置类来实现。
bean的创建过程(IOC容器的创建过程)
IOC容器加载过程
- 创建容器:new ApplicationContext
- 读取bean定义信息,并转化为多个beanDefinition对象,并注册到beanDefinitionMap(一个哈希表)中
- 一个个创建bean,需要判断是否懒加载,是否单例bean
创建
- 实例化:使用反射,从beanDefinition获取beanclass
- 属性注入
- 初始化
- 缓存到一级缓存(单例池,如果是单例)
动态代理
- 动态代理是在运行时动态生成代理对象,而不是在编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。
- 若代理的类实现了接口,使用JDK动态代理,没有实现则使用CGLIB
三级缓存解决循环依赖
循环依赖问题
- 第一种:通过构造方法进行依赖注入时产生的循环依赖问题。
- 第二种:通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
- 第三种:通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。
- 解决的是第三种
三级缓存
- singletonObjects (一级缓存):存放的是完全初始化好的、可用的 Bean 实例,getBean() 方法最终返回的就是这里面的 Bean。此时 Bean 已实例化、属性已填充、初始化方法已执行、AOP 代理(如果需要)也已生成。
- earlySingletonObjects (二级缓存):存放的是提前暴露的 Bean 的原始对象引用 或 早期代理对象引用,专门用来处理循环依赖。当一个 Bean 还在创建过程中(尚未完成属性填充和初始化),但它的引用需要被注入到另一个 Bean 时,就暂时放在这里。此时 Bean 已实例化(调用了构造函数),但属性尚未填充,初始化方法尚未执行,它可能是一个原始对象,也可能是一个为了解决 AOP 代理问题而提前生成的代理对象。
- singletonFactories (三级缓存):存放的是 Bean 的 ObjectFactory 工厂对象。,这是解决循环依赖和 AOP 代理协同工作的关键。当 Bean 被实例化后(刚调完构造函数),Spring 会创建一个 ObjectFactory 并将其放入三级缓存。这个工厂的 getObject() 方法负责返回该 Bean 的早期引用(可能是原始对象,也可能是提前生成的代理对象),当检测到循环依赖需要注入一个尚未完全初始化的 Bean 时,就会调用这个工厂来获取早期引用。
过程
- 1️⃣ 创建 Bean A
- Spring 开始创建 Bean A 的实例(构造函数执行)
- 此时还未注入属性,也未初始化
- Spring 将一个 ObjectFactory(可能生成代理对象) 放入 三级缓存
- 2️⃣ Bean A 依赖 Bean B → 创建 Bean B
- Spring发现 Bean A 依赖 Bean B,开始创建 Bean B
- 同样放入 Bean B 的 ObjectFactory 到三级缓存
- 3️⃣ Bean B 依赖 Bean A → 需要获取 Bean A 的引用
- Spring 调用 getBean(“A”),发现 Bean A 正在创建中
- 查找缓存:
- 一级缓存:没有
- 二级缓存:没有
- 三级缓存:找到了 Bean A 的 ObjectFactory
- 4️⃣ 通过 ObjectFactory 获取 Bean A 的早期引用
- Spring 调用 ObjectFactory 的 getObject() 方法
- 此时会判断是否需要 AOP 代理:
- ✅ 如果需要 → 创建代理对象(JDK 或 CGLIB)
- ❌ 如果不需要 → 返回原始实例
- 将代理对象放入 二级缓存
- 5️⃣ Bean B 注入 Bean A 的代理对象
- Bean B 完成属性注入和初始化
- 放入一级缓存
- 6️⃣ 回到 Bean A,完成注入和初始化
- Bean A 注入 Bean B 的完整引用
- Bean A 完成初始化
- 放入一级缓存(此时是代理对象)
Spring相关知识
https://sdueryrg.github.io/2025/09/11/Spring相关知识/