文章閱讀前推薦
推薦先去看看源碼,源碼很短,但是對于我們在腦子里構(gòu)建一個(gè)完整思路很重要??雌饋矸浅:唵?,只需要雙擊shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到550行左右的doCreateBean方法,重點(diǎn)看一下580行到600行這20行代碼就行,包含了三級緩存、屬性注入、初始化,精華都在這20行,實(shí)在沒條件的可以直接看文末附帶的doCreateBean方法源碼
Spring可以自動(dòng)解決的循環(huán)依賴
public class AService {
@Autowired
private BService bService;
}
public class BService {
@Autowired
private AService aService;
}
Spring無法自動(dòng)解決構(gòu)造器的循環(huán)依賴
public class DService {
public DService(CService cService) {
...
}
}
public class CService {
public CService(DService dService) {
...
}
}
源碼中關(guān)于三級緩存的定義
// 一級緩存
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);
所以說,其實(shí)三級緩存就是三個(gè)Map而已
三級緩存有什么區(qū)別?
一級緩存
一級緩存里存的是成品對象,實(shí)例化和初始化都完成了,我們的應(yīng)用中使用的對象就是一級緩存中的
二級緩存
二級緩存中存的是半成品,沒有完成屬性注入和初始化,用來解決對象創(chuàng)建過程中的循環(huán)依賴問題
早期暴露出去的Bean,其實(shí)也就是解決循環(huán)依賴的Bean。早期的意思就是沒有完完全全創(chuàng)建好,但是由于有循環(huán)依賴,就需要把這種Bean提前暴露出去。其實(shí) 早期暴露出去的Bean 跟 完完全全創(chuàng)建好的Bean 他們是同一個(gè)對象,只不過早期Bean里面的注解可能還沒處理,完完全全的Bean已經(jīng)處理了完了,但是他們指的還是同一個(gè)對象,只不過它們是在Bean創(chuàng)建過程中處于的不同狀態(tài)
三級緩存
三級緩存中存的是 ObjectFactory<?> 類型的代理工廠對象,用于處理存在 AOP 時(shí)的循環(huán)依賴問題
存的是每個(gè)Bean對應(yīng)的ObjectFactory對象,通過調(diào)用這個(gè)對象的getObject方法,就可以獲取到早期暴露出去的Bean。
注意:這里有個(gè)很重要的細(xì)節(jié)就是三級緩存只會對單例的Bean生效,像多例的是無法利用到三級緩存的,通過三級緩存所在的類名DefaultSingletonBeanRegistry就可以看出,僅僅是對SingletonBean也就是單例Bean有效果。
發(fā)生循環(huán)依賴時(shí)的執(zhí)行流程(精華必讀)
正常不存在循環(huán)依賴的A、B對象是依次創(chuàng)建的,但是如果存在循環(huán)依賴的話,創(chuàng)建A的過程中,會順便把B也創(chuàng)建了。注意,每次獲取bean對象都會先去一級緩存看有沒有值。
具體流程是:
1、遍歷待創(chuàng)建的所有beanName,第一次遍歷,開始獲取A,此時(shí)緩存中沒有,會開始正常創(chuàng)建流程
2、A初始創(chuàng)建完成,然后判斷A是否是單例,且沒有創(chuàng)建完畢,如果是,那么就會把A的beanFactory存入三級緩存
3、A開始處理@Autowired注解,開始注入B屬性,于是嘗試從緩存獲取B,獲取不到,則開始正常創(chuàng)建B的流程
4、B初始創(chuàng)建完成,同樣判斷B是否是單例,且沒有創(chuàng)建完畢,如果是,那么就會把B的beanFactory存入三級緩存
5、B開始處理@Autowired注解,開始注入A屬性,于是依次從一級緩存、二級緩存查找A屬性,都沒有就嘗試從三級緩存獲取A的beanFactory,通過beanFactory.getObject()方法獲取A屬性,接下來把A存入二級緩存,清除三級緩存。因?yàn)榇藭r(shí)能獲取到A,所以B的A屬性能填充成功,B接著執(zhí)行初始化,B處于實(shí)例化、初始化都完成的完全狀態(tài)
6、B執(zhí)行addSington(),把完全狀態(tài)的B存入一級緩存,清空二三級緩存(實(shí)際只有三級有值)
7、A繼續(xù)開始填充B屬性,于是調(diào)用beanFactory.getBean()獲取B,第六步已經(jīng)把B存入一級緩存,此時(shí)直接返回,填充成功,繼續(xù)執(zhí)行初始化,得到一個(gè)完全狀態(tài)的A
8、A執(zhí)行addSington(),把完全狀態(tài)的A存入一級緩存,清空二三級緩存(實(shí)際只有二級有值)
9、第二次遍歷,開始獲取B,此時(shí)一級緩存中有B,直接返回。
至此A、B全部實(shí)例化、初始化完成
疑惑解答
問題一:步驟5中,為什么要把B放入二級緩存?
答:主要是怕還有其他的循環(huán)依賴,如果還有的話,直接從二級緩存中就能拿到早期的AService對象
問題二:步驟6中,為什么要清空二三級緩存?
答:因?yàn)楹罄m(xù)其他bean中也需要注入B時(shí),會按順序從一級緩存直到三級緩存查找,一級緩存有了,二三級緩存中的就不需要了,節(jié)省空間
問題三:文章開頭提到,構(gòu)造器造成的循環(huán)依賴三級緩存解決不了,為什么?
答:因?yàn)闃?gòu)造器循環(huán)依賴是發(fā)生在bean實(shí)例化階段,此時(shí)連早期對象都還沒創(chuàng)建出來,拿什么放到三級緩存。三級緩存只能是在bean實(shí)例化之后,才能起到作用
問題四:不用三級緩存,只用二級緩存能不能解決循環(huán)依賴?
答:不能,因?yàn)橥ㄟ^ObjectFactory獲取的Bean可能是兩種類型,第一種就是實(shí)例化階段創(chuàng)建出來的對象,還是一種就是實(shí)例化階段創(chuàng)建出來的對象的代理對象。至于是不是代理對象,取決于你的配置,如果添加了事務(wù)注解又或是自定義AOP切面,那就需要代理。假設(shè)舍棄第三級緩存,也就是沒有ObjectFactory,那么就需要往第二緩存放入早期的Bean,那么是放沒有代理的Bean還是被代理的Bean呢,這是在后面的屬性注入階段,處理注解的時(shí)候才能分辨的?
1)如果直接往二級緩存添加沒有被代理的Bean,那么可能注入給其它對象的Bean跟最后最后完全生成的Bean是不一樣的,因?yàn)樽詈笊傻氖谴韺ο?,這肯定是不允許的;
2)那么如果直接往二級緩存添加一個(gè)代理Bean呢?
● 假設(shè)沒有循環(huán)依賴,提前暴露了代理對象,那么如果跟最后創(chuàng)建好的不一樣,那么項(xiàng)目啟動(dòng)就會報(bào)錯(cuò),
● 假設(shè)沒有循環(huán)依賴,使用了ObjectFactory,那么就不會提前暴露了代理對象,到最后生成的對象是什么就是什么,就不會報(bào)錯(cuò),
● 如果有循環(huán)依賴,不論怎樣都會提前暴露代理對象,那么如果跟最后創(chuàng)建好的不一樣,那么項(xiàng)目啟動(dòng)就會報(bào)錯(cuò)
通過上面分析,如果沒有循環(huán)依賴,使用ObjectFactory,就減少了提前暴露代理對象的可能性,從而減少報(bào)錯(cuò)的可能。
問題五:如果把二級緩存去掉,只留下一級、三級緩存呢?
答:假設(shè)舍棄第二級緩存,也就是沒有存放早期的Bean的緩存,其實(shí)肯定也不行。上面說過,ObjectFactory其實(shí)獲取的對象可能是代理的對象,那么如果每次都通過ObjectFactory獲取代理對象,那么每次都重新創(chuàng)建一個(gè)代理對象,這肯定也是不允許的。
doCreateBean()方法源碼(帶注釋)
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//1、通過BeanDefinition實(shí)例化對象
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//對象是否單例、是否未創(chuàng)建完成
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");
}
//將對象的工廠加入到三級緩存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//屬性注入(在這里解析@Autowired注解時(shí),觸發(fā)循環(huán)依賴)
populateBean(beanName, mbd, instanceWrapper);
//初始化
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);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
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.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
文章來源:http://www.zghlxwxcb.cn/news/detail-843290.html
從緩存中獲取Bean的源碼
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從一級緩存中獲取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果一級緩存里沒有 且 bean正在創(chuàng)建中
synchronized (this.singletonObjects) {
// 從二級緩存里獲取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 二級緩存沒有 從三級緩存獲取一個(gè)工廠
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 能獲取到工廠 則創(chuàng)建bean
singletonObject = singletonFactory.getObject();
// 把實(shí)例存入二級緩存
this.earlySingletonObjects.put(beanName, singletonObject);
// 把工廠從三級緩存移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
–我是“三七有脾氣”,一個(gè)在互聯(lián)網(wǎng)"茍且偷生"的Java程序員
“如果感覺博客對你有用,麻煩給個(gè)點(diǎn)贊、評論、收藏,謝謝”文章來源地址http://www.zghlxwxcb.cn/news/detail-843290.html
到了這里,關(guān)于Spring使用三級緩存解決循環(huán)依賴?終于完全弄明白了的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!