日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring IoC 源码系列(四)bean创建流程与循环依赖问题分析

發布時間:2024/9/30 javascript 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring IoC 源码系列(四)bean创建流程与循环依赖问题分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

創建單例 bean 的代碼細節在 org.springframework.beans.factory.support.AbstractBeanFactory#getBean 中,getBean 顧名思義是獲取 bean 的流程,如果 bean 不存在的話會先創建,創建前與創建后的具體流程在下一篇文章里進行總結。這里只對創建 bean 的流程進行詳細的分析。

1.入口

在 AbstractBeanFactory 有一個方法 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,這個方法很長,這里只貼出了創建單例 bean 相關的代碼。

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {......if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {// 創建 beanreturn createBean(beanName, mbd, args);}catch (BeansException ex) {// 創建過程中出現異常銷毀 beandestroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}......}

2.creatBean 方法預覽

mbd 是融合了父子容器配置的 beanDefinition,關于父子容器配置可以參考:Spring中的父子容器。

@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {RootBeanDefinition mbdToUse = mbd;// 獲取 beanDefinition 中的 class 屬性Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}try {// 覆蓋方法(meta、lookup-method、replace-method)校驗,判斷是否該方法是否重載mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.// 實例化對象前的處理,且可以創建代理 bean 對象,創建成功直接返回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 {// 創建 beanObject beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}}
  • 從 beanDefinition 中解析 bean class
  • 覆蓋方法校驗,主要是 lookup-method、replace-method,判斷是否有這兩個方法的重載
  • 判斷實例化之前是否需要創建代理 bean,如果需要,則通過 InstantiationAwareBeanPostProcessor 提前創建 bean
  • 創建 bean 實例
  • Spring 提供了lookup-method,replace-method 兩種類型類型的 overrides 方法,基于一種可拔插的方式改變 bean 的方法,平時在項目中很少會用到,具體的用法可以參考:Spring高級配置:lookup-method, replaced-method。

    3.doCreateBean 創建 bean

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 使用 BeanWrapper 包裝創建的 beanif (instanceWrapper == null) {// 創建 beaninstanceWrapper = createBeanInstance(beanName, mbd, args);}// 獲取包裝的 bean 實例對象(暫未填充屬性)與 class 對象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 {// 如果有配置 MergedBeanDefinitionPostProcessor 類型的后置處理,則修改 beanDefinitionapplyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// earlySingletonExposure 表示是否提前曝光 bean,可以用來解決循環依賴boolean earlySingletonExposure = (mbd.isSingleton() // 單例模式&& this.allowCircularReferences && // 允許循環依賴isSingletonCurrentlyInCreation(beanName)); // 當前 bean 正在被創建if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 將工廠對象放入 singletonFactories 中,可以用來解決循環依賴addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.Object exposedObject = bean;try {// 對 bean 進行填充,將各個屬性值注入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) {// 從緩存中獲取提前暴露的 beanObject earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {// 這個 bean 可能不是完整的 beanexposedObject = 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;}
  • 創建 bean,并使用 BeanWrapper 進行包裝
  • 執行 MergedBeanDefinitionPostProcessor 后置處理
  • 判斷是否需要提前曝光 bean,如果需要,將 bean 加入到 singletonFactories map 緩存中,可以用來解決 bean 之間的循環依賴問題
  • 通過 populateBean 方法填充 bean 依賴的屬性
  • 激活 bean 的…Aware 配置,執行 BeanPostProcessor 的前置處理,執行 InitializingBean 回調,執行 BeanPostProcessor 的后置處理,這幾個過程都不是必須的,如果沒有配置是不會執行的
  • 注冊銷毀回調
  • 這個方法處理的流程很多,其中 createBeanInstance 方法用于創建 bean 實例,這里先不分析創建 bean 的具體細節,感興趣的可以關注后面的總結。

    4.bean 之間的循環依賴

    這里可以引申出一個面試中長問的知識點:Spring 是如何解決 bean 之間的循環依賴。下面來分析一下:

    上面的方法中用 earlySingletonExposure 判斷這個 bean 是否需要提前曝光出去,如果需要提前曝光會執行 addSingletonFactory 方法,將未填充依賴的 bean 保存到 singletonFactories 中,接著會執行 populateBean 方法,用于注入 bean 依賴的屬性。

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}

    假如 A 依賴 B,而 B 又依賴與 A,且 A 需要提前暴露,那么 A 會被保存到 singletonFactories 中,此時 A 還不是完整的 bean,因為還沒有注入依賴的 B,接著執行 populateBean 方法,發現 A 依賴與 B,于是會通過 getBean 方法創建 B,getBean 調用 doGetBean,而在 doGetBean 方法中會提前調用 getSingleton 方法,判斷該 bean 是否已經被創建。下面是具體的代碼

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {......Object sharedInstance = getSingleton(beanName);......if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}......}@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {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;}

    此時 B 還沒有創建,于是執行初始化操作,在 B 注入依賴的時候發現依賴于 A 對象,于是調用 getBean 去獲取 A 對象,當指定到 getSingleton 方法時會從 singletonFactories 中獲取到提前暴露的 A 對象,這樣 B 依賴與 A 的問題就解決了,此時 B 也已經創建完畢,于是 A 就注入了一個初始化完全的 B 對象。

    5.initializeBean 處理流程

    這個方法會執行所有的 BeanPostProcessor,這里把代碼流程提出來供大家參考。

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {// 激活 Aware 方法,對特殊的 bean 處理:Aware、BeanClassLoaderAware、BeanFactoryAwareinvokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {invokeAwareMethods(beanName, bean);}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {// 前置處理,用戶可以實現 BeanPostProcessor 進行自定義業務處理wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {// 如果用戶實現了 InitializingBean 接口或者自定義了 init 方法,進行回調invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {// 后置處理wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}

    參考

    Spring IOC 容器源碼分析 - 創建單例 bean 的過程

    總結

    以上是生活随笔為你收集整理的Spring IoC 源码系列(四)bean创建流程与循环依赖问题分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。