現象
- 兩個類都是spring的單例類,兩個類裡面有一個成員變量,變量都是對方。
@Component
public class BeanA
{
@Resource
private BeanB beanB;
}
@Component
class BeanB
{
@Resource
private BeanA beanA;
}
這個問題不是什麼複雜的問題。正常思路是這樣的。
BeanA a = new BeanA();
BeanB b = new BeanB();
a.setBeanB(b);
b.setBeanA(a);
spring 建立對象
spring的思路是這樣子的,這個就出現了循環依賴的問題
public static BeanA createA() {
BeanA beanA = new BeanA();
BeanB b = createB();
beanA.setBeanB(b);
return beanA;
}
public static BeanB createB() {
BeanB beanB = new BeanB();
BeanA a = createA();
beanB.setBeanA(a);
return beanB;
}
spring的理念是,隻要你建立一個對象,那麼這個對象中的需要自動注入的屬性就要即時注入,如果沒有就建立這個屬性,或者報錯。
- spring的解決方式(引入緩存)僞代碼
private static Map<String,Object> cacheObjects = new HashMap<>();
public static BeanA createA() {
// 先從緩存中擷取
BeanA beanA = (BeanA) cacheObjects.get("beanA");
if (beanA != null){
return beanA;
}
beanA = new BeanA();
cacheObjects.put("beanA",beanA);
BeanB b = createB();
beanA.setBeanB(b);
return beanA;
}
public static BeanB createB() {
// 先從緩存中擷取
BeanB beanB = (BeanB) cacheObjects.get("beanB");
if (beanB != null){
return beanB;
}
beanB = new BeanB();
cacheObjects.put("beanB",beanB);
BeanA a = createA();
beanB.setBeanA(a);
return beanB;
}
源碼解讀
流程圖
- 先嘗試從緩存中拿取執行個體
// 從緩存中拿取執行個體
Object sharedInstance = getSingleton(beanName);
緩存容器
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
從緩存中先嘗試擷取
....
Object singletonObject = null;
// 從三級緩存中擷取執行個體
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
}
return singletonObject;
....
如果對象不存在建立一個對象
// 建立執行個體
instanceWrapper = createBeanInstance(beanName, mbd, args);
建立對象之後加入緩存,注意這個對象是一個空的對象。
之後來進行屬性注入
// 擷取有@AutoWired 注解的字段和方法
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 進行設定值
metadata.inject(bean, beanName, pvs);
}
// 擷取依賴注入的值
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
如果它是一個對象類型的資料,最終觸發getBean操作
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName);
}