Spring启动流程
1)Spring 三级缓存与循环依赖
本文将以DefaultListableBeanFactory#preInstantiateSingletons
方法为切入点,主要整理有关三级缓存和循环依赖的问题。
下面简单的回顾一下之间的流程,详细内容参看另一篇文章:Spring启动流程
前文回顾
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
同样用简图来描述流程
preInstantiateSingletons
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
//初始化所有非懒加载的单例Bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
//在BeanFactory引导期间的单例预实例化阶段结束时触发的回调接口。 该接口可以由单例 bean 实现,
//以便在常规单例实例化之后执行一些初始化,避免意外早期初始化(例如来自ListableBeanFactory.getBeansOfType调用)的副作用。
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
-
迭代所有的
BeanDefinitionNames
集合,挨个初始化所有非懒加载的单例类。 -
BeanDefinitionNames
是在注册BeanDefinitionMap
时同时注册的集合。该集合,主要用在这里进行迭代初始化Spring Bean
,并且也方便一些类似获取BeanDefinitionName
的方法使用。 -
初始化的时候还对
FactoryBean
进行判断,因为默认FactoryBean
默认是惰性初始化,如果实现SmartFactoryBean
设置isEagerInit
为true,则代表该类需要急切的进行初始化,会即刻开始初始化FactoryBean
里getObject
返回的类。 -
关于
getMergedLocalBeanDefinition
和getBean
:Spring通过getBean
来完成类的初始化并添加到Spring容器内(即Spring的一级缓存:单例池里面),而在初始化时需要当前类的相关信息:即BeanDefinition
,而BeanDefinition
是可以存在父子关系(并非真的继承,而是BeanDefinition
可以设置父BeanDefinition
),所以Spring通过getMergedLocalBeanDefinition
来获取当前类的完整信息。
getBean→doGetBean
getBean
内继续进行方法调用,最终进入到doGetBean
里面,
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
...
...
// 创建Bean.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
//创建Bean
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);
}
...
...
}
先简单总结一下省略部分的代码作用:
- 尝试从单例池(一级缓存)中获取,如果能获取则直接返回。
- 检查
@DependsOn
注解,如果当前类依赖于@DependsOn
标注的类,则先初始化被依赖的类。 - 标记当前类的状态
- 各种参数的合法性校验
- 针对
prototype
类型Bean的相关处理
最终调用getSingleton
完成Bean的初始化。先整理一下与接下来的分析相关的概念。
自动装载与循环依赖
在Spring中我们通过@Autowired
和@Resource
,完成一个Spring Bean
注入到另外一个Spring Bean
中的,这个过程被我们称之为自动装载。
那么Spring是怎样将两个Bean都存储在单例池中,并且完成引用的呢?如果是简单的先存后拿,肯定是不行的。因为存在特殊情况:
即在类A中依类B,类B依赖类A。单纯的考虑,加载A→判断依赖B→加载B→判断依赖A→加载A……
按照一般的思路就会发生上面的情况,出现了死循环。Spring在一定程度上是支持循环依赖的。对应的方案是引入了三级缓存。
三级缓存
在DefaultListableBeanFactory
的父类链上的DefaultSingletonBeanRegistry
定义了三个缓存池:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
singletonObjects
:单例池,通常意义上的Spring容器,Spring在这个池中存放已经初始化完毕的Spring Bean
;earlySingletonObjects
:存放提前暴露并且经过代理的单例类,singletonFactories
:存放ObjectFactory
,而ObjectFactory
的getObject
方法定义了如何获取早期的Bean引用。
getSingleton
下面进入代码解析:getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// 如果在创建bean期间隐式出现了对应的资源(即发现此时已经添加进入了单例池,则抛出异常)
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
//如果获取到了新的Bean实例(已经完成了代理和自动装载),则添加进单例池中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
在doGetBean
里面调用getSingleton
方法时,利用到了lambda的特性来作为入参。先逐行观察getSingleton
。
主要的代码是beforeSingletonCreation(beanName)
。这行代码很重要:将单例注册为当前正在创建中。
然后是调用ObjectFactory
的getObject
方法,即之前lambda定义的内容:return createBean(beanName, mbd, args)
createBean
点进createBean
方法。
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
//推断当前Beandefinition的beanClass
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// 准备方法重写 包括lookup-method和replaced-method的处理,本文暂不展开
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 第一次调用后置处理器:初始化Bean之前,InstantiationAwareBeanPostProcessor,
// 根据BeanDefinition获取到了对应的class类
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
//创建Bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
createBean
里面完成了第一次后置处理器的调用。关于后置处理器本文暂不展开,并且调用了doCreateBean
方法。
doCreateBean
方法代码较长,并且是spring处理循环依赖的重要部分,我们拆分开来分析。
doCreateBean
推断构造函数并利用反射创建
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//从未完成的 FactoryBean 实例中移除
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//实例化对象(仅仅是生成对象,并非添加到spring单例池中,即目前并没有成为Spring Bean),
//并且第二次调用 后置处理器 determineConstructorsFromBeanPostProcessors
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
...
...
...
}
首先通过createBeanInstance
方法调用后置处理器推断其构造函数,并反射创建BeanDefinition
对应的实例。
- 注意此时只是创建了类对象,但还没有添加到Spring的单例池中,即还没有加入到Spring容器内部。
- 可以理解为此时只是完成了类对象的声明:即类似于
A a=null
Spring 解决循环依赖的原理就在于java类可以先声明,再实例化
新增ObjectFactory到第三级缓存
继续分析doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
...
//允许后置处理器重新定义beanDefinition
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//第三次调用后置处理器 MergedBeanDefinitionPostProcessors
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
//判断是否允许循环依赖,默认是允许的,可以通过代码修改的一般只有allowCircularReferences的值
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");
}
// getEarlyBeanReference里面包含了又一次后置处理器的调用,SmartInstantiationAwareBeanPostProcessor
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
}
调用了两次后置处理器,并且根据earlySingletonExposure
判断当前项目是否允许循环依赖。
earlySingletonExposure
由三个值判断:
- 当前创建类是否是单例类
allowCircularReferences
是否为true
,即当前是否允许循环依赖。注意此参数是可以由程序员手动修改的,即Spring允许程序员自行决定程序中是否允许出现循环依赖isSingletonCurrentlyInCreation(beanName)
:需要当前类处于创建状态,在前文getSingleton
的beforeSingletonCreation(beanName)
中已经设置为 创建中状态
属性填充:populateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Initialize the bean instance.
Object exposedObject = bean;
try {
//填充属性(即自动注入) 完成第五次和第六次后置处理器的调用
// InstantiationAwareBeanPostProcessor.after
// hasInstantiationAwareBeanPostProcessors
populateBean(beanName, mbd, instanceWrapper);
//完成第七次和第八次后置处理器的调用,
//判断当前Bean,是否是BeanNameAware、BeanClassLoaderAware、BeanFactoryAware并分别设置相关内容
//applyBeanPostProcessorsAfterInitialization
//applyBeanPostProcessorsAfterInitialization
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
}
populateBean
方法完成属性填充,跟进。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
//在设置属性之前,让任何 InstantiationAwareBeanPostProcessors 有机会修改 bean 的状态。例如,这可用于支持字段注入样式。
//扩展点,又一次调用后置处理器,
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
//如果后置处理器修改了Beandefinition,则判断是否已经修改过了属性值的相关配置
//如果修改了,则按照器byName或byType的方式直接完成装载
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
//利用后置处理器,处理属性的获取
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
//根据PropertyValues完成属性填充
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
populateBean
里面再次调用后置处理器,spring在这里提供了一个扩展点,允许开发者在属性填充这个阶段修改Bean的状态(使用后置处理器的方式)。并且可能直接依据byName
和byType
装载属性值。
我们知道,使用自动装载这一功能时,我们可以使用@Autowired
和@Resource
。这两个注解的功能也不一样。那么解析的方式也应当不一样。
这里,提出一个老生常谈的问题:
@Autowired
和@Resource
的联系与区别
从功能层面,也是最简单的层面来看:
@Autowired
和@Resource
都是完成自动装载的注解@Autowired
装载时默认是byType
注入的,byType
时如果存在多个结果,使用@Primary
标注的类。想要使用byName
注入,则需要配合@Qualifer
注解@Resource
默认按照byName
注入,如果不匹配,则会回退为原始类型进行匹配。并且@Resource
可以同时支持byName
和byType
从解析层面,也是更深刻的层面来看:
- spring在完成属性填充(自动装载)时利用后置处理器来解析,会调用后置处理器的
postProcessProperties
方法。 - 而解析
@Autowired
和@Resource
的后置处理器也是不一样的。这点很容易理解,毕竟二者存在使用上的区别,自然不可能使用同一套代码来完成解析。
@Resource
:使用CommonAnnotationBeanPostProcessor
后置处理器解析
@Autowired
:使用AutowiredAnnotationBeanPostProceoor
后置处理器解析
前面提到正常来说完成自动装载应当有以下步骤:加载A→判断依赖B→加载B→判断依赖A→加载A……
spring即是在上面提到的两个后置处理器里面完成了上面的步骤。我们以A依赖B,B依赖A的情况来做分析。
后置处理器获取或加载属性
以AutowiredAnnotationBeanPostProceoor
为例(假定A中的成员变量B为@Autowired
的Setter注入)跳到关键代码如下:
DefaultListableBeanFactory#doResolveDependency
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
...
...
...
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName);
}
此时是在AutowiredAnnotationBeanPostProceoor
内部加载时,发现A依赖B,并通过getBean
获取B。
注意此时B是还没有初始化的,即在Spring的单例池中并不存在。
那么此时经过getBean
的调用,B也将进行一次上面A的加载步骤:
- 推断构造函数创建B,封装到
BeanWrapper
中 - 新增B的
ObjectFactory
到第三级缓存 - 属性填充,
AutowiredAnnotationBeanPostProceoor
获取B的属性。
矛盾点来了:此时B的属性经判断时A,此时A还没有完成初始化,难道再次创建A吗,这无疑造成了死循环。
我们来看看Spring是如何处理:
//尝试获取
Object sharedInstance = getSingleton(beanName);
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
...
...
...
@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) {
//如果二级缓存没取到,则从三级缓存里面取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//三级缓存移动到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
getBean
调用时会调用getSingleton
尝试获取,在B的后置处理器内获取A时,进入到方法内部。
有3个重要点:创建中状态、第三级缓存(ObjectFactory)、allowEarlyReference(是否允许获取早期引用)
this.singletonObjects.get(beanName)
:尝试从一级缓存单例池取,此时A没有创建完毕,自然获取不到
this.earlySingletonObjects.get(beanName)
:先判断是否处于创建中状态,如果是的话,则可以从二级缓存里面取。
- 前面提到A在最开始
getBean
的时候已经在getSingleton(String beanName, ObjectFactory<?> singletonFactory)
的beforeSingletonCreation(beanName)
里面设置了A为创建中状态
此时依旧无法获取到A,二级缓存中不存在。
this.singletonFactories.get(beanName)
:allowEarlyReference
为true,此时尝试从三级缓存里面获取。调用三级缓存ObjectFactory.getObject()
方法。成功获取到A,并将三级缓存移动到二级缓存。
此时成功创建了B,再度回到A的创建过程当中。
调用initializeBean
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
invokeAwareMethods
:如果当前类是BeanNameAware
、BeanClassLoaderAware
、BeanFactoryAware
则设置相关属性;调用初始化方法- 调用两次后置处理器
判断Bean版本问题
正常来说,到这里就结束了。不过spring还进行了额外的判断。同样是循环依赖的收尾问题。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
...
...
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// allowRawInjectionDespiteWrapping:在循环引用的情况下是否诉诸于注入原始 bean 实例,即使注入的 bean 最终被包裹
// 如果当前创建的bean出现在了二级缓存里面,证明出现了循环引用
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
//获取当前bean 依赖的bean
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
//如果在已经创建了对应的依赖
actualDependentBeans.add(dependentBean);
}
}
//存在已经创建的依赖,即当前bean已经被引用它的bean 使用了。
// 即当前bean已经被装载了,但是此刻的bean被修改了。(例如 在initializeBean的后置处理器重新new,或生成了另外一个代理)
// 证明初始当前bean出现了版本差异
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
}
- 首先是判断当前是否支持循环依赖
- 如何支持的话,就尝试是否能获取到当前对象,调用
getSingleton(beanName, false)
注意第二个参数allowEarlyReference
为false,这表明只从二级缓存里面去取(此时本类尚处于创建中,一级缓存里面是肯定没有的,所以实际上就是从二级缓存里面去取)
为什么要从二级缓存里面去取?
- 正常情况下,spring的二级缓存只有一个途径新增,那就是从三级缓存移动过来,此时会调用三级缓存内的
ObjectFactory#getObject
,最终调用getEarlyBeanReference
。在这里面可能会进行aop的代理,即最终到达二级缓存里面的对象是经过代理的(如果该类需要被代理的话)
那么什么时候会移动三级缓存到二级缓存?
- 答案是出现循环依赖的时候,老例子A和B,初始化B的时候,需要获取A,而A正在创建中,此时就需要从三级缓存里面获取。
所以,之所以要从二级缓存里面去取 是为了验证当前类是否出现了循环依赖。
那么上面一长段代码的作用就很明显了:
用A和B举例
- 验证当前创建的A对象,是否已经被其他对象给依赖注入了。
- 如果是的话,那么当前A类是否被代理过了,如果代理了,那么B里面的A对象和经过
initializeBean
之后的对象是否为同一个? - 是的话,直接返回。不是的话,根据
allowRawInjectionDespiteWrapping
的值判断,是否抛出异常。
总的来说就是为了确保是否要在循环引用的情况下,是否仍要在单例池里面增加当前对象。其实还是为了解决aop
代理的问题。
回到getSingleton方法,添加到单例池里面
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
//标记当前类为 正在创建中
//设置当前初始化类 到set集合中,如果不能添加就直接报错 (set 不能重复添加)
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//通过createBean方法新创建的对象
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
//清除二三级缓存,新增当前实例到一级缓存单例池
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
好家伙,经历了漫长的调用栈,差点忘记上面所有的代码只是调用了getSingleton#singletonFactory.getObject()
而已。
此时singletonObject = singletonFactory.getObject()
获取到的singleObject已经是完全创建好的对象了。
调用addSingleton
方法,直接放到spring的以及缓存里面即可。
注意:此时addSingleton
会清除二三级缓存里面的值。
- 如果没有发生循环依赖,三级缓存里面始终有一份当前
beanName
的缓存。 - 或者类似于初始化B时,B获取A时,能够从A的二级缓存获取到。但是B本身还是始终在三级缓存里面有一份的,这种情况三级缓存里面也有
- 如果发生了循环依赖,则存在于二级缓存,(因为A初始化的时候被引用,从三级缓存跳到了二级缓存)
ok,初始化完毕
2)其他问题
开发者可以设置的两个特别变量
-
allowEarlyReference
:当出现循环依赖的时候,是否允许提前获取引用(从三级缓存里面获取) -
allowRawInjectionDespiteWrapping
:出现循环依赖时,是否仍要在单例池里面增加当前对象
这两个变量都是可以设置的,如下:
@Configuration
@ComponentScan("com.sulin")
@EnableAspectJAutoProxy
public class AppConfig {
}
class StartApplication {
public static void main(String[] args) throws InterruptedException {
//注册BeanDefinition,设置两个变量,并手动refresh
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
AbstractAutowireCapableBeanFactory beanFactory = (AbstractAutowireCapableBeanFactory) ac.getBeanFactory();
beanFactory.setAllowCircularReferences(false);
beanFactory.setAllowRawInjectionDespiteWrapping(false);
ac.register(AppConfig.class);
ac.refresh();
}
}
为什么需要三级缓存?二级缓存可以吗?
答案:可以,但没必要。
- 我们这里说的二级缓存指的是,一级缓存和三级缓存,即单例池和
ObjectFactory
缓存。 - 确实一级+三级缓存就足够解决循环依赖。二级缓存只是存的从三级缓存
ObjectFactory
获取到的对象而已。 - 那么不存到二级缓存,每次直接从三级缓存
getObject
也是可以的。就算是存在AOP代理。到时候再执行一次就可以了。
事实上,在较早期的spring版本中就是二级缓存。中间的一层是后面的版本再加进去的。
三级缓存的getEarlyBeanReference
包含后置处理器进行代理(AOP),中间再加一层,提高了效率,不必每次都去进行后置处理器的代码调用。设计上面来说也更加科学。
3)流程案例总结
简单整理一下存在循环依赖时和不存在循环依赖时整体的过程。
存在循环依赖:例如AB互相依赖
- 通过
getBean
获取 A 时,将 A 相关的ObjectFactory
添加进三级缓存,设置 A 当前状态为为创建中状态; - 通过
Beandefinition
进行构造器推断,反射创建出 A 的Java对象,封装到WrappedInstance
; - 进行 A 的属性填充,发现需要注入B对象(根据
@Resource
或@Autowired
判断),尝试获取 B ; - 通过
getBean(B)
获取 B 时,将 B 相关的ObjectFactory
添加进三级缓存,设置 B 当前状态为为创建中状态 - 通过
Beandefinition
进行构造器推断,反射创建出 B 的Java对象,封装到WrappedInstance
; - 进行 B 的属性填充,发现需要注入 B 对象(根据
@Resource
或@Autowired
判断),尝试获取 A; - 此时因为 A 处于创建中状态,并且
allowEarlyReference
为true,便从三级缓存拿 A(此时如果A需要代理,遍会调用后置处理器进行AOP相关操作),移动到二级缓存,A 从三级缓存移动到二级缓存。B 成功获取到 A 的引用。 - B 初始化完毕,在
getSingleton
方法中进行资源清理,将新的 B对象添加到一级缓存当中,并从三级缓存里面移除 B ; - 回到 A,此时 A 通过
getBean(B)
成功获取到 B - A 初始化完毕,在
getSingleton
方法中进行资源清理,将新的 A对象添加到一级缓存当中,并从二级缓存里面移除 B