SpringBean:创建、使用、生命周期、循环依赖问题

灵魂拷问环节

Q1: Spring 中,什么时候会出现循环依赖,导致无法正常启动?

   循环依赖问题的出现,其核心就是在循环依赖的多个类中,实例化类的同时,也在设置循环依赖的对象

在 Spring 中,依赖注入有三种方式:

  1. 基于构造方法的依赖注入
  2. 基于 setter() 方法的依赖注入
  3. 基于 成员变量 的依赖注入

   发生循环依赖时,Spring 框架可以 自动帮我们解决,Bean Scope 为 singleton,且是基于成员变量或 setter() 方法的方式注入依赖。其原因就是 singleton 类型的 Bean 在实例化后,被添加到了 Bean 集合的缓存中,具体的源码逻辑下文有介绍。

   基于构造器 DI 的循环依赖,Spring中没有自动解决,但可以通过在构造器上添加 @Lazy 注解,产生一个 CGlib代理类来手动解决这个问题。

   其他 Scope Bean 产生的循环依赖,无法解决。

循环依赖代码案例参考

Q2: Spring 是如何解决 singleton 类型 Bean 循环依赖的?

  1. Spring 通过 提前曝光 的方式,将未完成 “初始化”的 BeanA,设值为 ObjectFactory,并存入单例类工厂缓存 singletonFactories 中(addSingletonFactory),然后再通过 getSingleton 将Bean 填充 earlySingletonObjects 缓存中 (该步骤都在AbstractAutowireCapableBeanFactory#doCreateBean方法中)
  2. 循环依赖的 BeanB 初始化会查找 BeanA ,便可以从 earlySingletonObjects 缓存中找到 earlySingletonRefrence,此时就已经打破了循环依赖的问题。

其中两步对应的关键代码如下:
DefaultSingletonBeanRegistry:

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

AbstractAutowireCapableBeanFactory:

protected Object doCreateBean(
    final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
                                                                throws BeanCreationException {
      .........
    // 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");
        }
        //将未完成 “初始化”的 BeanA,设值为 ObjectFactory
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
     }
    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
       populateBean(beanName, mbd, instanceWrapper);
       exposedObject = initializeBean(beanName, exposedObject, mbd);
    }catch(...){...}
    if (earlySingletonExposure) {
    	//将Bean填充到earlySingletonObjects 缓存中
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                  exposedObject = earlySingletonReference;
            }
            .........
        }
    }
    return exposedObject;
}

Q3: 为什么要三级缓存?二级缓存不能解决循环依赖吗?

二级缓存配合也可以解决循环依赖,但三级缓存的使用,是为了完成 Spring AOP 中的后置处理 功能而提出的。

Q4:如果 非要用 二级缓存来解决上面提到的两个问题,如何来实现?为什么 Spring 没有采用你说的这种方案?

二级缓存架构下,为了获取到 AOP 代理后的对象,需要对 Bean A 在 实例化 后,完成代理,然后放入二级缓存提前曝光,保证 Bean B 在填充类属性时,可以从二级缓存获取到代理后的 Aop-A,以实现 Spring Aop 的代理功能以及解决 A<=>B 的循环依赖问题。

上述的逻辑并没有被 Spring 采用,是因为上述流程和 Spring Bean 生命周期的设计相互冲突了。 Bean 生命周期的最后一步才是完成 AOP 代理,而不是二级缓存架构中提到的在实例化后,就完成代理。

Q5:AOP 代理对象提前放入了三级缓存,没有经过初始化和属性填充,这个代理又是如何保证依赖属性的注入的呢?

AOP 中的代理对象,保存着 traget 也就是是 原始bean 的引用,因此后续 原始bean 的完善,也就相当于 Spring AOP 中的 target 的完善,这样就保证了 AOP 的 属性填充初始化 了!

Q6: 明明初始化的时候是A对象,那么 Spring 是在哪里将 A的代理对象放入到容器中 的呢?

在完成 Bean A 的初始化 后,Spring 又调用了一次 getSingleton 去缓存中获取对象,但这次是禁用三级缓存的获取方式(allowEarlyReference = false)。在这之前,为 Bean B 中注入 Bean A 时,已经将 A 从三级缓存中的工厂取出,并从工厂中获取到了Aop-A(半成品)放入到了二级缓存中。所以初始化完成后的 getSingleton 调用,拿到的是二级缓存中 Aop-A 对象,最后 将 Aop-A 移除二级缓存,放入一级缓存中。然后继续执行 Bena 接下来的流程。

Q7: 回到 Q1 ,提到的两种方式中,为什么没有解决循环依赖问题?

  1. 对于使用了构造器的循环依赖,由于 无法通过构造器生成 向缓存中存入的 实例化后的 Bean ,所以 Spring 会抛出异常。
  2. 对于 prototype 作用域的 Bean ,Spring 容器没有设计缓存机制,因此也无法提前暴露一个创建中的 Bean。
    原因也比较简单,prototype 作用域的 Bean 每次请求都会创建一个实例对象,如果使用缓存,在请求量比较大的场景下,对 GC 的压力会非常大,所以 Spring 直接抛出异常。

  OK,如果你对于上面6个问题都能比较轻松的回答到的话,那么恭喜你,你的 Spring 学习已经可以毕业了,可以和面试官互相吹逼了。如果小老弟还一问三不知,三问傻痴痴的,那么接着往下学习吧(对应的源码太多了,阅读起来费时费力,后面尽量少贴)。

Bean 创建、获取

首先,需要知道,Spring 在创建 Bean 的时候默认是按照自然排序来进行创建的,其过程主要分为4 个阶段(Bean的生命周期):

  1. 实例化(Instantiation),简单理解就是使用空参构造器 new 了一个对象
  2. 属性注入(Populate),为实例化中 new 出来的对象填充属性
  3. 初始化(Initialization),进行初始化,并实现 Spring AOP 代理
  4. 销毁(Destruction),回调在使用前注册的销毁方法

然后我们来看一张不包含 Spring AOP 的,循环依赖 Bean 创建流程图:
在这里插入图片描述
图中涉及到了三级缓存,他们的区别如下:

  • singletonObjects(单例对象缓存):用于存储 已经创建完成且属性已经注入完成 的Bean实例。

  • earlySingletonObjects(早期单例对象缓存):用于存储 已经创建完成但属性还未注入完成 的Bean实例,解决循环依赖。

  • singletonFactories(单例类工厂缓存):用于存储Bean实例的工厂对象,即创建Bean实例的工厂方法。二级缓存中存储的就是从这个工厂中获取到的对象,解决代理 Bean 的问题。

DefaultSingletonBeanRegistry:

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

包含 Spring AOP 的,循环依赖 Bean 创建流程图(两张):
在这里插入图片描述
在这里插入图片描述

拿着这3张图,然后再去对比源码阅读,下面是一些重要的代码片段,多的就不贴了,影响阅读体验。

AbstractBeanFactory:

/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * @param name the name of the bean to retrieve
	 * @param requiredType the required type of the bean to retrieve
	 * @param args arguments to use when creating a bean instance using explicit arguments
	 * (only applied when creating a new instance as opposed to retrieving an existing one)
	 * @return an instance of the bean
	 * @throws BeansException if the bean could not be created
	 */
	public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
			throws BeansException {

		return doGetBean(name, requiredType, args, false);
	}

	/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * @param name the name of the bean to retrieve
	 * @param requiredType the required type of the bean to retrieve
	 * @param args arguments to use when creating a bean instance using explicit arguments
	 * (only applied when creating a new instance as opposed to retrieving an existing one)
	 * @param typeCheckOnly whether the instance is obtained for a type check,
	 * not for actual use
	 * @return an instance of the bean
	 * @throws BeansException if the bean could not be created
	 */
	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);//从三个缓存中获取bean
        
        ……
        //当无法从三处缓存中获取到bean时执行
		// 开始创建bean。如下片段为创建单例bean
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}


DefaultSingletonBeanRegistry:

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not.本次调用链路下,将传入true
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 从一级缓存获取
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
                // 从二级缓存获取
				singletonObject = this.earlySingletonObjects.get(beanName);
                // 是否允许早期引用创建
				if (singletonObject == null && allowEarlyReference) {
                    /**
					 * 三级缓存获取,key=beanName value=objectFactory,objectFactory中存储getObject()方法用于获取提前曝光的实例
					 *
					 * 而为什么不直接将实例缓存到二级缓存,而要多此一举将实例先封装到objectFactory中?
					 * 主要关键点在getObject()方法并非直接返回实例,而是对实例又使用
					 * SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法对bean进行处理
					 *
					 * 也就是说,当spring中存在该后置处理器,所有的单例bean在实例化后都会被进行提前曝光到三级缓存中,
					 * 但是并不是所有的bean都存在循环依赖,也就是三级缓存到二级缓存的步骤不一定都会被执行,有可能曝光后直接创建完成,没被提前引用过,
					 * 就直接被加入到一级缓存中。因此可以确保只有提前曝光且被引用的bean才会进行该后置处理
 					 */
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
                        /**
						 * 通过getObject()方法获取bean,通过此方法获取到的实例不单单是提前曝光出来的实例,
						 * 它还经过了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法处理过。
						 * 这也正是三级缓存存在的意义,可以通过重写该后置处理器对提前曝光的实例,在被提前引用时进行一些操作
 						 */
						singletonObject = singletonFactory.getObject();
                        // 将三级缓存生产的bean放入二级缓存中
						this.earlySingletonObjects.put(beanName, singletonObject);
                        // 删除三级缓存
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

Bean循环依赖参考


Bean的生命周期

在这里插入图片描述

其过程主要分为4 个阶段:

  1. 实例化,简单理解就是使用空参构造器 new 了一个对象

  2. 属性注入,为实例化中 new 出来的对象填充属性

  3. 初始化,第 3、4 步为在初始化前执行,第 5、6 步为初始化操作,第 7 步在初始化后执行,该阶段结束,才能被用户使用。第 3、4、7完成了 Spring AOP 功能

  4. 销毁(Destruction)

AbstractAutowireCapableBeanFactory:

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

    // 1. 实例化
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    
    Object exposedObject = bean;
    try {
        // 2. 属性赋值
        populateBean(beanName, mbd, instanceWrapper);
        // 3. 初始化
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }

    // 4. 销毁-注册回调接口
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }

    return exposedObject;
}

Bean的生命周期参考

生命周期中常用的前、后置API

分类:

  1. Constructor:类构造方法
  2. @PostConstruct:javax.annotation.PostConstruct
  3. afterPropertiesSet:InitializingBean#afterPropertiesSet
  4. init-method:

实际的执行顺序可查看日志编号:

@Configuration
@Component
public class BeanLifecycleTest implements InitializingBean, DisposableBean {
    public final Logger logger = LoggerFactory.getLogger(this.getClass());

    public BeanLifecycleTest() {
        logger.info("1 BeanLifecycleTest Construct");
    }

    @PostConstruct
    public void postConstruct() {
        logger.info("2 BeanLifecycleTest @PostConstruct ");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        logger.info("3 BeanLifecycleTest InitializingBean#afterPropertiesSet ");
    }

    @Bean(initMethod = "init")
    public void initMethod() {
        logger.info("4 BeanLifecycleTest @Bean.initMethod");
    }

    @Bean(destroyMethod = "destory")
    public void destroyMethod() {
        //标注destroyMethod后,会在bean初始化完成后,调用该销毁方法
        logger.warn("5 BeanLifecycleTest @Bean.destroyMethod");
    }

    @PreDestroy
    public void preDestroy() {
        //todo     为什么6在7之前????
        logger.warn("6 BeanLifecycleTest @PreDestroy");
    }

    @Override
    public void destroy() throws Exception {
        logger.warn("7 BeanLifecycleTest DisposableBean#destroy");
    }
}

nitmethod
---->

  • 9
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值