假设我有如下 Bean:
- 就这样一个简单的循环依赖,默认情况下,A 会被先加载,然后在 A 中做属性填充的时候,去创建了 B,创建 B 的时候又需要 A,之后呢?
sequenceDiagram participant A创建过程 participant 缓存 participant B创建过程 A创建过程->>缓存: 检查A实例 alt A实例不存在 缓存->>A创建过程: 放入A的工厂对象至三级缓存 end A创建过程->>缓存: 检查B实例 alt B实例不存在 缓存->>A创建过程: 放入B的工厂对象至三级缓存 end A创建过程->>B创建过程: 创建B实例 B创建过程->>缓存: 检查A实例 alt A实例不存在 缓存->>B创建过程: 使用A的工厂对象创建尚未完全初始化的A实例 B创建过程->>缓存: 将尚未完全初始化的A实例放入二级缓存 end B创建过程->>A创建过程: 将尚未完全初始化的A实例注入到B中 A创建过程->>B创建过程: 完成B实例化并放入一级缓存 B创建过程->>A创建过程: 使用已实例化的B完成A实例化 A创建过程->>缓存: 将A实例放入一级缓存
-
What can we learn from the fact spring use three-level cache system to solve circular dependency problems?
-
Design Patterns: Spring’s approach to handling circular dependencies demonstrates the effective use of design patterns, such as the Factory pattern and Proxy pattern. By using these patterns, Spring can create and manage objects in a more flexible and efficient manner, making it easier to handle complex scenarios like circular dependencies.
-
Separation of Concerns: The three-level cache system in Spring separates the concerns of object creation, dependency management, and object storage. This separation allows Spring to handle each aspect independently and in a more organized manner, making the overall system more maintainable and scalable.
-
Fault Tolerance: By using a three-level cache system, Spring can detect and handle circular dependencies at runtime, preventing potential issues and crashes that could arise from such dependencies.
-
Optimization: The caching mechanism in Spring helps to optimize the process of object creation and dependency resolution. By storing partially and fully resolved objects in different caches, Spring can avoid unnecessary object creation and redundant operations, improving the overall performance of the system.
-
Best Practices: While Spring provides a solution for circular dependencies, it’s important to remember that the best practice is to avoid circular dependencies altogether. This can be achieved by using techniques like constructor injection, lazy loading, and code restructuring. The fact that Spring can handle circular dependencies serves as a reminder to developers to be mindful of their code design and to strive for clean, modular, and well-structured code.
-
-
Why can we use constructor injection and lazy loading to solve the circular dependencies? Let’s take an example of two classes
ClassA
andClassB
with circular dependencies:In this example, if we try to create an instance of
ClassA
using constructor injection, we’ll need an instance ofClassB
. But to create an instance ofClassB
, we’ll need an instance ofClassA
. This circular dependency will cause issues during object creation. By the way, we recommend using constructor injection for beans because it can help us discover issues with circular dependencies.To resolve this circular dependency using constructor injection, we can introduce lazy loading using Java
Supplier
:Now, instead of directly injecting the instances of
ClassA
andClassB
, we injectSupplier
instances that can provide the required objects on demand. This breaks the circularity during object creation and allows us to create instances of both classes:By combining constructor injection with lazy loading using
Supplier
, we can resolve the circular dependency betweenClassA
andClassB
. However, it’s important to note that this approach may not be suitable for all scenarios, and it’s best to avoid circular dependencies whenever possible.
记一个隐秘的循环依赖:自定义AOP(非Spring AOP)如果没实现 Spring 解决循环依赖接口 getFactoryBeanReference ,当该类与其他普通类循环依赖时Spring 会启动失败。