Spring
Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性。
IOC(控制反转)
控制反转(IoC, Inversion of Control)是一种设计原则,指的是将对象的创建和管理责任从程序代码转移到框架或容器中,允许框架控制程序的流程和对象的生命周期,从而实现更高的模块化和可维护性。在Spring中,IoC主要通过依赖注入(DI, Dependency Injection)实现,使得对象之间的依赖关系可以在运行时动态地进行管理,从而减少耦合度。
优点:
实现组件之间的解耦
提高程序的灵活性和可维护性
缺点:
对程序员来说创建对象的步骤变复杂了,不直观
因为使用反射来创建对象,所以在效率上会有些损耗。但相对于程序的灵活性和可维护性来 说,这点损耗是微不足道的。
缺少IDE重构的支持,如果修改了类名,还需到XML文件中手动修改。
DI(依赖注入):
通常,依赖注入可以通过三种方式完成,即: 构造函数注入 setter 注入 接口注入
spring 提供了哪些配置方式
Spring 提供了多种配置方式,以便开发者根据需求和偏好选择合适的方式。主要的配置方式包括:
1. XML 配置:使用 XML 文件定义 Spring 的各种 bean 和其依赖关系,是最传统的配置方式。
2. Java 注解配置:通过使用注解(如 `@Component`, `@Service`, `@Repository`, `@Controller`, `@Autowired`, `@Configuration` 等)来标识和配置 beans,减少了XML的使用,提升了可读性。
3. Java 代码配置:使用 Java 类和方法来定义和配置 beans,结合 `@Configuration` 注解,可通过 `@Bean` 注解指定 bean 的创建,这种方式与纯 Java 的编程风格更为一致。
XML 配置:
优点: 方便 直观 代码少(没有配置文件的书写那么复杂)
缺点:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的
Java 注解配置:
优点: 配置和代码是分离的 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
缺点:编写麻烦,效率低,大型项目过于复杂。
解决循环依赖(三级缓存)
在Spring中,循环依赖是指两个或多个bean相互依赖,导致在实例化时出现问题,即A需要B,B又需要A。在此情况下,若使用传统的单例模式进行依赖注入,可能会导致StackOverflowError
或NullPointerException
。为了解决这个问题,Spring引入了三级缓存机制。
三级缓存机制
-
单例池(singletonObjects):存储已完全初始化的 bean 实例。
-
早期单例池(earlySingletonObjects):存储那些已被创建但尚未完全初始化的 bean 实例。这个池用于存放在依赖注入过程中的 bean。
-
所需依赖的 bean 定义(singletonFactories):存储生成早期单例的工厂实例。这是一个
Supplier
接口的实现,可以在需要时创建 bean。
具体实现步骤
- 当 bean A 需要 bean B 时,Spring 会将 bean A 放入 早期单例池 中。
- 然后,Spring 对 bean B 进行初始化,假设 B 又依赖于 A,此时可以从 早期单例池 中获取 A 的引用(代理)。
- 当 bean B 初始化完成后,Spring 再返回到 A 的初始化流程并继续进行注入。
注意的是,Spring 只支持通过构造函数注入的方式处理循环依赖,而如果是通过 setter 方法或字段注入,则可能会出现依赖注入不完全的问题。
一级缓存(singletonObjects):存储已经完全初始化好的单例Bean实例。当一个Bean被成功创建并注入所有依赖后,它会被存放在这里。
二级缓存(earlySingletonObjects):存储正在创建中的单例Bean实例。当一个Bean的实例化过程开始,但还没有完成属性填充和初始化时,它会被存放在这里。这个缓存允许其他Bean引用尚未完全初始化的Bean,从而解决循环依赖问题。
三级缓存(singletonFactories):存储单例Bean工厂对象,用于创建Bean实例。当一个Bean被其他Bean引用,并且需要提前曝光时,Spring会将一个能够创建该Bean实例的工厂对象存放在这里。这个工厂对象可以确保在需要时能够创建Bean的实例,同时支持代理对象的创建,如AOP代理。
Spring Bean
Spring Bean的生命周期
创建过程
1. 首先实例化Bean,并设置Bean的属性。
2. 根据其实现的Aware接口(主要是 BeanFactoryAware接口,BeanFactoryAware, ApplicationContextAware)设置依赖信息。
3. 接下来调用BeanPostProcess的postProcessBeforeInitialization方法,完成initial前的自定义逻 辑;
4. afterPropertiesSet方法做一些属性被设定后的自定义的事情;
5. 调用Bean自身定义的init方法,去做一些初始化相关的工作;
6. 然后再调用postProcessAfterInitialization去做一些bean初始化之后的自定义工作。 这四个方法的 调用有点类似AOP。
7. 此时,Bean初始化完成,可以使用这个Bean了。
销毁过程:
1. 如果实现了DisposableBean的destroy方法,则调用它。
2. 如果实现了自定义的销毁方法,则 调用之。
Spring Bean的作用域
singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
prototype : 每次请求都会创建一个新的 bean 实例。
request: 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经 没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容 器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话
@Autowired的使用
AOP
AOP的基本概念
- 切面(Aspect):关注横切关注点的模块化,例如日志记录、事务管理等。
- 连接点(Join Point):程序执行中一个明确的点,可以在该点添加切面逻辑,例如方法调用、构造函数执行等。
- 通知(Advice):切面在特定连接点执行的代码。可以是前置通知、后置通知、环绕通知等。前置通知 → 2. 环绕通知(进入方法体前) → 3. 目标方法 → 4. 环绕通知(进入方法体后) →返回通知(如果目标方法正常返回)或异常通知(如果目标方法抛出异常) → 6. 后置通知
- 切入点(Pointcut):定义了哪些连接点应该被通知的表达式。
- 目标(Target):被切面所增强的对象。
- 代理(Proxy):AOP通常是通过生成代理对象来实现的,代理会控制对目标对象的调用。
AOP的实现方式
1. 代理模式
-
JDK动态代理:
- 适用于接口实现。
- 利用Java的反射机制,通过实现
InvocationHandler
接口动态生成代理类。 - 只能对接口进行代理,不能对具体类进行代理。
-
CGLIB(Code Generation Library)代理:
- 适用于没有接口的类或需要代理具体类的情况。
- 通过字节码增强的方式,创建目标类的子类来实现代理。
- 相比于JDK动态代理,CGLIB会产生更多的字节码,但能够代理类的方法。
PS:Maven是一个用于Java项目的项目管理和构建自动化工具,主要通过依赖管理和标准化构建流程来提升开发效率。
事务
Spring 管理事务的方式有几种
1. 编程式事务,在代码中硬编码。(难维护,不推荐使用)
2. 声明式事务,在配置文件中配置(推荐使用) 声明式事务又分为两种:
1. 基于XML的声明式事务
2. 基于注解的声明式事务
Spring 事务中的隔离级别
Spring 支持以下四种隔离级别:
-
READ_UNCOMMITTED(读未提交)
允许读取其他事务未提交的更改。这种级别可能导致脏读、不可重复读和幻读。 -
READ_COMMITTED(读已提交)
只允许读取已提交的事务更改。这可以防止脏读,但可能会导致不可重复读和幻读。 -
REPEATABLE_READ(可重复读)
确保在同一事务中多次读取同一数据时返回相同的值,防止了脏读和不可重复读,但可能会导致幻读。 -
SERIALIZABLE(串行化)
是最高的隔离级别,确保事务串行执行,避免脏读、不可重复读和幻读。性能较低,通常不推荐在高并发场景中使用。
如何设置隔离级别
在 Spring 中,可以通过 @Transactional
注解设置隔离级别,例如:
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(isolation = Isolation.READ_COMMITTED)
public void someTransactionalMethod() {
// 业务逻辑
}
Spring 事务失效
1. 异常未捕获
如果在事务中发生未捕获的运行时异常,事务会自动回滚。但如果是受检异常(如 `IOException`),则需要显式回滚。
2. 配置错误
如果未正确配置事务管理器或未启用事务注解,事务可能无法生效。
3. 传播行为
使用 `@Transactional` 时,如果传播行为设置为 `Propagation.NEVER` 或 `Propagation.NOT_SUPPORTED`,事务不会被创建或参与。
4. 自我调用
如果在同一个类内部调用带有 `@Transactional` 的方法,事务不会生效。Spring 的 AOP 代理机制要求通过代理类进行调用。
5. 事务超时
如果事务超时,Spring 会自动回滚事务。可以通过 `@Transactional(timeout = seconds)` 设置超时时间。
6. 未使用 Spring 管理的对象
如果在非 Spring 管理的对象中使用事务,事务不会生效。
7. 数据源问题
如果数据源配置不正确或连接失败,事务也可能无法正常工作。
8. 外部事务管理
如果同一数据库连接被其他框架或代码管理(例如,使用 JDBC 直接操作),可能导致事务失效。