溫馨提示×

溫馨提示×

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

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

如何解析Spring循環(huán)依賴源碼實現

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

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

一、概述

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

二、什么是循環(huán)依賴

循環(huán)依賴直白點就是發(fā)生在兩個類,你引用我,我引用你的狀態(tài),如圖:

如何解析Spring循環(huán)依賴源碼實現      
循環(huán)依賴示意圖
   

三、如果不依賴于Spring自己解決循環(huán)依賴如何解決

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

如何解析Spring循環(huán)依賴源碼實現    

總結一下上面的流程:

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

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

   

四、如果不依賴于Spring自己解決循環(huán)依賴如何解決

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

package simulation.annotations;

import java.lang.annotation.*;

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

然后我們需要模擬一個循環(huán)引用

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;
}
   

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

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

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解決循環(huán)依賴的問題
* @author huangfu
*/
public class DebugTest {

   /**
    * 已經完全創(chuàng)建好的
    */
   private final Map<String,Object> singletonObject = new HashMap<>(8);
   /**
    * 創(chuàng)建一半但是沒有屬性注入的
    */
   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) {
            //兩個緩存都沒有就創(chuàng)建類
               object = createBean(tClass,beanName);
           }
       }
       return (T)object;
   }

   /**
    * 創(chuàng)建一個bean
    * @param tClass
    * @param beanName
    * @return
    */
   public Object createBean(Class<?> tClass,String beanName) throws IllegalAccessException, InstantiationException {
       //反射創(chuàng)建對象
       Object newInstance = tClass.newInstance();
       //實例化完就放到二級緩存
       earlySingletonObjects.put(beanName,newInstance);
       //開始填充屬性
       populateBean(newInstance);
       //填充完成后從創(chuàng)作中的集合轉移到完全體集合
       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循環(huán)依賴源碼實現      
image-20200729225238673

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

   

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

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

   

六、Spring中是如何解決循環(huán)依賴的?

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

入口方法:

org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization

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

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

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

這一步主要就是getBean,你試想一下,按照Spring的命名規(guī)范,這里明明是在創(chuàng)建Bean為什么就起個名字叫getBean呢?他這么做肯定是有他的用意所在,他這么起名是因為,在屬性注入的時候,發(fā)現依賴某一個屬性并不會立即創(chuàng)建,而是會調用這個方法獲取一遍,沒有再去創(chuàng)建!不明白沒關系,你記住這個地方,往下看!方法進入到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為創(chuàng)建中的狀態(tài)時(實例化完成但是沒有初始化),檢查二級緩存對象,有就返回
//當二級緩存沒有 檢查三級緩存,調用三級緩存的匿名內部類的回調方法獲取bean對象,放置到二級緩存,刪除三級緩存的該數據  返回當前bean
//從三級緩存取的原因是因為如果該類為依賴類,并且被設置了代理,則再該方法內部獲取的就是代理對象,保證注入時,第一次獲取的就是一個代理對象
//事實上 如果是循環(huán)引用,被引用對象再注入屬性時三級緩存已經存在,就會使用三級緩存的工廠對象,返回該bean該做代理的時候做代理,沒代理的話直接返回
Object sharedInstance = getSingleton(beanName);
//當發(fā)生循環(huán)依賴時,第一次查詢該對象返回的數據一定為null
if (sharedInstance != null && args == null) {
....忽略不必要代碼....
} else {
....忽略不必要代碼....
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
....忽略不必要代碼,這里主要做一些判斷,比如實例化時的依賴(@dependsOn)等....

// 創(chuàng)建bean實例。這個是個真正的創(chuàng)建bean實例的方法   單例池獲取,沒有的話就將該bean加入到正在創(chuàng)建  然后走創(chuàng)建bean的回調
if (mbd.isSingleton()) {
                   //這個方法很重要,方法內部會做這樣幾件事:
//1.判斷當前的一級緩存里面有沒有bean
//2.沒有就回調java8里面的回調方法(createBean)創(chuàng)建方法,添加到一級緩存,返回bean
//3.一級緩存存在就直接返回該bean
sharedInstance = getSingleton(beanName, () -> {
try {
//這里是真正的創(chuàng)建bean的邏輯,由 {@link #getSingleton} 方法回調該對象去走真正的執(zhí)行創(chuàng)建bean的邏輯
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 從單例緩存中顯式刪除實例:它可能已經放在那里
// 急于通過創(chuàng)建過程,以允許循環(huán)引用解析。
// 還刪除所有收到對該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;
}
   
  • 之前手寫了一遍解決循環(huán)依賴的代碼,這里是不是很熟悉?這就是在緩存里面尋找對應的bean,當緩存有的時候直接返回,沒有的時候才去創(chuàng)建!相信聰明的你一定若有所思!  這里極其重要,咱們進入到 createBean里面
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                                                                               throws BeanCreationException {
  ....忽略不必要代碼....
  try {
     //真正干活的方法來了  呵呵呵呵   反射創(chuàng)建bean
     Object beanInstance = doCreateBean(beanName, mbdToUse, args);
     //....忽略不必要代碼....
     return beanInstance;
  }
  catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
     //先前檢測到的具有正確的bean創(chuàng)建上下文的異常,
     //或非法的單例狀態(tài),最多可以傳達給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) {
//開始創(chuàng)建bean 的邏輯  這里實際上該類已經被實例化了 只不過返回的是一個包裝對象,包裝對象內部存在該實例化好的對象
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//獲取之前創(chuàng)建的bean
final Object bean = instanceWrapper.getWrappedInstance();
   
....忽略不必要代碼....

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

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

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

// 給任何InstantiationAwareBeanPostProcessors修改機會,
// 設置屬性之前Bean的狀態(tài)。例如,可以使用它
// 支持場注入方式。
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) {
           ....忽略不必要代碼....
           //注入邏輯發(fā)生的實際代碼 因為是屬性注入,所以 使用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 {
           //真正的解決依賴的代碼,查找依賴創(chuàng)建依賴的代碼
           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里面的邏輯就可以知道循環(huán)依賴的問題了?我們果斷進去看看果然發(fā)現還沒完:
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);
......忽略不必要代碼......
//這一步是真正創(chuàng)建一個類這里面會調用getBean方法重新的走上面的那一套創(chuàng)建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,沒錯,他們倆是同一個方法,你會發(fā)現,最終需要注入的屬性也會走一遍上述的邏輯從而完成屬性對象的創(chuàng)建和獲取,從而完成整個循環(huán)依賴!借用YourBatman大佬的一張圖,總結一下整個解決三級緩存的邏輯

   

七、總結

如何解析Spring循環(huán)依賴源碼實現      

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

向AI問一下細節(jié)

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

AI