How does Spring resolve circular dependencies? - springboot actual e-commerce project mall4j

How does Spring resolve circular dependencies?

springboot actual e-commerce project mall4j (https://gitee.com/gz-yami/mall4j)

java open source mall system

@component
class A {
    private B b;
}
@component
class B {
    private A a;
}

Class A depends on B as an attribute, and class B uses class A as an attribute, cyclically dependent on each other.

Circular dependency.jpg

Source code understanding:

//调用AbstractBeanFactory.doGetBean(),向IOC容器获取Bean,触发依赖注入的方法
protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
		...
         // 第一次getSingleton获取对象实例
		// 先从缓存中取是否已经有被创建过的单例类型的Bean[没有的话就去获取半成品的,也就是earlySingletonObjects,缓存二的东西]
		// 对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
		Object sharedInstance = getSingleton(beanName);
    	...
        try {
            //创建单例模式Bean的实例对象
            if (mbd.isSingleton()) {
                //第二次getSingleton尝试创建目标对象,并且注入属性
                //这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        //创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
                        return createBean(beanName, mbd, args);
                    } catch (BeansException ex) {
                        //显式地从容器单例模式Bean缓存中清除实例对象
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                // 如果传入的是factoryBean,则会调用其getObject方法,得到目标对象
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                //IOC容器创建原型模式Bean实例对象
            }
            ...
        } catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
	    ...
	    return (T) bean;
	}

That is, when A is instantiated, *[the first getSingleton] is not found in the cache , so go to the second getSingleton to instantiate A [actually call doCreateBean()]*, because A needs B, go to doGetBean and try again Get B, find that B is not in the cache, continue to call the second getSingleton to instantiate, when the attribute A is to be injected, the semi-finished product A is found in the second-level cache, and the successful injection returns to the stage of A's instantiation, and B is injected.

The first getSingleton code

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//从spring容器中获取bean
		Object singletonObject = this.singletonObjects.get(beanName);//缓存1
		//如果获取不到 判断要获取的对象是不是正在创建过程中----如果是,则去缓存(三级缓存)中取对象(不是bean)
		//isSingletonCurrentlyInCreation() 存放的对象 的时机是在getBean中第二次调用getSingleton时候beforeSingletonCreation(beanName);存进去的
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);//缓存2
				if (singletonObject == null && allowEarlyReference) {//allowEarlyReference--判断是否支持循环依赖,默认为true
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);//缓存3
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						//将三级缓存升级为二级缓存
						this.earlySingletonObjects.put(beanName, singletonObject);
						//从三级缓存中删除  为什么删除?防止重复创建。设置三级缓存的目的是为了提高性能,因为每次创建都需要经过factory,会花费很多时间
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

When AB is instantiated, the three caches cannot find these two classes, because neither is created;

L3 cache

	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//一级缓存,存放完整的bean信息

	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);//三级缓存,bean创建完了就放进去,还有他的bean工厂

	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);//二级缓存,存放还没进行属性赋值的bean对象,也即半成品bean

The second getSingleton code

Here, A is created first <u> and placed in the third-level cache</u>, which is actually delegated to another **doGetBean()** to complete

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    	...
		synchronized (this.singletonObjects) {
			// 再次判断ioc容器中有无该bean
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				..before..
				try {
					// 回调到doCreateBean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				..after..
			}
			return singletonObject;
		}
	}

Since the first method of obtaining a singleton cannot find AB, it will enter the second method of obtaining a singleton to try to find it. In this method, singletonFactory.getObject() is the core, and it will call back to the doCreateBean method to continue creating beans.

doCreateBean code

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		//封装被创建的Bean对象
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			//单例的情况下尝试从factoryBeanInstanceCache获取 instanceWrapper,并清除同名缓存
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			// 创建Bean实例
			// instanceWrapper会包装好目标对象
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		...
		//向容器中缓存单例模式的Bean对象,以防循环引用,allowCircularReferences是判断是否支持循环依赖,这个值可以改为false
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		//判断是否允许循环依赖
		if (earlySingletonExposure) {
			...
			//将这个对象的工厂放入缓存中  (注册bean工厂singletonFactories,第一个getSingleton使用的),此时这个bean还没做属性注入
			//这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
			// getEarlyBeanReference很特别,这里面会做aop代理
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		//Bean对象的初始化,依赖注入在此触发
		//这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
		Object exposedObject = bean;
		try {
			//填充属性 -- 自动注入
			//前面是实例化,并没有设置值,这里是设置值.将Bean实例对象封装,并且Bean定义中配置的属性值赋值给实例对象
           	 //这里做注入的时候会判断依赖的属性在不在,不在就调用doGetBean继续创建
			populateBean(beanName, mbd, instanceWrapper);
			// 该方法主要是对bean做一些扩展
			// 初始化Bean对象。属性注入已完成,处理各种回调
			// (对实现Aware接口(BeanNameAware、BeanClassLoaderAware、BeanFactoryAware)的bean执行回调、
			// aop、init-method、destroy-method?、InitializingBean、DisposableBean等)
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		...
		return exposedObject;
	}

In this method, when A is instantiated, A has been added to the third-level cache singletonFactories as a semi-finished product by calling the addSingletonFactory method, so that the semi-finished product instance of A can be obtained when B is recursively instantiated. The detailed code is as follows:

Add the created bean to the <u>level 3 cache</u>

Happens in the addSingletonFactory method

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				//放到三级缓存
				// 注册bean工厂
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

So when does a recursive call happen?

In fact, when <u>populateBean(beanName, mbd, instanceWrapper);</u> is doing property injection, it is assumed that it is automatically injected according to the name, and <u>autowireByName() is called,</u> the method To loop through the attributes of the xml file that have been added to the registry before getBean,

populateBean has a method of autowireByName, the code is as follows

protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

		//对Bean对象中非简单属性(不是简单继承的对象,如8中原始类型,字符串,URL等都是简单属性)进行处理
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			//如果Spring IOC容器中包含指定名称的Bean,就算还没进行初始化,A发现有B属性时,B属性已经被写入注册表之类的东西,所以这个判断返回true
			if (containsBean(propertyName)) {
				//调用getBean方法向IOC容器索取指定名称的Bean实例,迭代触发属性的初始化和依赖注入
				Object bean = getBean(propertyName); //实际上就是委托doGetBean()
				pvs.add(propertyName, bean);
				//指定名称属性注册依赖Bean名称,进行属性依赖注入
				registerDependentBean(propertyName, beanName);
				if (logger.isDebugEnabled()) {
					logger.debug("Added autowiring by name from bean name '" + beanName +
							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
				}
			}
			...
		}
	}

Object bean = getBean(propertyName); This method actually delegates doGetBean() again, and goes up the process recursively again, that is, when A finds that there is another B when it is instantiated to this step, it will start again from DoGetBean() starts, and searches and creates step by step. The difference is that when B goes to the method injected by name, A can already be found in the second-level cache at this time, and there is no need to create the A object again.

Summarize

Spring uses the third-level cache to solve the problem of circular dependencies;

In a recursive way, the object is gradually instantiated, and the semi-finished object that has been added to the cache in the previous step is injected as an attribute;

When the last recursion is reached, it will gradually return and create the corresponding instances one by one.

springboot actual e-commerce project mall4j (https://gitee.com/gz-yami/mall4j)

java open source mall system

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324098412&siteId=291194637