亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何解析Spring循環依賴源碼實現

發布時間:2021-12-18 15:02:41 來源:億速云 閱讀:140 作者:柒染 欄目:大數據

如何解析Spring循環依賴源碼實現,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

一、概述

我這幾天總結了一下,為了能夠讓讀者更加的去了解Spring解決循環依賴問題,我決定分析Spring解決循環依賴問題。

二、什么是循環依賴

循環依賴直白點就是發生在兩個類,你引用我,我引用你的狀態,如圖:

如何解析Spring循環依賴源碼實現      
循環依賴示意圖
   

三、如果不依賴于Spring自己解決循環依賴如何解決

以上圖為例,假設,我們能夠創建完成AService之后,放置到到一個緩存中,再去注入屬性!每次注入屬性的時候,所需要的屬性值都從緩存中獲取一遍,緩存中沒有再去創建不就解決了?如圖所示:

如何解析Spring循環依賴源碼實現    

總結一下上面的流程:

  1. AService創建完成后將自己加入到二級緩存,然后開始注入屬性
  2. 發現 AService依賴 BService于是先查詢一級緩存是否有數據一級緩存沒有就查詢二級緩存,有就返回,沒有就創建 BService
  3. 緩存中沒有,開始實例化 BService,然后注入內部屬性!
  4. 注入內部屬性時發現依賴 AService,于是先查詢一級緩存是否有數據一級緩存沒有就查詢二級緩存,有就返回,沒有就創建,很顯然,二級緩存是有數據的。于是從二級緩存取出 AService注入到 BService
  5. BService創建完成后將自己從二級緩存挪到一級緩存,并返回。
  6. AService獲取到 BService后,注入到自己的屬性中并把自己從二級緩存挪的一級緩存,返回 AService!
  7. 至此,循環依賴創建完成!

那么有了上面的思路,我們如何用代碼實現一遍我們的邏輯呢?

   

四、如果不依賴于Spring自己解決循環依賴如何解決

首先,我么肯定要定義一個類似于@Autowired這樣的注解,這里我們叫做    @MyAutowired

package simulation.annotations;

import java.lang.annotation.*;

/**
* 自定義注入注解 相當于 Spring的@Autowired
* @author huangfu
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Inherited
public @interface MyAutowired {
}
   

然后我們需要模擬一個循環引用

package simulation.service;

import simulation.annotations.MyAutowired;

public class AService {
   @MyAutowired
   private BService bService;
}
   
package simulation.service;

import simulation.annotations.MyAutowired;

public class BService {

   @MyAutowired
   private AService aService;
}
   

以上,我們定義了一個循環引用,AService引用BService;而且BService引用AService,標準的循環引用

然后,我們就根據(三)說的思路,我們去用代碼解決

package simulation;

import simulation.annotations.MyAutowired;
import simulation.service.AService;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* 模擬Spring解決循環依賴的問題
* @author huangfu
*/
public class DebugTest {

   /**
    * 已經完全創建好的
    */
   private final Map<String,Object> singletonObject = new HashMap<>(8);
   /**
    * 創建一半但是沒有屬性注入的
    */
   private final Map<String,Object> earlySingletonObjects = new HashMap<>(8);

   public static void main(String[] args) throws IllegalAccessException, InstantiationException {
       DebugTest debugTest = new DebugTest();
       AService bean = debugTest.getBean(AService.class);
       System.out.println(bean);
   }

   /**
    * 獲取一個bean對象
    * @param tClass
    * @return
    */
   public <T> T getBean(Class<T> tClass) throws InstantiationException, IllegalAccessException {
       //先查詢一級緩存是否有數據
       String beanName = getBeanName(tClass);
       Object object = singletonObject.get(beanName);
       //一級緩存沒有在查詢二級緩存是否有數據
       if(object == null){
           object = earlySingletonObjects.get(beanName);
           if(object == null) {
            //兩個緩存都沒有就創建類
               object = createBean(tClass,beanName);
           }
       }
       return (T)object;
   }

   /**
    * 創建一個bean
    * @param tClass
    * @param beanName
    * @return
    */
   public Object createBean(Class<?> tClass,String beanName) throws IllegalAccessException, InstantiationException {
       //反射創建對象
       Object newInstance = tClass.newInstance();
       //實例化完就放到二級緩存
       earlySingletonObjects.put(beanName,newInstance);
       //開始填充屬性
       populateBean(newInstance);
       //填充完成后從創作中的集合轉移到完全體集合
       earlySingletonObjects.remove(beanName);
       singletonObject.put(beanName,newInstance);
       return newInstance;
   }

   /**
    * 填充屬性
    */
   public void populateBean(Object object) throws InstantiationException, IllegalAccessException {
    //獲取所有添加了 @MyAutowired 注解的屬性
       List<Field> autowiredFields = getAutowiredField(object.getClass());
       for (Field field : autowiredFields) {
        //開始注入
           doPopulateBean(object, field);
       }
   }

   /**
    * 開始注入對象
    * @param object
    * @param field
    */
   public void doPopulateBean(Object object, Field field) throws IllegalAccessException, InstantiationException {
    //重新調用獲取邏輯
       Object target = getBean(field.getType());
       field.setAccessible(true);
       //反射注入
       field.set(object,target);
   }

   /**
    * 獲取被標識自動注入的屬性
    * @param tClass
    * @return
    */
   private List<Field> getAutowiredField(Class<?> tClass){
       Field[] declaredFields = tClass.getDeclaredFields();
       return Arrays.stream(declaredFields).filter(field ->
                              ield.isAnnotationPresent(MyAutowired.class)).collect(Collectors.toList());
   }
   /**
    * 獲取類名稱
    * @param tClass
    * @return
    */
   public String getBeanName(Class<?> tClass){
       return tClass.getSimpleName();
   }
}
   

結果

如何解析Spring循環依賴源碼實現      
image-20200729225238673

由上面的結果圖示,我們解決了循環依賴,事實上Spring的解決方案,和我們手寫的類似,但是Spring作為一個生態,它的設計和編碼也是考慮的極其周全的,我們這樣寫雖然和Spring的最初想法是類似的,但是會出現哪些問題呢?

   

五、自己實現的方式有什么缺陷?

我們現在是直接注入類的對象,假設我們換了一種邏輯,如果我們注入的目標對象,是一個需要被代理的對象(比如該方法被AOP代理),我們這種寫法就無能為力了,當然我們可以再創建的時候進行判斷是否需要增加代理,當然這是一種方案,但是對于Spring而言,他的初衷是希望在bean生命周期的最后幾步才去aop,再注入的時候就把該對象的代理邏輯給做完了,很顯然不符合它的設計理念,那么Spring到底是如何解決的呢?

   

六、Spring中是如何解決循環依賴的?

首先,我們需要找到類在哪里實例化的,因為只有實例化了,才會執行注入的邏輯!

入口方法:

org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

@Override
public void preInstantiateSingletons() throws BeansException {
//遍歷一個副本以允許使用init方法,這些方法依次注冊新的bean定義。
//盡管這可能不是常規工廠引導程序的一部分,但可以正常運行。
//這里獲取所有的bean name
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

// 觸發所有非惰性單例bean的初始化...
for (String beanName : beanNames) {
//獲取該類的詳細定義
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//實例化條件 第一不是抽象的類  第二是單例的類  第三不是懶加載的類
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//哦吼  這里引申出來一個概念 當這個bean集成了beanname那么就不再走bean生命周期的實例化了 直接創建
if (isFactoryBean(beanName)) {
....忽略不必要代碼,正常bean的初始化不會走這里...
} else {
//普通的bean  這里就是再創建Spring bean的實體對象的,這里也是我們探究最重要的一個邏輯
getBean(beanName);
}
}
}
       ......忽略忽略.....
   }
}
   

這一步主要就是getBean,你試想一下,按照Spring的命名規范,這里明明是在創建Bean為什么就起個名字叫getBean呢?他這么做肯定是有他的用意所在,他這么起名是因為,在屬性注入的時候,發現依賴某一個屬性并不會立即創建,而是會調用這個方法獲取一遍,沒有再去創建!不明白沒關系,你記住這個地方,往下看!方法進入到getBean --> doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

final String beanName = transformedBeanName(name);
Object bean;

// 檢查單例緩存是否有手動注冊的單例。
//檢查一級緩存內是否有該單例bean的對象
//當一級緩存沒有 而卻當前的bean為創建中的狀態時(實例化完成但是沒有初始化),檢查二級緩存對象,有就返回
//當二級緩存沒有 檢查三級緩存,調用三級緩存的匿名內部類的回調方法獲取bean對象,放置到二級緩存,刪除三級緩存的該數據  返回當前bean
//從三級緩存取的原因是因為如果該類為依賴類,并且被設置了代理,則再該方法內部獲取的就是代理對象,保證注入時,第一次獲取的就是一個代理對象
//事實上 如果是循環引用,被引用對象再注入屬性時三級緩存已經存在,就會使用三級緩存的工廠對象,返回該bean該做代理的時候做代理,沒代理的話直接返回
Object sharedInstance = getSingleton(beanName);
//當發生循環依賴時,第一次查詢該對象返回的數據一定為null
if (sharedInstance != null && args == null) {
....忽略不必要代碼....
} else {
....忽略不必要代碼....
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
....忽略不必要代碼,這里主要做一些判斷,比如實例化時的依賴(@dependsOn)等....

// 創建bean實例。這個是個真正的創建bean實例的方法   單例池獲取,沒有的話就將該bean加入到正在創建  然后走創建bean的回調
if (mbd.isSingleton()) {
                   //這個方法很重要,方法內部會做這樣幾件事:
//1.判斷當前的一級緩存里面有沒有bean
//2.沒有就回調java8里面的回調方法(createBean)創建方法,添加到一級緩存,返回bean
//3.一級緩存存在就直接返回該bean
sharedInstance = getSingleton(beanName, () -> {
try {
//這里是真正的創建bean的邏輯,由 {@link #getSingleton} 方法回調該對象去走真正的執行創建bean的邏輯
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 從單例緩存中顯式刪除實例:它可能已經放在那里
// 急于通過創建過程,以允許循環引用解析。
// 還刪除所有收到對該bean的臨時引用的bean。
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
....忽略不必要代碼....
} else {
....忽略不必要代碼....
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}

if (requiredType != null && !requiredType.isInstance(bean)) {
....忽略不必要代碼....
}
return (T) bean;
}
   
  • 之前手寫了一遍解決循環依賴的代碼,這里是不是很熟悉?這就是在緩存里面尋找對應的bean,當緩存有的時候直接返回,沒有的時候才去創建!相信聰明的你一定若有所思!  這里極其重要,咱們進入到 createBean里面
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                                                                               throws BeanCreationException {
  ....忽略不必要代碼....
  try {
     //真正干活的方法來了  呵呵呵呵   反射創建bean
     Object beanInstance = doCreateBean(beanName, mbdToUse, args);
     //....忽略不必要代碼....
     return beanInstance;
  }
  catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
     //先前檢測到的具有正確的bean創建上下文的異常,
     //或非法的單例狀態,最多可以傳達給DefaultSingletonBeanRegistry。
     throw ex;
  }
  catch (Throwable ex) {
     throw new BeanCreationException(
           mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
  }
}
   
  • 進入到 doCreateBean里面
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {

// 實例化bean。
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//開始創建bean 的邏輯  這里實際上該類已經被實例化了 只不過返回的是一個包裝對象,包裝對象內部存在該實例化好的對象
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//獲取之前創建的bean
final Object bean = instanceWrapper.getWrappedInstance();
   
....忽略不必要代碼....

       //判斷當前這個對象是不是單例   是不是支持循環引用  是不是正在創建中 滿足這幾個條件才會放置到三級緩存
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences
                                         &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
....忽略不必要代碼....
               
//這個方法時將當前實例號的bean放置到三級緩存 三級緩存內部存放的時 beanName -> bean包裝對象  這個樣的kv鍵值對
//設置這個方法的目的時 Spring設計時是期望Spring再bean實例化之后去做代理對象的操作,而不是再創建的時候就判斷是否 是代理對象
//但實際上如果發生了循環引用的話,被依賴的類就會被提前創建出來,并且注入到目標類中,為了保證注入的是一個實際的代理對象
           //所以Spring來了個偷天換日,偷梁換柱
//后續需要注入的時候,只需要通過工廠方法返回數據就可以了,在工廠里面可以做代理相關的操作,執行完代理操作后,在返回對象
//符合了Spring設計時,為了保證代理對象的包裝再Springbean生命周期的后幾步來實現的預期
//這一步還會刪除二級緩存的數據
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// 初始化bean實例。
Object exposedObject = bean;
try {
//填充內部的屬性
//☆這一步解決了循環依賴的問題,在這里發生了自動注入的邏輯
populateBean(beanName, mbd, instanceWrapper);
//執行初始化的邏輯  以及生命周期的回調
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
....忽略不必要代碼....
}

if (earlySingletonExposure) {
....忽略不必要代碼....
}
....忽略不必要代碼....
return exposedObject;
}
   
  • 進入到 populateBean 方法,這里執行屬性注入,同時也解決了循環依賴!
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
....忽略不必要代碼....

// 給任何InstantiationAwareBeanPostProcessors修改機會,
// 設置屬性之前Bean的狀態。例如,可以使用它
// 支持場注入方式。
boolean continueWithPropertyPopulation = true;

if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
....忽略不必要代碼....
}
}

if (!continueWithPropertyPopulation) {
return;
}
....忽略不必要代碼....
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
//因為是使用@Autowired注解做的自動注入
// 故而Spring會使用 AutowiredAnnotationBeanPostProcessor.postProcessProperties來處理自動注入
                   //事實上這一步是會做注入處理的,這個也是我們重點觀察的對象
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
....忽略不必要代碼....
}
}
}
if (needsDepCheck) {
....忽略不必要代碼....
}

if (pvs != null) {
//開始設置屬性值  mbd是依賴的bean
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
   
  • 進入到 AutowiredAnnotationBeanPostProcessor.postProcessProperties
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
....忽略不必要代碼....
try {
//注入邏輯
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
   
  • 進入到 inject
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   Collection<InjectedElement> checkedElements = this.checkedElements;
   Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);
   if (!elementsToIterate.isEmpty()) {
       for (InjectedElement element : elementsToIterate) {
           ....忽略不必要代碼....
           //注入邏輯發生的實際代碼 因為是屬性注入,所以 使用AutowiredFieldElement.inject
           element.inject(target, beanName, pvs);
       }
   }
}
   
  • 進入到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   //獲取需要注入的屬性對象
   Field field = (Field) this.member;
   Object value;
   ......忽略不必要代碼......
   else {
       ......忽略不必要代碼......
       try {
           //真正的解決依賴的代碼,查找依賴創建依賴的代碼
           value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
       }
       catch (BeansException ex) {
           throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
       }
       ......忽略不必要代碼......
   }
   if (value != null) {
       //反射的注入邏輯
       ReflectionUtils.makeAccessible(field);
       field.set(bean, value);
   }
}
   
  • 此時別說你們我都想說一句 wo cao終于看到希望了,這里由 beanFactory.resolveDependency獲取即將要注入的對象,然后后面通過反射注入到對象里面去,我們是不是只需要知道 beanFactory.resolveDependency里面的邏輯就可以知道循環依賴的問題了?我們果斷進去看看果然發現還沒完:
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
......忽略不必要代碼......
if (result == null) {
//解決依賴性 這個是實際干活的方法
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
   

進入到 doResolveDependency

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

......忽略不必要代碼......
//根據類型和名稱查詢該bean的數據
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
......忽略不必要代碼......
//這一步是真正創建一個類這里面會調用getBean方法重新的走上面的那一套創建bean的邏輯
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
......忽略不必要代碼......
return result;
}
......忽略不必要代碼......
}
   
  • 恭喜你熬到頭了,我們進入到 descriptor.resolveCandidate(autowiredBeanName, type, this)方法:

org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {

   return beanFactory.getBean(beanName);
}
   

哦吼,gentBean ,相信大家一定失憶了,你是不是在哪見過? 想想上文我讓你記住的那個地方,里面是不是也是一個getBean,沒錯,他們倆是同一個方法,你會發現,最終需要注入的屬性也會走一遍上述的邏輯從而完成屬性對象的創建和獲取,從而完成整個循環依賴!借用YourBatman大佬的一張圖,總結一下整個解決三級緩存的邏輯

   

七、總結

如何解析Spring循環依賴源碼實現      

看完上述內容,你們掌握如何解析Spring循環依賴源碼實現的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

迁西县| 渝北区| 九寨沟县| 绍兴市| 郴州市| 定西市| 格尔木市| 报价| 浦北县| 柳州市| 运城市| 吉木萨尔县| 麻江县| 五莲县| 张北县| 鄢陵县| 岫岩| 淮安市| 武平县| 绥德县| 南京市| 罗甸县| 松江区| 兰考县| 肥东县| 如东县| 张家口市| 盐山县| 海口市| 东莞市| 泰兴市| 唐海县| 阿瓦提县| 彝良县| 调兵山市| 庄浪县| 新泰市| 斗六市| 唐河县| 泸西县| 都兰县|