Spring IoC 的工作流程:
- 读取 BeanDefinition: Spring 容器启动时,会读取 Bean 的配置信息 (例如 XML 配置文件、注解或 Java 代码),并将这些配置信息转换为
BeanDefinition
对象。 - 创建 Bean 实例: 根据
BeanDefinition
中的信息,Spring 容器使用反射机制创建 Bean 的实例。 - 解析依赖关系: Spring 容器解析 Bean 的依赖关系,找到 Bean 所依赖的其他 Bean。
- 注入依赖: Spring 容器使用依赖注入的方式,将依赖对象注入到 Bean 中。
- Bean 的生命周期管理: Spring 容器负责管理 Bean 的生命周期,包括初始化、使用和销毁。
自动装配的核心概念:
-
@EnableAutoConfiguration
:- 这是一个复合注解,通常放在 Spring Boot 应用的启动类上。
- 它启用了 Spring Boot 的自动配置机制。
- 它实际上包含了
@AutoConfigurationPackage
和@Import(AutoConfigurationImportSelector.class)
两个注解。
-
@AutoConfigurationPackage
:- 它用于指定自动配置的基础包。
- Spring Boot 会扫描该包及其子包下的所有组件 (例如
@Component
,@Service
,@Repository
,@Controller
等),并将它们注册为 Bean。 - 通常情况下,
@AutoConfigurationPackage
会自动扫描启动类所在的包作为基础包。
-
AutoConfigurationImportSelector
:- 它是自动配置的核心组件。
- 它负责扫描所有符合自动配置条件的类,并将它们导入到 Spring 容器中。
- 它主要通过以下几个步骤来实现自动配置:
- 扫描
META-INF/spring.factories
文件:AutoConfigurationImportSelector
会扫描所有 jar 包中的META-INF/spring.factories
文件。 - 加载自动配置类:
spring.factories
文件中定义了大量的自动配置类,AutoConfigurationImportSelector
会加载这些类。 - 条件过滤:
AutoConfigurationImportSelector
会根据一定的条件对自动配置类进行过滤,只有满足条件的自动配置类才会被导入到 Spring 容器中。 - 导入自动配置类:
AutoConfigurationImportSelector
使用@Import
注解将符合条件的自动配置类导入到 Spring 容器中。
- 扫描
-
条件注解 (Conditional Annotations):
- Spring Boot 提供了大量的条件注解,用于控制自动配置类的生效条件。
MyBatis中一级缓存和二级缓存有什么差别?
1. 一级缓存 (Local Cache):
- 作用域: SqlSession 级别。
- 生命周期: 与 SqlSession 的生命周期相同。 当 SqlSession 关闭时,一级缓存也会被清空。
- 存储介质: 内存。
- 工作原理:
- 当执行一个查询语句时,MyBatis 首先会从一级缓存中查找是否存在相同的 SQL 语句和参数。
- 如果存在,则直接从一级缓存中返回结果,避免访问数据库。
- 如果不存在,则访问数据库,并将查询结果放入一级缓存中。
- 当执行更新、插入或删除语句时,MyBatis 会清空一级缓存,以保证数据的一致性。
- 优点:
- 提高查询性能,减少数据库访问次数。
- 实现简单,无需额外配置。
- 缺点:
- 缓存范围小,只能在单个 SqlSession 中共享。
- 并发性差,多个 SqlSession 之间无法共享缓存。
- 默认开启: 一级缓存默认开启,无需手动配置。
- 清空时机:
- 执行更新、插入或删除语句时。
- 手动调用
SqlSession.clearCache()
方法时。 - SqlSession 关闭时。
2. 二级缓存 (Second Level Cache):
- 作用域: Mapper 级别 (namespace 级别)。
- 生命周期: 与应用程序的生命周期相同。
- 存储介质: 可以配置为内存、磁盘或其他存储介质。
- 工作原理:
- 当执行一个查询语句时,MyBatis 首先会从二级缓存中查找是否存在相同的 SQL 语句和参数。
- 如果存在,则直接从二级缓存中返回结果,避免访问数据库。
- 如果不存在,则访问数据库,并将查询结果放入二级缓存中。
- 当执行更新、插入或删除语句时,MyBatis 会清空二级缓存中与该语句相关的缓存区域,以保证数据的一致性。
- 优点:
- 提高查询性能,减少数据库访问次数。
- 缓存范围大,可以在多个 SqlSession 之间共享。
- 并发性好,多个 SqlSession 可以同时访问二级缓存。
- 缺点:
- 实现复杂,需要手动配置。
- 数据一致性难以保证,需要谨慎使用。
- 默认关闭: 二级缓存默认关闭,需要手动配置才能开启。
- 清空时机:
- 执行更新、插入或删除语句时,会清空与该语句相关的缓存区域。
- 手动配置缓存刷新策略。
- 应用程序关闭时。
当使用 new
关键字创建一个对象时,JVM 会执行以下步骤:
-
加载类: 如果
MyClass
类还没有被加载,JVM 会使用类加载器 (ClassLoader) 将MyClass.class
文件加载到内存中。 -
分配内存: JVM 会在堆 (Heap) 内存中为新对象分配内存空间。 内存分配的方式取决于堆的实现和垃圾回收器的策略。
-
初始化零值: JVM 会将分配到的内存空间初始化为零值。 这意味着所有的实例变量都会被赋予默认值:
int
->0
double
->0.0
boolean
->false
Object
->null
-
设置对象头: JVM 会设置对象的对象头 (Object Header)。 对象头包含以下信息:
- Mark Word: 存储对象的哈希码、GC 分代年龄、锁状态标志等。
- Klass Pointer: 指向对象所属的类 (Class) 的指针。
-
执行构造方法: JVM 会调用对象的构造方法 (Constructor) 来初始化对象的实例变量。构造方法会根据代码中的逻辑给实例变量赋值。
-
返回对象引用: JVM 会将新创建对象的引用返回给调用者。
3. 内存分配方式
JVM 在堆中分配内存的方式有两种:
-
指针碰撞 (Bump the Pointer): 适用于堆内存是规整的情况,即所有用过的内存都放在一边,空闲的内存放在另一边。 JVM 只需要将指针向空闲内存方向移动一段与对象大小相等的距离,即可完成内存分配。
-
空闲列表 (Free List): 适用于堆内存是不规整的情况,即用过的内存和空闲的内存相互交错。 JVM 维护一个空闲列表,记录了所有可用的空闲内存块。 在分配内存时,JVM 会从空闲列表中找到一块足够大的内存块,并将其分配给对象。
4. 对象头的结构
对象头 (Object Header) 是 JVM 中每个对象都必须包含的信息,它存储了对象的元数据。 对象头的结构如下:
- Mark Word (8 字节): 存储对象的哈希码、GC 分代年龄、锁状态标志等。 Mark Word 的结构会根据对象的状态而变化。
- Klass Pointer (4 字节或 8 字节): 指向对象所属的类 (Class) 的指针。 如果 JVM 开启了指针压缩 (Compressed Oops),则 Klass Pointer 占用 4 字节,否则占用 8 字节。
- 数组长度 (4 字节): 只有数组对象才有这个字段,用于记录数组的长度。
5. 对象创建的优化
JVM 会对对象创建进行一些优化:
- TLAB (Thread-Local Allocation Buffer): 为每个线程分配一个私有的 TLAB,用于加速对象的分配。 线程可以在自己的 TLAB 中分配对象,而无需进行同步。
- 逃逸分析: 分析对象的生命周期,如果对象只在方法内部使用,没有逃逸到方法外部,则可以将对象分配在栈上,而不是堆上。 这可以减少垃圾回收的压力。
虽然 ArrayList<Integer>
实现了 List<Integer>
接口,但是 ArrayList<ArrayList<Integer>>
不是 List<List<Integer>>
的子类型 (因为 Java 泛型是不变的)。
我们需要 ArrayList<List<Integer>>
是因为我们要创建一个 List<List<Integer>>
类型的对象,而 ArrayList
是 List
接口的一个常见的实现类。
Spring Boot 的启动过程
Spring Boot 的启动过程可以概括为以下几个步骤:
- 创建 SpringApplication 对象: Spring Boot 应用的入口类通常会调用
SpringApplication.run()
方法来启动应用。SpringApplication
对象负责初始化 Spring Boot 的运行环境。 - 设置运行环境:
SpringApplication
会根据 classpath 中的依赖、配置文件等信息,设置运行环境,例如设置应用上下文、设置 Banner 等。 - 加载配置:
SpringApplication
会加载配置文件 (例如application.properties
或application.yml
),并将配置信息绑定到 Spring Bean 上。 - 创建应用上下文 (ApplicationContext):
SpringApplication
会创建一个ApplicationContext
对象,作为 Spring IoC 容器。 - 注册 BeanDefinition: Spring 会扫描 classpath 中的组件 (例如
@Component
、@Service
、@Controller
等),并将它们注册为 BeanDefinition,放入 IoC 容器中。 - 启动嵌入式 Web 容器 (如果需要): 如果应用是 Web 应用,Spring Boot 会启动嵌入式的 Web 容器 (例如 Tomcat、Jetty、Undertow)。
- 执行 ApplicationRunner 和 CommandLineRunner: Spring Boot 会执行所有实现了
ApplicationRunner
和CommandLineRunner
接口的 Bean,用于执行一些初始化操作。 - 应用启动完成: Spring Boot 应用启动完成,开始监听请求。
今天亚信问到JDBC查询过程,不记得了:
1. 加载 JDBC 驱动程序
-
目的: 将特定数据库的 JDBC 驱动程序加载到 JVM 中,以便 Java 程序能够与该数据库建立连接。
-
方法:
- Class.forName() 方法 : 使用
Class.forName("com.mysql.cj.jdbc.Driver")
加载 MySQL 驱动程序。com.mysql.cj.jdbc.Driver
是 MySQL 驱动程序的类名,不同的数据库有不同的驱动程序类名。 这种方式的优点是可以在程序中动态加载驱动,而不需要在编译时就确定。
- Class.forName() 方法 : 使用
-
原理:
Class.forName()
会执行指定类的静态初始化块,JDBC 驱动程序在静态初始化块中会将自身注册到DriverManager
中。
2. 建立数据库连接
-
目的: 使用
DriverManager
创建一个到数据库的连接。
3. 创建 Statement 对象
-
-
目的: 创建一个
Statement
对象,用于执行 SQL 语句。 -
方法: 调用
Connection
对象的createStatement()
方法。Statement
是最基本的 SQL 执行器,适用于执行简单的 SQL 语句。PreparedStatement
是Statement
的子接口,预编译 SQL 语句,可以提高执行效率,并防止 SQL 注入攻击 (推荐使用)。CallableStatement
用于执行存储过程。
-
4. 执行 SQL 查询
-
目的: 使用
Statement
对象执行 SQL 查询语句。 -
方法: 调用
Statement
对象的executeQuery(sql)
方法。sql
: SQL 查询语句,例如:"SELECT * FROM users"
executeQuery()
方法返回一个ResultSet
对象,包含查询结果
5. 处理查询结果 (ResultSet)
-
目的: 从
ResultSet
对象中提取查询结果。
6. 关闭资源
-
目的: 释放数据库连接和相关的资源,防止资源泄漏。