SpringBoot源码角度深入浅出IOC依赖注入过程
前置知识:下面先用一个从ApplicationContext中getBean的一个案例熟悉一下
用OkHttpClient注入到了ICO容器中并获取为例
打断点进来发现,如果是无参构造的话则会执行if里面的,


protected <T> T doGetBean(String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// 1. 解析 beanName(去掉别名,转为真正的名字)
String beanName = this.transformedBeanName(name);
// 2. 尝试从单例池里拿(一级缓存)
Object sharedInstance = this.getSingleton(beanName);
Object beanInstance;
if (sharedInstance != null && args == null) {
// 说明这个 Bean 已经创建过了(缓存命中)
// 如果是循环依赖提前暴露的 Bean,这里会打印 trace 日志
if (this.logger.isTraceEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.trace("Returning eagerly cached instance of singleton bean '"
+ beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 3. 如果是 FactoryBean,还需要处理 getObject() 包装
beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
}
}可以发现sharedInstance和beanInstance指向了同一个地址

如果下次进行getBean的时候,并且这个Bean对象设置原型模式,就会新疆一个Bean,否则则直接返回原来的对象
Ioc容器加载过程:
1. new ApplicationContext(配置类/xml);
@SpringBootApplication
@Configurable
public class Main {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Main.class);
Object bean = applicationContext.getBean("IService");
}
}
//这里进行new一个ConfigurableApplicationContext
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
}2. 解析配置--->扫描包--->bean的定义信息读取进来--->BeanDefinition-->map
BeanDefinition = 把类的配置信息登记到 Spring 容器(BeanFactory)的“元数据字典”里,方便后续创建 Bean。
BeanDefinition,里面写着:
- beanClass = UserService
- scope = singleton
- lazyInit = false
3.循环一个个创建bean--->条件判断--->懒加载的bean\多例Bean\抽象bean
条件判断的时候会判断是否是单例Bean,是单例Bean的话进行一个Bean对象的创建,因为
BeanFactory#getBean
4. 通过beanfactory.getBean创建bean对象
创建之前先获取
5.实例化 -> 在ApplicationContext中,通过调用getBean(Spring boot中是通过BeanFactory拿到)拿到BeanDifinitionMap通过name拿到这个Bean的反射射new AAService()
6.DI 属性注入 (@autowrited 属性注入是进行注入一些属性,比如拿到字段上含有@autowrite注解的字段,进行getBean获取到后通过反射设置进去。)
7.0 初始化前 BeanPostProserssor.postProcessBeforeInitialization
7.初始化 (初始化后才算完整的Bean对象,回调一些方法)-> 如果你先调用自己的初始化方法,或者其他回调方法,都是在这里进行调用的,因为没有初始化的时候这个类的属性都没注入进来,自然而然也无法进行调用其他方法。回掉方法可以使用@Construct注解或者implemnt BeanPostProcessor重写里面的回调方法来实现。
7.1初始化后 -->postProcessAfterInitialization--> AOP --> 动态代理对象

查看一下BeanPostProserssorInitialization实现类 AbstractAutoProxyCreator#postProcessAfterInitialization

继续往下看里面的创建代理对象的方法,这个实现原理实际上就是根据AspectJ中的切点表达式,进行匹配,比如这个切点@Pointcut里面的表达式,如果当前的类满足切点表达式的话,则可以执行if中的逻辑,进行执行createProxy创建一个代理对象
@Aspect
@Component
public class LogAspect {
// 切点表达式:拦截 com.example.service 包下所有类的所有方法
@Pointcut("execution(* com.example.service..*(..))")
public void serviceMethods() {}
// 在方法执行前打印日志
@Before("serviceMethods()")
public void beforeServiceMethod() {
System.out.println("【AOP】调用 Service 方法之前...");
}
}
继续往下看createProxy这个创建代理对象的源码

继续往下看,看他怎么拿到代理对象的

可以看到他的这个AopProxy代理对象的实现类(我这个项目引用了第三方的maven包没有进行排除,实际上就2个实现类,一个cglib一个jdk的动态代理)

先看一下JDK的动态代理的实现(一目了然,能看到是使用了一个代理类进行创建代理对象的),CGlib也是差不多的就是创建一个代理对象
这里我写一个简单的创建代理对象的例子,了解一下代理对象是什么
public interface UserService {
void addUser(String name);
}
public class UserServiceImpl implements UserService {
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
}
public class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用前:日志记录");
Object result = method.invoke(target, args); // 调用真实方法
System.out.println("调用后:方法执行完成");
return result;
}
}
// 使用
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyInvocationHandler(target));
proxy.addUser("Tom");
//输出
调用前:日志记录
添加用户:Tom
调用后:方法执行完成
8.缓存起来 ->放到一级缓存singletonObjects
getBean() --> 动态代理对象 -->调用方法时被增强
只有实现了AOP的获取的才是代理对象
AOP-> 动态代理
Spring动态代理:
JDK。 bean实现了接口使用JDK. 代理对象:动态生成实现目标bean接口的代理对象
CGlib。 没有实现接口使用cglib
JDK
代理对象 implment IAService{
add(){
// 前置通知
//调用你的目标方法
AService.add()
//后置通知
}
}
CGlib
代理对象 extends 目标bean {
// 重写目标方法
}QA:
1.一级缓存能不能解决循环依赖
能(如果只有一级缓存,那么锁粒度就很大,要锁整个getBean方法)
2.二级缓存能不能解决循环依赖
能(二级缓存可以降低锁的粒度)
二级缓存 确实可以拿到“半成品 Bean”,解决最基础的循环依赖。虽然spring现在看是三级缓存解决的循环依赖,实际上二级缓存也可以解决,但是用二级缓存解决的话会导致代理对象注入错误(也就是只能返回原始 Bean,如果这个 Bean 需要 AOP 增强,就会导致依赖方注入的不是代理对象,增强逻辑失效)。所以需要三级缓存
3.三级缓存是解决aop创建代理对象问题吗?
(如果循环依赖时,注入了原始对象,后面再生成代理对象,就会出现 注入的不是最终的 Bean 的问题。)
三级缓存这里有个误区,好多人以为三级缓存就是用来解决循环依赖的的,实际上只有一级缓存或者二级缓存都可以解决循环依赖!
三级缓存还是主要用来规范Bean创建的生命周期的,如果没有三极缓存,那么Bean就必须要在实例化的时候创建代理AOP对象,加入三级缓存的时候让其只有在循环依赖的时候才会创建动态代理,保证他的规范性
不全是,
可以看到三级缓存他缓存的是

getSingleton() —— 获取 Bean 时用三级缓存
// 逻辑就是: 一级缓存 → 二级缓存 → 三级缓存(工厂生成)
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 先从一级缓存取(完全实例化好的 Bean)
Object singletonObject = this.singletonObjects.get(beanName);
// 2. 如果没有 && 正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 2.1 尝试从二级缓存取(半成品 Bean)
singletonObject = this.earlySingletonObjects.get(beanName);
// 2.2 如果还没有 && 允许提前暴露
if (singletonObject == null && allowEarlyReference) {
// 2.3 从三级缓存取 ObjectFactory,调用它创建早期引用(可能是代理对象)
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 放进二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除三级缓存里的工厂
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}再往下看可以看到一个往3级缓存中put内容的方法,put进去的内容是一个 **ObjectFactory** 工厂
在 AbstractAutowireCapableBeanFactory#doCreateBean 里,调用的DefaultSingletonBeanRegistry#addSingletionFactory方法。
这里可以看到给三级缓存中原来存的是一个匿名函数!

迎刃而解了,RootBeanDefinition看一下可以大概猜到了,存的相当于是告诉你创建哪个类的,这些累的基本信息都有了,直接反射就可以创建

a.三级缓存还可以 支持代理对象(AOP、事务)在循环依赖场景下的正确注入,也就是说让代理对象都是在实例化后创建的
初始化 判断 只有在循环依赖中bean,才在循环依赖中提前创建AOP代理对象,防止aop对象被注入的不是代理对象,也就说是如果一级缓存里没有bean对象,但是二级缓存中没有,这时候要用到三级缓存(实际上三级缓存存的是一个函数式的接口用来存一个bean对象)拿到这个aop Bean对象存到二级缓存中
b.解决循环依赖(提前暴露半成品 Bean)
4.Spirng怎么解决多级缓存循环依赖问题?
⚠️注意:这个和第6个问题不一样!这个是单例Bean,第6题是多例bean
使用三级缓存(就是第三级缓存,而不是三个缓存!)
@Component
class A {
@Autowired B b;
}
@Component
class B {
@Autowired A a;
}注意这里提示了循环依赖报错,需要手动添加配置文件,Spring Boot 2.6.x 开始,默认就关闭了循环依赖的支持,加上配置后就可以了
spring.main.allow-circular-references=true
注意这里有个小提示,
a.如果Bean 被 AOP 代理了,就需要加上@Lazy注解
- 如果 A 或 B 上有
@Transactional、@Async、@Validated之类的注解,Spring 会给它创建代理对象 - 在循环依赖过程中,Spring 可能没法把“代理对象”提前暴露出去 → 导致失败
b.作用域不是单例 ,比如是圆型模式@Scope("prototype") 那 Spring 就不会缓存半成品对象了 → 无法解决循环依赖
c.如果 A 或 B 不是 Spring 管理的 Bean(比如你自己 new 出来的),Spring 自然没法做循环依赖处理。
排查步骤
a.确认 A 和 B 上是不是 @Component 或 @Service,并且在 Spring 扫描路径里。
b.确认 没加 **@Scope("prototype")**。
c.如果用的是 Spring Boot 2.6+,先加上:
spring.main.allow-circular-references=trued.检查是否加了 @Transactional 或者别的切面注解。若有,先去掉测试,或者在依赖处加 @Lazy。
5.Spring 怎么保证单例 Bean 的创建是线程安全的?
DefaultSingletonBeanRegistry 里的缓存机制 + synchronized 锁:
DCL机制(学过单例的都知道这个,双重检查锁!),但是Spring这里也是类似于DCL的
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) { //类似 DCL 的第一次检查
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) { //再检查三级缓存和二级缓存,类似 DCL 的第二次检查
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}6.Spring是否解决多例Bean的循环依赖详解
| 类型 | Spring 是否支持循环依赖 | 原因 |
|---|---|---|
| 单例(singleton) | ✅ 支持 | Spring 可以通过 二级缓存(earlySingletonObjects) 暴露半成品 Bean,从而解决循环依赖 |
| 多例(prototype) | ❌ 不支持 | Spring 不会缓存多例 Bean,每次请求都会创建新的实例,无法提前暴露半成品 Bean |
为什么多例 Bean 无法解决循环依赖
原理:
多例 Bean 每次调用 **getBean()** 都会创建新的实例
MyBean a = context.getBean(MyBean.class);
MyBean b = context.getBean(MyBean.class); // 新实例Spring 不把多例 Bean 放入 singleton 缓存
- 因此没有 earlySingletonObjects 来存半成品 Bean
- 循环依赖时,A 需要 B,但 B 还没创建完成 → Spring 无法提前返回 B 的引用
结果:
- 会抛出 BeanCurrentlyInCreationException
- 循环依赖失败
Spring 会报错:BeanCurrentlyInCreationException: Error creating bean with name 'A'
因为没有缓存机制来提前暴露半成品 Bean
@Component
@Scope("prototype")
class A {
@Autowired
B b;
}
@Component
@Scope("prototype")
class B {
@Autowired
A a;
}解决多例循环依赖的方法
1.改成单例 Bean,Spring 可以用三级缓存机制解决循环依赖
2.使用 @Lazy 延迟注入
3.使用 ObjectFactory 或 Provider 手动注入
@Autowired
private ObjectProvider<B> bProvider;
...
B b = bProvider.getObject();4.重新设计 Bean 依赖关系
我觉得这个是最重要的,如果有循环依赖的问题,你要好好想想是不是真的有这个需要,还是说自己的代码设计失误!
7.Spring是否解决构造函数中的循环依赖详解
a.什么是构造函数循环依赖
构造函数循环依赖指的是:两个或多个 Bean 在 构造函数中相互依赖,例如:
- A 需要 B 构造完成
- B 需要 A 构造完成
- 形成“死循环”
@Component
class A {
private final B b;
public A(B b) { // 构造函数依赖 B
this.b = b;
}
}
@Component
class B {
private final A a;
public B(A a) { // 构造函数依赖 A
this.a = a;
}
}b.Spring 是否能解决?
✅ 单例 Bean 的 setter/字段注入循环依赖可以解决
❌ 构造函数循环依赖无法自动解决
原因:
Spring 的循环依赖解决机制依赖“提前暴露半成品 Bean”
- 二级缓存(
**earlySingletonObjects**)存放的是 实例对象 - 三级缓存(
**singletonFactories**)存放的是 ObjectFactory - Spring 可以在 属性注入阶段暴露半成品 Bean,供其他 Bean 注入
- 二级缓存(
构造函数依赖问题
- 构造函数阶段,Bean 必须先创建实例才能被依赖
- Spring 无法提前创建半成品 Bean(因为构造函数还没执行完成)
- 没有实例可暴露,循环依赖就会导致 BeanCurrentlyInCreationException
c.如何解决构造函数循环依赖?
改成 setter/字段注入(非构造函数)
@Component
class A {
@Autowired
private B b;
}
@Component
class B {
@Autowired
private A a;
}使用 @Lazy 延迟注入
8.循环依赖被关闭了?
循环依赖被关闭的背景
- 默认情况下:Spring 通过 二级缓存/三级缓存 解决循环依赖(仅限单例 + setter/字段注入)。
- 当我们关闭循环依赖(
allowCircularReferences=false)后,Spring 不会把“半成品 Bean”提前暴露出来,因此循环依赖就会直接报错。spring.main.allow-circular-references=false
AOP在循环依赖中会有的问题
1.一级缓存 和依赖注入的Bean不一致
循环依赖中AOP动态的代理, 初始化就不创建了
如果循环以来中创建了

初始化时就不会在创建动态代理Bean了

小tips:本文可能受个人技术能力限制,如果有错误地方可以指出或者加我微信,评论区自动过滤垃圾评论
版权属于:戏人看戏博客网
本文链接:https://blog.web3er.cn/archives/1924.html
若无注明均为戏人看戏原创,转载请注明出处,感谢您的支持!
第7.0笔误 是implement,少打了个e😜