Spring IOC原理補(bǔ)充說明(循環(huán)依賴、Bean作用域等)
前言
通過之前的幾篇文章將Spring基于XML配置的IOC原理分析完成,但其中還有一些比較重要的細(xì)節(jié)沒有分析總結(jié),比如循環(huán)依賴的解決、作用域的實(shí)現(xiàn)原理、BeanPostProcessor的執(zhí)行時(shí)機(jī)以及SpringBoot零配置實(shí)現(xiàn)原理(@ComponentScan、@Import、@ImportSource、@Bean注解的使用和解析)等等。下面就先來看看循環(huán)依賴是怎么解決的,在此之前一定要熟悉整個(gè)Bean的實(shí)例化過程,本篇只會(huì)貼出關(guān)鍵性代碼。
正文
循環(huán)依賴
首先來看幾個(gè)問題:
什么是循環(huán)依賴?
在熟悉了Bean實(shí)例化原理后,你會(huì)怎么解決循環(huán)依賴的問題?
Spring怎么解決循環(huán)依賴?有哪些循環(huán)依賴可以被解決?哪些又不能?
什么是循環(huán)依賴?
這個(gè)概念很容易理解,簡單說就是兩個(gè)類相互依賴,類似線程死鎖的問題,也就是當(dāng)創(chuàng)建A對(duì)象時(shí)需要注入B的依賴對(duì)象,但B同時(shí)也依賴A,那到底該先創(chuàng)建A還是先創(chuàng)建B呢?
Spring是如何解決循環(huán)依賴的?
探究Spring的解決方法之前,我們首先得搞清楚Spring Bean有幾種依賴注入的方式:
通過構(gòu)造函數(shù)
通過屬性
通過方法(不一定是setter方法,只要在方法上加上了@Autowired,都會(huì)進(jìn)行依賴注入)
其次,Spring作用域有singleton、prototype、request、session等等,但在非單例模式下發(fā)生循環(huán)依賴是會(huì)直接拋出異常的,下面這個(gè)代碼不知道你還有沒有印象,在AbstractBeanFactory.doGetBean中有這個(gè)判斷:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
為什么這么設(shè)計(jì)呢?反過來想,如果不這么設(shè)計(jì),你怎么知道循環(huán)依賴到底是依賴的哪個(gè)對(duì)象呢?搞清楚了這個(gè)再來看哪些依賴注入的方式發(fā)生循環(huán)依賴是可以解決,而那些又不能。結(jié)論是構(gòu)造函數(shù)方式?jīng)]辦法解決循環(huán)依賴,其它兩種都可以。
我們先來看看為什么通過屬性注入和方法注入可以解決。回憶一下Bean的實(shí)例化過程:
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);
}
if (instanceWrapper == null) {
//創(chuàng)建實(shí)例
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 {
// Bean實(shí)例化完成后收集類中的注解(@PostConstruct,@PreDestroy,@Resource, @Autowired,@Value)
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.
// 單例bean提前暴露
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");
}
//這里著重理解,對(duì)理解循環(huán)依賴幫助非常大,重要程度 5 添加三級(jí)緩存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//ioc di,依賴注入的核心方法,該方法必須看
populateBean(beanName, mbd, instanceWrapper);
//bean 實(shí)例化+ioc依賴注入完以后的調(diào)用,非常重要
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 {
//注冊(cè)bean銷毀時(shí)的類DisposableBeanAdapter
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
仔細(xì)看這個(gè)過程其實(shí)不難理解,首先Spring會(huì)通過無參構(gòu)造實(shí)例化一個(gè)空的A對(duì)象,實(shí)例化完成后會(huì)調(diào)用addSingletonFactory存入到三級(jí)緩存中(注意這里存入的是singletonFactory對(duì)象):
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 一級(jí)緩存
if (!this.singletonObjects.containsKey(beanName)) {
System.out.println("========set value to 3 level cache->beanName->" + beanName + "->value->" + singletonFactory);
// 三級(jí)緩存
this.singletonFactories.put(beanName, singletonFactory);
// 二級(jí)緩存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
然后才會(huì)去依賴注入觸發(fā)類B的實(shí)例化,所以這時(shí)緩存中已經(jīng)存在了一個(gè)空的A對(duì)象;同樣B也是通過無參構(gòu)造實(shí)例化,B依賴注入又調(diào)用getBean獲取A的實(shí)例,而在創(chuàng)建對(duì)象之前,先是從緩存中獲取對(duì)象:
//從緩存中拿實(shí)例
Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//根據(jù)beanName從緩存中拿實(shí)例
//先從一級(jí)緩存拿
Object singletonObject = this.singletonObjects.get(beanName);
//如果bean還正在創(chuàng)建,還沒創(chuàng)建完成,其實(shí)就是堆內(nèi)存有了,屬性還沒有DI依賴注入
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//從二級(jí)緩存中拿
singletonObject = this.earlySingletonObjects.get(beanName);
//如果還拿不到,并且允許bean提前暴露
if (singletonObject == null && allowEarlyReference) {
//從三級(jí)緩存中拿到對(duì)象工廠
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//從工廠中拿到對(duì)象
singletonObject = singletonFactory.getObject();
//升級(jí)到二級(jí)緩存
System.out.println("======get instance from 3 level cache->beanName->" + beanName + "->value->" + singletonObject );
this.earlySingletonObjects.put(beanName, singletonObject);
//刪除三級(jí)緩存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
很明顯,會(huì)從三級(jí)緩存中拿到singletonFactory對(duì)象并調(diào)用getObject方法,這是一個(gè)Lambda表達(dá)式,在表達(dá)式中又調(diào)用了getEarlyBeanReference方法:
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;
}
這里你點(diǎn)進(jìn)去看會(huì)發(fā)現(xiàn)都是返回之前我們創(chuàng)建的空的A對(duì)象,因此B對(duì)象能夠依賴注入完成并存入到一級(jí)緩存中,接著A對(duì)象繼續(xù)未完成的依賴注入自然是可以成功的,也存入到一級(jí)緩存中。Spring就是這樣通過緩存解決了循環(huán)依賴,但是不知道你注意到?jīng)]有在上面的getSingleton方法中,從三級(jí)緩存中拿到對(duì)象后,會(huì)添加到二級(jí)緩存并刪除三級(jí)緩存,這是為什么呢?這個(gè)二級(jí)緩存有什么用呢?
其實(shí)也很簡單,就是為了提高效率的,因?yàn)樵趃etEarlyBeanReference方法中是循環(huán)調(diào)用BeanPostProcessor類的方法的,當(dāng)只有一對(duì)一的依賴時(shí)沒有什么問題,但是當(dāng)A和B相互依賴,A又和C相互依賴,A在注入完B觸發(fā)C的依賴注入時(shí),這個(gè)循環(huán)還有必要么?讀者們可以自行推演一下整個(gè)過程。
至此,Spring是如何解決循環(huán)依賴的相信你也很清楚了,現(xiàn)在再來看通過構(gòu)造函數(shù)依賴注入為什么不能解決循環(huán)依賴是不是也很清晰了?因?yàn)橥ㄟ^構(gòu)造函數(shù)實(shí)例化并依賴注入是沒辦法緩存一個(gè)實(shí)例對(duì)象供依賴對(duì)象注入的。
作用域?qū)崿F(xiàn)原理以及如何自定義作用域
作用域?qū)崿F(xiàn)原理
在Spring中主要有reqest、session、singleton、prototype等等幾種作用域,前面我們分析了singleton創(chuàng)建bean的原理,是通過緩存來實(shí)現(xiàn)的,那么其它的呢?還是回到AbstractBeanFactory.doGetBean方法中來:
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
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;
}
});
// 該方法是FactoryBean接口的調(diào)用入口
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
// 該方法是FactoryBean接口的調(diào)用入口
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
// 該方法是FactoryBean接口的調(diào)用入口
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
}
在singleton作用域下,會(huì)調(diào)用getSingleton方法,然后回調(diào)createBean創(chuàng)建對(duì)象,最終在getSingleton中完成緩存;而當(dāng)scope為prototype時(shí),可以看到是直接調(diào)用了createBean方法并返回,沒有任何的緩存操作,因此每次調(diào)用getBean都會(huì)創(chuàng)建新的對(duì)象,即使是同一個(gè)線程;除此之外都會(huì)進(jìn)入到else片段中。
這個(gè)代碼也很簡單,首先通過我們配置的scopeName從scopes中拿到對(duì)應(yīng)的Scope對(duì)象,如SessionScope和RequestScope(但這兩個(gè)只會(huì)在Web環(huán)境中被加載,在WebApplicationContextUtils.registerWebApplicationScopes可以看到注冊(cè)操作),然后調(diào)用對(duì)應(yīng)的get方法存到對(duì)應(yīng)的request或session對(duì)象中去。代碼很簡單,這里就不分析了。
自定義Scope
通過以上分析,不難發(fā)現(xiàn)我們是很容易實(shí)現(xiàn)一個(gè)自己的Scope的,首先實(shí)現(xiàn)Scope接口,然后將我們類的實(shí)例添加到scopes緩存中來,關(guān)鍵是怎么添加呢?在AbstractBeanFactory類中有一個(gè)registerScope方法就是干這個(gè)事的,因此我們只要拿到一個(gè)BeanFactory對(duì)象就行了,那要怎么拿?還記得在refresh中調(diào)用的invokeBeanFactoryPostProcessors方法么?因此我們只需要實(shí)現(xiàn)BeanFactoryPostProcessor接口就可以了,是不是So Easy!
BeanPostProcessor的執(zhí)行時(shí)機(jī)
BeanPostProcessor執(zhí)行點(diǎn)很多,根據(jù)其接口類型在不同的位置進(jìn)行調(diào)用,只有熟記其執(zhí)行時(shí)機(jī),才能更好的進(jìn)行擴(kuò)展,這里以一張時(shí)序圖來總結(jié):

SpringBoot零配置實(shí)現(xiàn)原理淺析
在SpringBoot項(xiàng)目中,省去了大量繁雜的xml配置,只需要使用@ComponentScan、@Configuration以及@Bean注解就可以達(dá)到和使用xml配置的相同效果,大大簡化了我們的開發(fā),那這個(gè)實(shí)現(xiàn)原理是怎樣的呢?熟悉了xml解析原理,相信對(duì)于這種注解的方式基本上也能猜個(gè)大概。
首先我們進(jìn)入到AnnotationConfigApplicationContext類,這個(gè)就是注解方式的IOC容器:
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
這里ClassPathBeanDefinitionScanner在解析xml時(shí)出現(xiàn)過,就是用來掃描包找到合格的資源的;同時(shí)還創(chuàng)建了一個(gè)AnnotatedBeanDefinitionReader對(duì)象對(duì)應(yīng)XmlBeanDefinitionReader,用來解析注解:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
在AnnotatedBeanDefinitionReader構(gòu)造方法中可以看到調(diào)用了registerAnnotationConfigProcessors注冊(cè)一些列注解解析的Processor類,重點(diǎn)關(guān)注ConfigurationClassPostProcessor類,該類是BeanDefinitionRegistryPostProcessor的子類,所以會(huì)在refresh中調(diào)用,該類又會(huì)委托ConfigurationClassParser去解析@Configuration、@Bean、@ComponentScan等注解,所以這兩個(gè)類就是SpringBoot實(shí)現(xiàn)零配置的關(guān)鍵類,實(shí)現(xiàn)和之前分析的注解解析流程差不多,所以具體的實(shí)現(xiàn)邏輯讀者請(qǐng)自行分析。
回頭看當(dāng)解析器和掃描器創(chuàng)建好后,同樣是調(diào)用scan方法掃描包,然后refresh啟動(dòng)容器,所以實(shí)現(xiàn)邏輯都是一樣的,殊途同歸,只不過通過父子容器的構(gòu)造方式使得我們可以很方便的擴(kuò)展Spring。
總結(jié)
本篇是關(guān)于IOC實(shí)現(xiàn)的一些補(bǔ)充,最重要的是要理解循環(huán)依賴的解決辦法,其次SpringBoot零配置實(shí)現(xiàn)原理雖然這里只是簡單起了個(gè)頭,但需要好好閱讀源碼分析。另外還有很多細(xì)節(jié),不可能全都講到,需要我們自己反復(fù)琢磨,尤其是Bean實(shí)例化那一塊,這將是后面我們理解AOP的基礎(chǔ)。希望大家多多支持腳本之家。
相關(guān)文章
Java正則表達(dá)式,提取雙引號(hào)中間的部分方法
今天小編就為大家分享一篇Java正則表達(dá)式,提取雙引號(hào)中間的部分方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07
Springboot攔截器如何獲取@RequestBody參數(shù)
這篇文章主要介紹了Springboot攔截器如何獲取@RequestBody參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
如何讓W(xué)in10實(shí)現(xiàn)Java文件的開機(jī)自啟動(dòng)
這篇文章主要介紹了如何讓W(xué)in10實(shí)現(xiàn)Java文件的開機(jī)自啟動(dòng),對(duì)于一些想要一直運(yùn)行的Java文件,就會(huì)造成每次系統(tǒng)更新之后的重啟導(dǎo)致Java文件無法繼續(xù)運(yùn)行。,需要的朋友可以參考下2019-06-06
java運(yùn)行jar包提示?“XXX中沒有主清單屬性”?"找不到主類”兩種解決辦法
本文主要介紹了java運(yùn)行jar包提示?“XXX中沒有主清單屬性”?"找不到主類”兩種解決辦法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Spring學(xué)習(xí)之開發(fā)環(huán)境搭建的詳細(xì)步驟
本篇文章主要介紹了Spring學(xué)習(xí)之開發(fā)環(huán)境搭建的詳細(xì)步驟,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07

