1. Java 中的异常分类

Java 中的异常分为以下三类:


2. Java 中的访问修饰符

Java 提供了四种访问修饰符来控制类、方法和变量的访问范围:

修饰符 同类 同包 子类 其他包
public
protected
默认(无修饰符)
private

注意:Java 中没有名为 default 的访问修饰符。当不指定修饰符时,采用默认的包级访问权限。


3. Spring Bean 的生命周期

Spring 管理的 Bean 在容器中有一系列生命周期过程:

  1. 实例化 Bean
    • 通过构造函数创建 Bean 实例。
  2. 依赖注入
    • 通过 setter 方法或注解(如 @Autowired)进行依赖注入。
  3. 处理 Aware 接口
    • 如果 Bean 实现了某些 Aware 接口(如 BeanNameAwareApplicationContextAware),Spring 会在此阶段调用它们的方法,让 Bean 获取 Spring 容器相关信息。
  4. 执行 BeanPostProcessor 前置处理器
    • 在 Bean 初始化方法之前调用,可以用于修改 Bean 实例。
  5. 调用初始化方法
    • 如果 Bean 实现了 InitializingBean 接口或配置了自定义的 init-method,Spring 会调用这些方法进行初始化。
  6. 执行 BeanPostProcessor 后置处理器
    • 在初始化方法之后调用,可能在此阶段为 Bean 创建代理对象。
  7. 使用 Bean
  8. 销毁 Bean
    • 如果实现了 DisposableBean 接口或配置了自定义 destroy-method,Spring 会在容器关闭时调用这些方法进行清理。

4. Aware 接口的作用

Spring 提供了一系列 \*Aware 接口,让 Bean 可以与 Spring 容器交互,获得容器的一些上下文信息。

常见的 Aware 接口包括:


5. 什么是 BeanDefinition?

BeanDefinition 是 Spring 用来描述 Bean 的定义信息的核心接口。它包含了关于 Bean 的所有元信息,如:

Spring 容器在启动时,会根据配置文件或注解生成 BeanDefinition,并在需要时根据这些定义信息创建和管理 Bean 实例。

示例:BeanDefinition 的常见来源


6. Spring 的事务管理

Spring 提供了强大的事务管理支持,主要有以下两种方式:

  1. 编程式事务管理
    • 使用 TransactionTemplate 或底层事务 API 明确地在代码中管理事务。
    • 优点:灵活性高。
    • 缺点:对业务代码有侵入性,代码可读性差。
  2. 声明式事务管理(推荐)
    • 基于 AOP,通过注解(如 @Transactional)声明事务规则,Spring 在运行时自动管理事务。
    • 优点:代码简洁,易于维护。
    • 缺点:灵活性稍差,但对于大多数场景已经足够。

事务失效场景


7. Service 层方法的访问修饰符


8. 代理模式与 AOP


9. Java 中的 BLOB 和 CLOB


10. 什么是版本链(Version Chain)?

InnoDB 存储引擎中,多版本并发控制(MVCC) 是通过 版本链 来实现的。
每行数据都包含多个版本,每个版本对应一次修改操作。版本链中的数据可以帮助实现事务隔离级别(如 READ COMMITTEDREPEATABLE READ),从而避免幻读和脏读等问题。

版本链中的重要字段:

  1. trx_id(事务ID):表示创建或修改该版本的事务ID。
  2. undo log:记录了每次数据更新前的旧值,构成一个链表,称为版本链。
  3. Roll Pointer:指向上一个版本的数据,实现数据的版本回溯。

11. ReadView 介绍

ReadView 是在事务执行时生成的一种快照,它记录了在生成快照时,系统中哪些事务是活跃的(未提交)。ReadView 用于判断一个数据版本是否对当前事务可见。

ReadView 主要字段:


12. 版本链数据访问规则

当事务读取某行数据时,InnoDB 会根据 ReadView 的规则判断该行数据的版本是否对当前事务可见。判断规则如下:

  1. trx_id < min_trx_id
    • 当前数据版本在生成 ReadView 之前已经提交,可以访问。
    • 这是最简单的情况,说明数据是历史版本,事务生成 ReadView 时已经存在且提交。
  2. trx_id > max_trx_id
    • 当前数据版本对应的事务在生成 ReadView 之后才开启,不可访问。
    • 解释: 这是一个未开始或未提交的版本,当前事务不应看到它。
  3. min_trx_id <= trx_id <= max_trx_id
    • 如果 trx_idm_ids 列表中,说明该版本对应的事务仍未提交,不允许访问。
    • 如果 trx_id 不在 m_ids 中,说明事务已经提交,可以访问。

13. 针对问题的详细解释

问题 1(第3条规则解释)

判断 trx_id > max_trx_id,成立说明该事务是在 ReadView 生成以后才开启,不允许访问。

解释:
在事务生成 ReadView 的瞬间,max_trx_id 记录了当前系统中的下一个事务 ID。如果某个数据版本的 trx_id 大于 max_trx_id,说明这个事务在 ReadView 生成后才开始,数据版本对当前事务不可见。

示例:

  1. 系统当前的事务情况:
    • min_trx_id = 2
    • max_trx_id = 5
    • m_ids = [2, 3, 4]
  2. 事务操作顺序:
    1. 事务 A 开始并生成 ReadView。
    2. 事务 B(trx_id = 6)在 ReadView 生成后开始并修改了某条数据。
  3. 判断数据版本的可见性:
    • 如果数据版本的 trx_id = 6,显然它大于 max_trx_id = 5,说明该数据版本是事务 B 创建的。
    • 因为事务 B 在事务 A 的 ReadView 生成后才开启,因此事务 A 无法看到这个版本。

问题 2(第4条规则解释)

判断 min_trx_id <= trx_id <= max_trx_id,如果 trx_id 不在 m_ids 列表中,代表数据是已提交的,可以访问。

解释:
在 ReadView 生成时,m_ids 记录了所有活跃事务的 ID。如果某个数据版本的 trx_idm_ids 中,说明该事务仍未提交,当前事务无法访问。反之,如果 trx_id 不在 m_ids 中,说明事务已经提交,数据对当前事务可见。

示例:

  1. 系统当前的事务情况:
    • min_trx_id = 2
    • max_trx_id = 5
    • m_ids = [2, 3, 4]
  2. 事务操作顺序:
    1. 事务 A 开始并生成 ReadView。
    2. 事务 C(trx_id = 5)已经提交并修改了某条数据。
  3. 判断数据版本的可见性:
    • 数据版本的 trx_id = 5,它在 min_trx_idmax_trx_id 之间。
    • trx_id = 5 不在 m_ids = [2, 3, 4] 中,说明事务 C 已经提交,数据版本对事务 A 可见。

问题 3:undolog 记录的时机

undo log 是在事务开始时就记录操作,还是等到实际操作时才记录?

解释:
undo log 是在 数据发生修改时 记录的,不是在事务开始时就立即生成。每当事务对数据进行修改,InnoDB 会将修改前的旧值写入 undo log,从而形成一个回滚链条,方便事务回滚或多版本读取。