Bean创建过程中调的方法

在AbstractBeanFactory中
getSingleton方法中,
调beforeSingletonCreation方法,
将beanName添加到defaultSingletonBeanRegistry的成员变量singletonCurrentlyInCreation集合中。
然后调singletonFactory.getObject方法,调到匿名beanFactory实例,

AbstractBeanFactory
getBean
doGetBean
{匿名beanFactory实例:
lambda.doGetBean {
AbstractAutowireCapableBeanFactory.createBean
}
}

创建过程:【

AbstractAutowireCapableBeanFactory.doCreateBean

// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			// 加入三级缓存,没有循环依赖也会执行这一步
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
 createBeanInstance
 		instantiateBean
 				SimpleInstantiationStrategy.instantiate
 				BeanUtils.instantiateClass

DefaultSingletonBeanRegistry.addSingleton方法
将已创建的Bean加入到容器。

三级缓存中的创建的Bean的移动

/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

在不存在循环依赖的时候, Bean创建过程的最后一步从singletonFactories中移除,添加到singletonObjects

	/**
	 * Add the given singleton object to the singleton cache of this factory.
	 * <p>To be called for eager registration of singletons.
	 * @param beanName the name of the bean
	 * @param singletonObject the singleton object
	 */
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

存在循环依赖的时候,先放在三级缓存singletonFactories,
当被其他Bean调用的之后从singletonFactories移除,增加到二级缓存earlySingletonObjects,
最后从earlySingletonObjects移除,增加到三级缓存singletonObjects。

解决循环依赖的过程,主要用到了singletonFactories,不要earlySingletonObjects行不行呢?

在Spring解决循环依赖的过程中,singletonFactoriesearlySingletonObjects都起着重要作用,通常不能完全不用earlySingletonObjects,以下是具体分析:

singletonFactories的作用

  • 存储对象工厂singletonFactories是一个ObjectFactory类型的三级缓存,用于存储创建单例Bean的工厂对象。当一个Bean正在创建过程中,还未完全初始化完成时,会将其对应的对象工厂放入该缓存中。
  • 生成早期引用:通过调用对象工厂的getObject方法,可以获取到Bean的早期引用。这个早期引用可以在其他Bean依赖该Bean时进行注入,从而解决循环依赖问题。

earlySingletonObjects的作用

  • 存储早期Bean实例earlySingletonObjects也是一个用于解决循环依赖的缓存,它存储的是已经创建但还未完全初始化的Bean实例的早期引用。当从singletonFactories中获取到对象工厂并调用getObject方法生成早期引用后,会将这个早期引用放入earlySingletonObjects缓存中。
  • 避免重复创建:在后续的依赖注入过程中,如果再次需要获取该Bean的早期引用,会直接从earlySingletonObjects缓存中获取,而不需要再次调用对象工厂的getObject方法,提高了性能并避免了可能的重复创建问题。

不能完全不用earlySingletonObjects的原因

  • 性能优化:如果没有earlySingletonObjects缓存,每次获取Bean的早期引用都需要从singletonFactories中获取对象工厂并调用getObject方法,这会带来一定的性能开销。尤其是在存在复杂的循环依赖关系时,频繁的工厂调用会降低系统的运行效率。
  • 保证一致性:在多线程环境下,earlySingletonObjects可以保证在同一时间内,对于同一个正在创建的Bean,所有线程获取到的早期引用是一致的。如果没有这个缓存,可能会导致不同线程获取到不同的早期引用,从而引发数据不一致等问题。

虽然singletonFactories在解决循环依赖中是关键,但earlySingletonObjects也不可或缺,它们共同协作来高效、正确地处理循环依赖问题。

构造方法相互依赖是三级缓存机制都解决不了的循环依赖

class BeanA {
    private BeanB b;

    public BeanA(BeanB b) {
        this.b = b;
    }
}

class BeanB {
    private BeanA a;

    public BeanB(BeanA a) {
        this.a = a;
    }
}

你所写的这种直接在构造函数中相互依赖注入的方式在Spring中是不行的,会导致循环依赖问题无法正常解决,原因如下:

Spring解决循环依赖的机制限制

  • Spring在处理循环依赖时,主要依靠三级缓存机制。当创建一个Bean时,会先将其早期引用放入缓存中,以便在其他Bean依赖它时可以获取到这个早期引用进行注入。但这种机制在构造函数相互依赖的情况下存在问题,因为在构造函数执行时,Bean还未完全创建完成,此时如果直接将未完成创建的Bean作为参数传递给另一个Bean的构造函数,就会导致依赖注入失败。

解决方案

使用@Lazy注解

可以在其中一个Bean的依赖注入处使用@Lazy注解,延迟该Bean的初始化,直到真正需要使用时才进行创建。例如:

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
public class BeanA {
    private final BeanB b;

    public BeanA(@Lazy BeanB b) {
        this.b = b;
    }
}

@Component
public class BeanB {
    private final BeanA a;

    public BeanB(BeanA a) {
        this.a = a;
    }
}

在上述代码中,BeanA的构造函数中注入BeanB时使用了@Lazy注解,这样BeanBBeanA构造函数被调用时不会立即创建,而是在第一次使用BeanB时才进行创建,从而避免了构造函数循环依赖的问题。

使用属性注入或方法注入

将依赖注入方式从构造函数注入改为属性注入或方法注入,这样可以让Spring在处理循环依赖时有更多的灵活性。例如:

import org.springframework.stereotype.Component;

@Component
public class BeanA {
    private BeanB b;

    public void setB(BeanB b) {
        this.b = b;
    }
}

@Component
public class BeanB {
    private BeanA a;

    public void setA(BeanA a) {
        this.a = a;
    }
}

然后在配置类中通过@Bean注解的方法来进行依赖注入的配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public BeanA beanA() {
        return new BeanA();
    }

    @Bean
    public BeanB beanB() {
        return new BeanB();
    }

    @Bean
    public void configure(BeanA a, BeanB b) {
        a.setB(b);
        b.setA(a);
    }
}

在上述代码中,BeanABeanB使用了属性注入的方式,通过AppConfig配置类中的configure方法来手动完成依赖注入,避免了在构造函数中直接相互依赖的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值