前提
(1)作者技術比較差,文章寫的比較随意,也可能有錯誤,歡迎您指出。
(2)如果您不了解Spring Bean的聲明周期,那麼您可以看一下文章(Bean的生命周期_CBeann的部落格-)或者百度其它文章,然後在回來看該文章,否則個人感覺應該看不懂
解決循環依賴
假設有一種下面的情況,A中有B,B中有A
@Data
public class A {
private B b;
public A() {System.out.println("A 無參構造器。。。");}
public void speak() {System.out.println("------AAA---------");}
}
@Data
public class B {
public B() {System.out.println("B 無參構造器。。。");}
private A a;
public void speak() {System.out.println("------BBB---------");}
}
圖檔分析
代碼分析
在建立的A的時候調用doCreateBean方法
1)調用A無參構造方法建立Bean
2)把該bean對象添加到三級緩存中(在下面的代碼中有注釋)
3)Bean的屬性指派,A的裡面引用了B,是以此時會調用doCteateBean(B)
//AbstractAutowireCapableBeanFactory
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
{
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//省略
}
if (instanceWrapper == null) {
//1)調用無參構造方法建立Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
synchronized (mbd.postProcessingLock) {
//省略
}
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
//省略
}
//2)把該bean對象添加到三級緩存中,注意getEarlyBeanReference方法,特别有用
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//3)Bean的屬性指派
populateBean(beanName, mbd, instanceWrapper);
//4)處理aware接口、applyBeanPostProcessorsBeforeInitialization、initMethod
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
//省略
}
else {
//省略
}
}
if (earlySingletonExposure) {
//省略
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
//省略
}
return exposedObject;
}
}
此時在建立B的時候調用getBean(A),然後會走到下面代碼的地方,從三級緩存中擷取到A(B=null),傳回該不完整的A的位址,然後B建立成功,然後繼續建立A,然後A也建立成功。
-------------------------源碼1
//DefaultSingletonBeanRegistry
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//從一級緩存中擷取,即IOC容器,即完整的Bean對象
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;
}
循環依賴總結
(1)建立A的時候調用A的無參構造方法,然後在把得到的位址A(B=null)放入到三級緩存中,然後填充自己的屬性B,也就會建立B;
(2)當建立B的時候,填充自己的屬性A,從三級緩存中拿到A(B=null)位址,然後B建立成功;
(3)此時回到(1),此時拿到B,然後完善A,建立A成功。
(4)因為在(2)中拿到的是A的位址,是以在(3)中完善A在B中是一個。
三級緩存
疑問
個人感覺二級緩存足矣,為什麼還要三級緩存?
反駁疑問
假設下面的場景:隻有singletonObject(第一級緩存)和singletonFactory (第三級緩存),即沒有earlySingletonObjects(第二級緩存)
如果有這麼一種情況A(B),B(A),還有一個AOP是關注A的某個方法
此時的邏輯為:
1)建立A
2)把A(B=null)的位址(abc)存入singletonFactory緩存中
3)建立B
4)B在指派a屬性的時候,在singletonFactory緩存中拿出A的位址(abc)并且指派給屬性a(左邊這句話是錯的)(這就是三級緩存的關鍵),
4.1)沒有AOP的時候,确實是存的a的位址,沒錯,傳回的也是a的位址。
4.2)如果有AOP,确實存進去的是a的位址,但是傳回的已經不是A的位址了,是A的代理對象位址(看源碼2,3,4)。
總結:此時就出現問題了,如果沒有earlySingletonObjects(第二級緩存),那麼每次在singletonFactory (第三級緩存)中拿到的A對象都會建立建立一個代理對象,即每次向依賴A的對象中賦的值都是不同的代理對象,那麼就不符合單例模式了。
-------------------------源碼2
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
-------------------------源碼3
//AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
//跟進去
return wrapIfNecessary(bean, beanName, cacheKey);
}
-------------------------源碼4
//AbstractAutoProxyCreator
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//傳回了一個新對象,新位址
//傳回了一個新對象,新位址
//傳回了一個新對象,新位址
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
總結
1)在沒有AOP的情況下二級緩存足矣解決循環依賴,三級緩存更能解決問題。
2)三級緩存其實也是解決循環依賴的,是解決帶AOP的循環依賴的,如上文中舉的例子。如果您查的三級緩存資料沒有說AOP,個人感覺這篇文章寫的不是很充實。
本文沒有回答的疑問
疑問1
上問中反駁二級緩存不能解決帶AOP的循環依賴問題時,是把earlySingletonObjects(第二級緩存)去掉;如果我說我去掉singletonFactory (第三級緩存),那該如何反駁二級緩存不能解決帶AOP的循環依賴問題呢???
疑問2
就拿上問中舉的例字來說,A依賴B,B依賴A,有一個關注A的AOP。
下面是建立Bean聲明周期的一段代碼,以建立A為例
//AbstractAutowireCapableBeanFactory
protected Object doCreateBean{
//建立A
Object exposedObject = bean;
try {
//初始化A,因為A中有屬性B,此時去建立B,然後把A的代理對象存入earlySingletonObjects緩存中,B建立完畢,然後又回到此處繼續初始化A
populateBean(beanName, mbd, instanceWrapper);
//為非代理對象A執行aware接口等等
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
//省略
}
}
if (earlySingletonExposure) {
//在earlySingletonObjects中拿到代理對象A
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
//把exposedObject由指向非代理對象A變為指向代理對象A,那麼
//exposedObject = initializeBean(beanName, exposedObject, mbd);
//我認為是白做了,我不清楚這個地方???????????????
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
如果有知道上面兩個問題答案的,可以在下問中評論,一起學習,共同進步
參考
看為什麼要三級緩存的話從70分鐘開始看