Spring面试题

Spring如何解决循环依赖?

  1. Spring通过递归的方式获取目标bean及其所依赖的bean。
  2. Spring实例化Bean后(此时还未注入属性),如果允许提前暴露,就将这个Bean绑定在ObjectFactory上保存到singletonFactories里。
  3. 后续如果需要这个bean的引用,则从singletonFactories里面获取,然后保存到二级缓存里,以后就能直接从二级缓存获取,不用再去访问三级缓存了。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	// 从上至下 分表代表这“三级缓存”
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
	...
	
	/** Names of beans that are currently in creation. */
	// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
	// 它在Bean开始创建时放值,创建完成时会将其移出~
	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	/** Names of beans that have already been created at least once. */
	// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
	// 至少被创建了一次的 都会放进这里~~~~
	private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}
复制代码
  1. singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  2. earlySingletonObjects: 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  3. singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

创建Bean

protected Object doCreateBean(){
    // Bean实例化...
    
	// 是否要提前暴露(允许循环依赖) 现在此处A是被允许的
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
	// 允许暴露,就把A绑定在ObjectFactory上,注册到三级缓存singletonFactories里面去保存着
	// Tips:这里后置处理器的getEarlyBeanReference方***被促发,自动代理创建器在此处创建代理对象(注意执行时机 为执行三级缓存的时候)
	if (earlySingletonExposure) {
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
    // 属性赋值
    populateBean(beanName, mbd, instanceWrapper);
	// 初始化Bean
	exposedObject = initializeBean(beanName, exposedObject, mbd);

}
复制代码
  1. Bean实例化
  2. 如果Bean允许提前暴露,则把Bean绑定在ObjectFactory上,注册到三级缓存singletonFactories里面去保存着
  3. 后续如果要获取这个Bean引用,则从三级缓存singletonFactories里获取,然后保存到二级缓存earlySingletonObjects,然后就可以给属性赋值啦!

获取单例Bean的源码如下:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	@Override
	@Nullable
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 先从一级缓存singletonObjects中去获取
		Object singletonObject = this.singletonObjects.get(beanName);
        // 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
                // 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    // 如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects。其实也就是从三级缓存移动到了二级缓存
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
	...
	public boolean isSingletonCurrentlyInCreation(String beanName) {
		return this.singletonsCurrentlyInCreation.contains(beanName);
	}
	protected boolean isActuallyInCreation(String beanName) {
		return isSingletonCurrentlyInCreation(beanName);
	}
	...
}
复制代码

步骤:

  1. 先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
  2. 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
  3. 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从**singletonFactories**中移除,并且放进**earlySingletonObjects**。其实也就是从三级缓存**移动(是剪切、不是复制哦~)**到了二级缓存)

加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

暖心小哥哥来总结一下

  1. Bean实例化(这没啥好说的)
  2. 实例化完成以后,如果这个Bean允许提前暴露,那么将这个Bean绑定在ObjectFactory上,注册到三级缓存singletonFactories里面去保存着。
  3. 后续循环依赖如果要用到这个Bean的属性值,那么先从三级缓存里获取,然后将它移动到二级缓存,然后就可以获得引用啦!

ps 个人学习的一些疑问

Q:为什么要先放到三级缓存,而不直接放到二级缓存呢?

A:主要是为了提供AOP支持。循环依赖设置属性的时候,设置的应该是代理对象而不是普通对象,所以要提前进行AOP。所以一个Bean实例化以后,会放一个lambda表达式到三级缓存里。后续设置属性的时候,会调用三级缓存里的lambda表达式提前生成Aop代理对象,然后放到二级缓存里。

Spring中Bean的生命周期

SpringIOC流程

  1. BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
  2. 容器扫描BeanDefinitionRegistry中的BeanDefinition;调用InstantiationStrategy进行Bean实例化的工作;使用BeanWrapper完成Bean属性的设置工作;

Spring AOP原理

一个类初始化完成后,会循环调用所有的BeanPostProcessor方法(可以看一下Bean的生命周期),AbstractAdvisorAutoProxyCreator(代理创建类)继承了BeanPostProcessor,所以会在此时调用它。

AbstractAdvisorAutoProxyCreator 根据是否实现了接口之类的,返回一个jdk动态代理对象或者cglib代理对象。

IOC容器把这个代理对象保存到SingtonObjectMap里面。所以我们以后用到的都是这个代理类。