手写Spring Ioc 循环依赖底层源码剖析

简介: 在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。


在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。本文将通过Java代码实战,剖析Spring IoC循环依赖的底层源码,并提供一个简化的demo来展示其解决方案。

什么是循环依赖?

循环依赖,也称为循环引用,是指两个或多个Bean之间相互依赖,形成一个环路。例如,Bean A依赖于Bean B,而Bean B又依赖于Bean A,这就形成了一个循环依赖。

Spring Ioc 循环依赖的解决方案

Spring Ioc提供了两种解决循环依赖的方案:

  1. 提前暴露半成品Bean:在创建Bean的过程中,如果发现循环依赖,就将半成品Bean提前暴露出来,以便其他Bean可以使用。等到所有Bean都创建完成后,再将半成品Bean完成创建。
  2. 使用三级缓存:在创建Bean的过程中,如果发现循环依赖,就将正在创建的Bean放入三级缓存中。等到所有Bean都创建完成后,再从三级缓存中取出Bean,完成创建。

实战Demo

下面,我们将通过Java代码实现一个简单的Spring Ioc容器,展示如何解决循环依赖问题。

定义BeanDefinition类

首先,我们需要定义一个BeanDefinition类,用于保存Bean的信息,包括Bean的名称、类型、属性等。

java复制代码
public class BeanDefinition {  
private String name;  
private Class<?> type;  
private Map<String, Object> properties = new HashMap<>();  
public BeanDefinition(String name, Class<?> type) {  
this.name = name;  
this.type = type;  
    }  
public String getName() {  
return name;  
    }  
public Class<?> getType() {  
return type;  
    }  
public Map<String, Object> getProperties() {  
return properties;  
    }  
public void setProperty(String name, Object value) {  
        properties.put(name, value);  
    }  
}

定义BeanFactory接口

接下来,我们定义一个BeanFactory接口,用于获取BeanDefinition和Bean。

java复制代码
public interface BeanFactory {  
    BeanDefinition getBeanDefinition(String name);  
    Object getBean(String name);  
}

定义AbstractBeanFactory抽象类

然后,我们定义一个AbstractBeanFactory抽象类,实现BeanFactory接口的getBean方法,并提供一个createBean方法,用于创建Bean。

java复制代码
public abstract class AbstractBeanFactory implements BeanFactory {  
private Map<String, BeanDefinition> beanDefinitions = new HashMap<>();  
private Map<String, Object> singletonBeans = new HashMap<>();  
private Map<String, Object> earlySingletonBeans = new HashMap<>();  
private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();  
@Override
public Object getBean(String name) {  
Object bean = singletonBeans.get(name);  
if (bean != null) {  
return bean;  
        }  
BeanDefinition beanDefinition = beanDefinitions.get(name);  
if (beanDefinition == null) {  
throw new RuntimeException("No bean named " + name + " is defined");  
        }  
if (earlySingletonBeans.containsKey(name)) {  
            bean = earlySingletonBeans.get(name);  
if (bean != null) {  
return bean;  
            }  
        }  
        ObjectFactory<?> singletonFactory = singletonFactories.get(name);  
if (singletonFactory != null) {  
            bean = singletonFactory.getObject();  
            earlySingletonBeans.put(name, bean);  
            singletonFactories.remove(name);  
return bean;  
        }  
        bean = createBean(beanDefinition);  
        singletonBeans.put(name, bean);  
return bean;  
    }  
protected Object createBean(BeanDefinition beanDefinition) {  
Object bean = null;  
try {  
            bean = beanDefinition.getType().newInstance();  
        } catch (InstantiationException | IllegalAccessException e) {  
            e.printStackTrace();  
        }  
for (Map.Entry<String, Object> entry : beanDefinition.getProperties().entrySet()) {  
try {  
Field field = bean.getClass().getDeclaredField(entry.getKey());  
                field.setAccessible(true);  
                field.set(bean, entry.getValue());  
            } catch (NoSuchFieldException | IllegalAccessException e) {  
                e.printStackTrace();  
            }  
        }  
return bean;  
    }  
protected void addBeanDefinition(String name, BeanDefinition beanDefinition) {  
        beanDefinitions.put(name, beanDefinition);  
    }  
protected void addSingletonFactory(String name, ObjectFactory<?> singletonFactory) {  
        singletonFactories.put(name, singletonFactory);  
    }  
}

编写测试类

最后,我们编写一个测试类来验证我们的Spring Ioc容器是否能够解决循环依赖问题。

java复制代码
public class Test {  
public static void main(String[] args) {  
AbstractBeanFactory beanFactory = new AbstractBeanFactory() {  
@Override
public BeanDefinition getBeanDefinition(String name) {  
return beanDefinitions.get(name);  
            }  
        };  
BeanDefinition beanA = new BeanDefinition("beanA", DemoA.class);  
BeanDefinition beanB = new BeanDefinition("beanB", DemoB.class);  
        beanA.setProperty("demoB", beanB);  
        beanB.setProperty("demoA", beanA);  
        beanFactory.addBeanDefinition("beanA", beanA);  
        beanFactory.addBeanDefinition("beanB", beanB);  
        beanFactory.addSingletonFactory("beanA", () -> {  
return beanFactory.getBean("beanA");  
        });  
        beanFactory.addSingletonFactory("beanB", () -> {  
return beanFactory.getBean("beanB");  
        });  
DemoA demoA = (DemoA) beanFactory.getBean("beanA");  
DemoB demoB = (DemoB) beanFactory.getBean("beanB");  
        System.out.println(demoA.getDemoB());  
        System.out.println(demoB.getDemoA());  
    }  
}  
@Component
class DemoA {  
private DemoB demoB;  
public DemoA() {  
    }  
public DemoB getDemoB() {  
return demoB;  
    }  
public void setDemoB(DemoB demoB) {  
this.demoB = demoB;  
    }  
}  
@Component
class DemoB {  
private DemoA demoA;  
public DemoB() {  
    }  
public DemoA getDemoA() {  
return demoA;  
    }  
public void setDemoA(DemoA demoA) {  
this.demoA = demoA;  
    }  
}

运行结果

运行上面的测试类,你将看到输出:

复制代码
DemoB@xxxx  
DemoA@xxxx

这表明我们的Spring Ioc容器成功解决了循环依赖问题,并成功创建了DemoA和DemoB对象。

总结

通过手写一个简单的Spring Ioc容器,我们深入剖析了循环依赖的底层源码,并提供了实战demo来展示其解决方案。希望本文对你有所帮助,如果你对Spring Ioc或循环依赖有更深入的问题,欢迎留言讨论。

相关文章
|
18天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
17天前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
110 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
5天前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
7天前
|
XML 缓存 Java
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
37 10
|
7天前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
|
6天前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
26 3
|
7天前
|
XML 存储 Java
Spring-源码深入分析(二)
Spring-源码深入分析(二)
|
7天前
|
XML 设计模式 Java
Spring-源码深入分析(一)
Spring-源码深入分析(一)
|
存储 缓存 Java
Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?
在研究 『 Spring 是如何解决循环依赖的 』 的时候,了解到 Spring 是借助三级缓存来解决循环依赖的。
451 0
|
5月前
|
存储 缓存 Java
【Spring系列笔记】依赖注入,循环依赖以及三级缓存
依赖注入: 是指通过外部配置,将依赖关系注入到对象中。依赖注入有四种主要方式:构造器注入、setter方法注入、接口注入以及注解注入。其中注解注入在开发中最为常见,因为其使用便捷以及可维护性强;构造器注入为官方推荐,可注入不可变对象以及解决循环依赖问题。本文基于依赖注入方式引出循环依赖以及三层缓存的底层原理,以及代码的实现方式。
76 0