Spring面试题
Spring如何解决循环依赖?
- Spring通过递归的方式获取目标bean及其所依赖的bean。
 - Spring实例化Bean后(此时还未注入属性),如果允许提前暴露,就将这个Bean绑定在ObjectFactory上保存到singletonFactories里。
 - 后续如果需要这个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));
}
复制代码 singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用earlySingletonObjects: 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖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);
}
复制代码 - Bean实例化
 - 如果Bean允许提前暴露,则把Bean绑定在ObjectFactory上,注册到三级缓存singletonFactories里面去保存着
 - 后续如果要获取这个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);
	}
	...
}
复制代码 步骤:
- 先从
一级缓存singletonObjects中去获取。(如果获取到就直接return) - 如果获取不到或者对象正在创建中(
isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return) - 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过
getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从**singletonFactories**中移除,并且放进**earlySingletonObjects**。其实也就是从三级缓存**移动(是剪切、不是复制哦~)**到了二级缓存) 
加入
singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决
暖心小哥哥来总结一下
- Bean实例化(这没啥好说的)
 - 实例化完成以后,如果这个Bean允许提前暴露,那么将这个Bean绑定在ObjectFactory上,注册到三级缓存singletonFactories里面去保存着。
 - 后续循环依赖如果要用到这个Bean的属性值,那么先从三级缓存里获取,然后将它移动到二级缓存,然后就可以获得引用啦!
 ps 个人学习的一些疑问
Q:为什么要先放到三级缓存,而不直接放到二级缓存呢?
A:主要是为了提供AOP支持。循环依赖设置属性的时候,设置的应该是代理对象而不是普通对象,所以要提前进行AOP。所以一个Bean实例化以后,会放一个lambda表达式到三级缓存里。后续设置属性的时候,会调用三级缓存里的lambda表达式提前生成Aop代理对象,然后放到二级缓存里。
Spring中Bean的生命周期
SpringIOC流程
- BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
 - 容器扫描BeanDefinitionRegistry中的BeanDefinition;调用InstantiationStrategy进行Bean实例化的工作;使用BeanWrapper完成Bean属性的设置工作;
 
Spring AOP原理
一个类初始化完成后,会循环调用所有的BeanPostProcessor方法(可以看一下Bean的生命周期),AbstractAdvisorAutoProxyCreator(代理创建类)继承了BeanPostProcessor,所以会在此时调用它。
AbstractAdvisorAutoProxyCreator 根据是否实现了接口之类的,返回一个jdk动态代理对象或者cglib代理对象。
IOC容器把这个代理对象保存到SingtonObjectMap里面。所以我们以后用到的都是这个代理类。

京公网安备 11010502036488号