首先,我将简单介绍一下Spring框架中的动态代理和循环依赖问题。
动态代理与循环依赖
1. 动态代理
在Spring框架中,动态代理是一种常用的技术,用于实现AOP(面向切面编程)。动态代理允许Spring在运行时为目标对象创建一个代理,以此来插入额外的逻辑,例如事务管理、日志记录等。
2. 循环依赖
循环依赖是指两个或多个Bean相互依赖,形成闭环,导致无法顺利完成依赖注入。例如,Bean A依赖Bean B,而Bean B又依赖Bean A。
最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。
这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软
解决循环依赖:三级缓存
1. 三级缓存
Spring使用三级缓存解决循环依赖的问题:
- 一级缓存:存放完全初始化完成的Bean(单例池)。
- 二级缓存:存放原始Bean的早期引用。
- 三级缓存:存放Bean的ObjectFactory,用于生成Bean的代理对象。
2. 工作机制
当Spring容器创建Bean时,首先将实例化后的原始Bean放入三级缓存。如果在Bean的完全初始化之前需要引用该Bean,Spring会通过三级缓存中的ObjectFactory来创建Bean的代理对象,并将其提升到二级缓存中。这样,即使在Bean还未完全初始化之前,也能通过代理对象来解决循环依赖的问题。
使用场景与性能优化
1. 使用场景
- 事务管理:使用动态代理管理事务边界。
- 日志记录:在方法执行前后添加日志记录。
- 安全性:在方法调用前进行权限检查。
2. 性能优化
- 减少代理创建:仅对关键服务进行代理,避免过度使用动态代理。
- 懒加载:适当使用懒加载,延迟Bean的初始化。
代码示例
让我们通过一个简单的例子来理解Spring的动态代理和循环依赖的处理:
// 服务接口
public interface UserService {void addUser(String username);
}// 实现类
public class UserServiceImpl implements UserService {@Autowiredprivate OrderService orderService; // 依赖OrderService@Overridepublic void addUser(String username) {System.out.println("Adding user: " + username);// ... 其他逻辑 ...}
}// 另一个服务
public class OrderService {@Autowiredprivate UserService userService; // 依赖UserServicepublic void createOrder(String username) {System.out.println("Creating order for: " + username);// ... 其他逻辑 ...}
}
在这个例子中,UserService
和 OrderService
互相依赖。Spring通过三级缓存机制,确保这种循环依赖不会导致问题。
Spring中三级缓存机制是如何工作的,特别是在解决循环依赖的情况下。
我会用一个稍微复杂点的例子来展示这一机制。
场景设定
假设我们有两个组件,A
和 B
,它们互相依赖。为了简化示例,我们假设它们都是单例(Spring默认的作用域)。
@Component
public class A {@Autowiredprivate B b;public A() {System.out.println("A 创建");}// A的其他方法...
}@Component
public class B {@Autowiredprivate A a;public B() {System.out.println("B 创建");}// B的其他方法...
}
三级缓存的工作原理
实例化:
当Spring容器启动时,它会尝试创建这些Bean。
首先,它创建了A的实例。在这个过程中,Spring发现A需要依赖B。
然后,它开始创建B的实例。同样,在创建B时,发现B需要依赖A。
三级缓存介入:
此时,A的实例已经被部分创建,并存放在三级缓存中。
当Spring为B创建依赖A时,它不会重新创建A的实例。相反,它会使用存在于三级缓存中的A的早期引用。
这个早期引用足以满足B对A的依赖,从而允许B的创建过程继续。
依赖注入与完成创建:
一旦B被成功创建,Spring会完成对A的依赖注入。
这时,A和B都已经被完全创建,并存放在Spring的一级缓存(即单例池)中。
关键点
这个过程中的“早期引用”通常是通过使用ObjectFactory创建的代理对象。
三级缓存主要用于解决这种循环依赖的问题,同时也确保了Spring容器的线程安全。
注意事项
虽然三级缓存机制很强大,但它仅适用于单例作用域的Bean。
对于原型作用域的Bean,Spring不会尝试解决循环依赖,这可能会导致BeanCurrentlyInCreationException异常。
推荐一个学习 Spring源码分析 的专栏文章
- 01、Spring源码分析 - 01-DispatcherServlet注册过程
- 02、Spring源码分析 - 02-Resource
- 03、Spring源码分析 - 03-ResourceLoader
- 04、Spring源码分析 - 04-类型转换
- 05、Spring源码分析 - 05-字段格式化
- 06、Spring源码分析 - 06-ResolvableType
- 07、Spring源码分析 - 07-BeanWrapper
- 08、Spring源码分析 - 08-DataBinder
- 09、Spring源码分析 - 09-PropertySourcesPropertyResolver
- 10、Spring源码分析 - 10-Environment
- 11、Spring源码分析 - 11-BeanFactory的实现
- 12、Spring源码分析 - 12-BeanFactory创建Bean的重要流程图
- 13、Spring源码分析 - 13-DispatcherServlet中WebApplicationContext启动过程
- 14、Spring源码分析 - 14-Spring默认重要的组件
- 15、Spring源码分析 - 15-ConfigurationClassPostProcessor
- 16、Spring源码分析 - 16-AutowiredAnnotationBeanPostProcessor
- 17、Spring源码分析 - 17-RequiredAnnotationBeanPostProcessor
- 18、Spring源码分析 - 18-CommonAnnotationBeanPostProcessor
- 19、Spring源码分析 - 19-ConfigurationClassPostProcessor
- 20、Spring源码分析 - 20-Spring事件/监听器机制
- 21、Spring源码分析 - 21-Spring AOP概述
- 22、Spring源码分析 - 22-Spring AOP的实现原理之ProxyFactoryBean
- 23、Spring源码分析 - 23-TargetSource目标源
- 24、Spring源码分析 - 24-基于注解@Aspect的AOP实现
- 25、Spring源码分析 - 25-Spring异步实现原理
- 26、Spring源码分析 - 26-TaskExecutor与TaskScheduler
- 27、Spring源码分析 - 27-基于注解@Scheduled定时任务实现
- 28、Spring源码分析 - 28-Spring缓存原理详解
- 29、Spring源码分析 - 29-JdbcTemplat的设计与实现
- 30、Spring源码分析 - 30-Spring编程式事物的设计与实现
- 31、Spring源码分析 - 31-Spring声明式事物的设计与实现
- 32、Spring源码分析 - 32-基于注解@Transactional的事物实现
- 33、Spring源码分析 - 32-基于注解@Transactional的事物实现
- 34、Spring源码分析 - 34-Spring Bean作用域的设计与实现
- 35、Spring源码分析 - 35-Spring MVC设计原理
- 36、Spring源码分析 - 36-Spring MVC参数值的绑定
- 37、Spring源码分析 - 37-Spring MVC的异常处理
- 38、Spring源码分析 - 38-RestTemplate详解
- 39、Spring源码分析 - 39-Spring容器生命周期回调接口LifeCycle
- 40、Spring源码分析 - 40-Spring Validation参数校验的使用与原理
- 41、Spring源码分析 - 41-ClassPathBeanDefinitionScanner
- 42、Spring源码分析 - 42-@Conditional详解
真实案例比较
在实际开发中,我们通常会遇到以下几种情况:
- 简单的CRUD操作:通常不需要动态代理。
- 复杂的业务逻辑:涉及事务、安全性检查时,动态代理非常有用。
- 高性能要求的场景:在这种场景下,过度使用动态代理
最后说一句(求关注,求赞,别白嫖我)
最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。
这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软
项目文档&视频:
项目文档 & 视频
本文,已收录于,我的技术网站 ddkk.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享
求一键三连:点赞、分享、收藏
点赞对我真的非常重要!在线求赞,加个关注我会非常感激