From f2b24072b5e79bf23190359bfe11d2ae0fa1501a Mon Sep 17 00:00:00 2001 From: Guide Date: Wed, 21 Aug 2024 12:26:42 +0800 Subject: [PATCH] [docs updete]typo --- .../mysql/mysql-query-execution-plan.md | 6 +- .../java-concurrent-questions-02.md | 2 - .../spring-knowledge-and-questions-summary.md | 81 ++++++++++++++++++- 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/docs/database/mysql/mysql-query-execution-plan.md b/docs/database/mysql/mysql-query-execution-plan.md index 48eb5a1e..8739ba0d 100644 --- a/docs/database/mysql/mysql-query-execution-plan.md +++ b/docs/database/mysql/mysql-query-execution-plan.md @@ -69,7 +69,7 @@ mysql> explain SELECT * FROM dept_emp WHERE emp_no IN (SELECT emp_no FROM dept_e ### id -SELECT 标识符,是查询中 SELECT 的序号,用来标识整个查询中 SELELCT 语句的顺序。 +`SELECT` 标识符,用于标识每个 `SELECT` 语句的执行顺序。 id 如果相同,从上往下依次执行。id 不同,id 值越大,执行优先级越高,如果行引用其他行的并集结果,则该值可以为 NULL。 @@ -94,7 +94,9 @@ id 如果相同,从上往下依次执行。id 不同,id 值越大,执行 ### type(重要) -查询执行的类型,描述了查询是如何执行的。所有值的顺序从最优到最差排序为:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL +查询执行的类型,描述了查询是如何执行的。所有值的顺序从最优到最差排序为: + +system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 常见的几种类型具体含义如下: diff --git a/docs/java/concurrent/java-concurrent-questions-02.md b/docs/java/concurrent/java-concurrent-questions-02.md index b5edec98..3097b2ff 100644 --- a/docs/java/concurrent/java-concurrent-questions-02.md +++ b/docs/java/concurrent/java-concurrent-questions-02.md @@ -423,8 +423,6 @@ CAS 经常会用到自旋操作来进行重试,也就是不成功就一直循 #### 只能保证一个共享变量的原子操作 -CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5 开始,提供了`AtomicReference`类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference`类把多个共享变量合并成一个共享变量来操作。 - CAS 操作仅能对单个共享变量有效。当需要操作多个共享变量时,CAS 就显得无能为力。不过,从 JDK 1.5 开始,Java 提供了`AtomicReference`类,这使得我们能够保证引用对象之间的原子性。通过将多个变量封装在一个对象中,我们可以使用`AtomicReference`来执行 CAS 操作。 除了 `AtomicReference` 这种方式之外,还可以利用加锁来保证。 diff --git a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md index 667c848f..edd21080 100644 --- a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md +++ b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md @@ -279,6 +279,78 @@ private SmsService smsService; - 当一个接口存在多个实现类的情况下,`@Autowired` 和`@Resource`都需要通过名称才能正确匹配到对应的 Bean。`Autowired` 可以通过 `@Qualifier` 注解来显式指定名称,`@Resource`可以通过 `name` 属性来显式指定名称。 - `@Autowired` 支持在构造函数、方法、字段和参数上使用。`@Resource` 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。 +### 注入 Bean 的方式有哪些? + +依赖注入 (Dependency Injection, DI) 的常见方式: + +1. 构造函数注入:通过类的构造函数来注入依赖项。 +1. Setter 注入:通过类的 Setter 方法来注入依赖项。 +1. Field(字段) 注入:直接在类的字段上使用注解(如 `@Autowired` 或 `@Resource`)来注入依赖项。 + +构造函数注入示例: + +```java +@Service +public class UserService { + + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + //... +} +``` + +Setter 注入示例: + +```java +@Service +public class UserService { + + private UserRepository userRepository; + + // 在 Spring 4.3 及以后的版本,特定情况下 @Autowired 可以省略不写 + @Autowired + public void setUserRepository(UserRepository userRepository) { + this.userRepository = userRepository; + } + + //... +} +``` + +Field 注入示例: + +```java +@Service +public class UserService { + + @Autowired + private UserRepository userRepository; + + //... +} +``` + +### 构造函数注入还是 Setter 注入? + +Spring 官方有对这个问题的回答:。 + +我这里主要提取总结完善一下 Spring 官方的建议。 + +**Spring 官方推荐构造函数注入**,这种注入方式的优势如下: + +1. 依赖完整性:确保所有必需依赖在对象创建时就被注入,避免了空指针异常的风险。 +2. 不可变性:有助于创建不可变对象,提高了线程安全性。 +3. 初始化保证:组件在使用前已完全初始化,减少了潜在的错误。 +4. 测试便利性:在单元测试中,可以直接通过构造函数传入模拟的依赖项,而不必依赖 Spring 容器进行注入。 + +构造函数注入适合处理**必需的依赖项**,而 **Setter 注入** 则更适合**可选的依赖项**,这些依赖项可以有默认值或在对象生命周期中动态设置。虽然 `@Autowired` 可以用于 Setter 方法来处理必需的依赖项,但构造函数注入仍然是更好的选择。 + +在某些情况下(例如第三方类不提供 Setter 方法),构造函数注入可能是**唯一的选择**。 + ### Bean 的作用域有哪些? Spring 中 Bean 的作用域通常有下面几种: @@ -802,7 +874,7 @@ class B { `@Lazy` 用来标识类是否需要懒加载/延迟加载,可以作用在类上、方法上、构造器上、方法参数上、成员变量中。 -Spring Boot 2.2 新增了全局懒加载属性,开启后全局 bean 被设置为懒加载,需要时再去创建。 +Spring Boot 2.2 新增了**全局懒加载属性**,开启后全局 bean 被设置为懒加载,需要时再去创建。 配置文件配置全局懒加载: @@ -829,11 +901,12 @@ springApplication.run(args); - 由于在 A 上标注了 `@Lazy` 注解,因此 Spring 会去创建一个 B 的代理对象,将这个代理对象注入到 A 中的 B 属性; - 之后开始执行 B 的实例化、初始化,在注入 B 中的 A 属性时,此时 A 已经创建完毕了,就可以将 A 给注入进去。 -通过 `@Lazy` 就解决了循环依赖的注入, 关键点就在于对 A 中的属性 B 进行注入时,注入的是 B 的代理对象,因此不会循环依赖。 +从上面的加载流程可以看出: `@Lazy` 解决循环依赖的关键点在于代理对象的使用。 -之前说的发生循环依赖是因为在对 A 中的属性 B 进行注入时,注入的是 B 对象,此时又会去初始化 B 对象,发现 B 又依赖了 A,因此才导致的循环依赖。 +- **没有 `@Lazy` 的情况下**:在 Spring 容器初始化 `A` 时会立即尝试创建 `B`,而在创建 `B` 的过程中又会尝试创建 `A`,最终导致循环依赖(即无限递归,最终抛出异常)。 +- **使用 `@Lazy` 的情况下**:Spring 不会立即创建 `B`,而是会注入一个 `B` 的代理对象。由于此时 `B` 仍未被真正初始化,`A` 的初始化可以顺利完成。等到 `A` 实例实际调用 `B` 的方法时,代理对象才会触发 `B` 的真正初始化。 -一般是不建议使用循环依赖的,但是如果项目比较复杂,可以使用 `@Lazy` 解决一部分循环依赖的问题。 +`@Lazy` 能够在一定程度上打破循环依赖链,允许 Spring 容器顺利地完成 Bean 的创建和注入。但这并不是一个根本性的解决方案,尤其是在构造函数注入、复杂的多级依赖等场景中,`@Lazy` 无法有效地解决问题。因此,最佳实践仍然是尽量避免设计上的循环依赖。 ### SpringBoot 允许循环依赖发生么?