欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBean依賴和三級緩存的案例講解

 更新時間:2021年02月27日 16:08:11   作者:酒劍隨馬@  
這篇文章主要介紹了SpringBean依賴和三級緩存的案例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

spring中的bean依賴有大體上可以分為兩類,共3中形式,下面簡單介紹一下。

第一類是構造方法中的循環(huán)依賴,這種會報錯

@Service
public class ServiceA { 
  private ServiceB serviceB; 
  public ServiceA(ServiceB serviceB) {
    this.serviceB = serviceB;
  } 
  public void methodA(){
    System.out.println("a");
  }
}
 
@Service
public class ServiceB { 
  private ServiceA serviceA; 
  public ServiceB(ServiceA serviceA) {
    this.serviceA = serviceA;
  } 
  public void methodB(){
    System.out.println("b");
  }
}
 
//錯誤提示
Description: 
The dependencies of some of the beans in the application context form a cycle:
 
┌─────┐
| serviceA defined in file [C:\demo\target\classes\com\example\demo\ServiceA.class]
↑   ↓
| serviceB defined in file [C:\demo\target\classes\com\example\demo\ServiceB.class]
└─────┘

第二類是field循環(huán)依賴,它分為兩種,第一類循環(huán)依賴的作用域scope默認是singleton,啟動不會報錯

@Service
public class ServiceA { 
  @Autowired
  private ServiceB serviceB;
 
  public void methodA(){
    System.out.println("a");
  }
}
 
@Service
public class ServiceB { 
  @Autowired
  private ServiceA serviceA;
 
  public void methodB(){
    System.out.println("b");
  }
}

第二種作用域scope為prototype,在這種情況下bean是多例的,按理說這種啟動也會報錯,但是它成功了。。我也不知道為啥

@Service
@Scope("prototype")
public class ServiceA { 
  @Autowired
  private ServiceB serviceB;
 
  public void methodA(){
    System.out.println("a");
  }
}
 
@Service
@Scope("prototype")
public class ServiceB { 
  @Autowired
  private ServiceA serviceA;
 
  public void methodB(){
    System.out.println("b");
  }
}
 

據(jù)我在網(wǎng)上查找的資料,spring可以幫我們處理bean的scope為singleton的field循環(huán)依賴,個人感覺應該也是這樣,下面說一下它的處理過程。

簡單說一下bean的加載過程,當spring啟動的時候,首先加載進beanFactory的是beanDefinition,之后會根據(jù)beanDefinition判斷其是否為sington并且為非抽象類非懶加載,那么之后會去創(chuàng)建bean,

bean的創(chuàng)建分為三步:

1.調用構造方法創(chuàng)建對象實例(這一步完成之后其它對象實例就可以引用它)

2.填充實例內(nèi)部屬性(會依次從三級緩存中獲取依賴的bean,如果沒有找到,則會先去創(chuàng)建依賴的bean,之后再返回繼續(xù)填充屬性)

3.執(zhí)行initializeBean方法,進行初始化

當bean進行創(chuàng)建時,會先調用getbean方法->執(zhí)行doGetBean方法,在doGetBean方法中會調用getSingleton方法,這一步就是從三級緩存中獲取對象緩存,因為是剛開始創(chuàng)建bean所以緩存中肯定沒有,之后會調用createBean方法,在createBean方法中會調用doCreateBean執(zhí)行bean的創(chuàng)建過程就是上面的那三步,當bean創(chuàng)建成功之后會將其放入一級緩存之中,此時會將它從三級和二級緩存中刪除。

public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
  }
 
  doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
 
    //從緩存中獲取實例
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      //省略代碼
    } else {
      //省略代碼
      createBean(beanName, mbd, args);
    }
    //將創(chuàng)建完成的bean放入一級緩存
    addSingleton(beanname,object)
  }
 
  protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    //省略代碼
    doCreateBean()
  }
 
  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
    //根據(jù)構造方法創(chuàng)建對象實例
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();
 
    //將對象實例放入第三級緩存中
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    //實例內(nèi)部屬性填充
    populateBean(beanName, mbd, instanceWrapper);
 
    //初始化bean
    exposedObject = initializeBean(beanName, exposedObject, mbd);
 
    return exposedObject;
  }
 
  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;
  }

其中我們可以看到當?shù)谝徊綀?zhí)行完畢后會將剛剛創(chuàng)建的實例放入singletonFactories(第三級緩存)中,那么我們下面了解下到底什么是spring的三級緩存。處于最上層的緩存是singletonObjects,它其中存儲的對象是完成創(chuàng)建好,可以正常使用的bean,二級緩存叫做earlySingletonObjects,它其中存儲的bean是僅執(zhí)行了第一步通過構造方法實例化,并沒有填充屬性和初始化,第三級緩存singletonFactories是一個工場。

/** Cache of singleton objects: bean name to bean instance. */
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
 /** Cache of singleton factories: bean name to ObjectFactory. */
 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
 
 /** Cache of early singleton objects: bean name to bean instance. */
 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

其實在getSingleton方法中會首先從一級緩存中獲取bean,一級緩存中沒有再從二級緩存中獲取,二級也沒有就會從三級中獲取factory當factory不為null時,則會調用getObject方法獲取bean,并將bean放入二級緩存,之后再從三級緩存中刪除該key-value對,代碼如下:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  synchronized (this.singletonObjects) {
  singletonObject = this.earlySingletonObjects.get(beanName);
  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;
 }

那么到此處我們就可以看出為什么不能在構造方法中存在循環(huán)依賴了,假如現(xiàn)在有a、b兩個service,它們兩個互相在構造方法中循環(huán)依賴,當項目啟動,創(chuàng)建a的bean時執(zhí)行第一步通過構造方法創(chuàng)建實例,但是發(fā)現(xiàn)依賴b的bean,所以就從三級緩存中獲取,但是沒發(fā)現(xiàn),那么就先掛起a的創(chuàng)建過程,先去創(chuàng)建b,在b創(chuàng)建過程中,又依賴于a,但是三級緩存中也沒有a的bean,這樣就進入了一個循環(huán)創(chuàng)建的過程,自然是不可取的。

而內(nèi)部field scope為prototype為何也會報錯呢,當scope為prototype每次引用它時都會創(chuàng)建一個新的對象,所以也會存在循環(huán)創(chuàng)建的過程。

而默認情況下bean的scope為singleton,整個容器中僅有整個service的一個bean,還是假如a、b兩service存在field循環(huán)依賴,當創(chuàng)建a的bean時,執(zhí)行完構造方法后,a的實例已生成,將其factory對象存入第三級緩存singletonFactories中,在填充屬性時,發(fā)現(xiàn)依賴b的bean,但是在緩存中沒有b的bean;因此轉而去創(chuàng)建b,在此過程中執(zhí)行完b的構造方法后將其factory也放入三級緩存,此時執(zhí)行b的屬性填充,發(fā)現(xiàn)依賴a,從三級緩存中獲取a的對象,并將a放入二級緩存中,之后執(zhí)行intialize初始化,最后將b的bean轉入一級緩存;再繼續(xù)回來創(chuàng)建a,這個時候發(fā)現(xiàn)在一級緩存中已經(jīng)有了b,那么屬性填充成功,進行初始化,最后a也放入一級緩存,至此執(zhí)行完畢。

那么大家可能會感到疑惑,為什么要使用三級緩存呢,感覺沒有singletonFactories使用二級緩存也可以呀?

從前面的代碼里可以看到向第三級緩存中放置的是一個objectFactory的匿名實現(xiàn)類,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)),當我們從singletonFactories中獲取objectFctory然后調用getObject方法獲取bean的時候,實際就是通過getEarlyBeanReference獲取的object,那么進入這個方法看一下。

它在這里會獲取所有的beanPostProcessor實現(xiàn)類,然后從中找出實現(xiàn)了SmartInstantiationAwareBeanPostProcessor的beanPostProcessor,然后調用它的getEarlyBeanReference(obgect,beanName)方法,對bean進行處理,然后進行返回,這些實現(xiàn)類中就有aop的核心AbstractAutoProxyCreator,從這里我們就可以看出來,從第三級緩存objectFactory中獲取的obejct是經(jīng)過了處理的一個代理對象,個人理解三級緩存就是為了獲取在創(chuàng)建對象的過程中提前對其進行一些擴展操作。

三級緩存實現(xiàn)bean的擴展,將代理對象放入二級緩存中,供其他依賴該bean的對象的使用,如果沒有了三級緩存,將bean擴展放在二級緩存中實現(xiàn),那么如果有bean a被其他多個bean依賴,那么在其他bean填充屬性的過程中會多次獲取bean a,這個過程中就會多次執(zhí)行獲取bean a代理,就有些多余,而三級緩存結構就是在第三級緩存完成bean的擴展,生成代理對象,放入二級緩存之中,供其他bean獲取。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。

相關文章

  • mybatis中${}和#{}取值的區(qū)別分析

    mybatis中${}和#{}取值的區(qū)別分析

    mybatis中使用sqlMap進行sql查詢時,經(jīng)常需要動態(tài)傳遞參數(shù),在動態(tài)SQL解析階段, #{ } 和 ${ } 會有不同的表現(xiàn),這篇文章主要給大家介紹了關于mybatis中${}和#{}取值區(qū)別的相關資料,需要的朋友可以參考下
    2021-09-09
  • ActiveMQ消息簽收機制代碼實例詳解

    ActiveMQ消息簽收機制代碼實例詳解

    這篇文章主要介紹了ActiveMQ消息簽收機制代碼實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • MyBatis-Plus中公共字段的統(tǒng)一處理的實現(xiàn)

    MyBatis-Plus中公共字段的統(tǒng)一處理的實現(xiàn)

    在開發(fā)中經(jīng)常遇到多個實體類有共同的屬性字段,這些字段屬于公共字段,本文主要介紹了MyBatis-Plus中公共字段的統(tǒng)一處理的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-08-08
  • 分析JVM的執(zhí)行子系統(tǒng)

    分析JVM的執(zhí)行子系統(tǒng)

    本文主要介紹了JVM執(zhí)行子系統(tǒng)。了解虛擬機是如何執(zhí)行程序的, 虛擬機怎樣運行一個Class文件的概念模型, 可以更好的理解怎樣寫出優(yōu)秀的代碼
    2021-06-06
  • Java實現(xiàn)圖片驗證碼功能

    Java實現(xiàn)圖片驗證碼功能

    這篇文章主要為大家詳細介紹了Java實現(xiàn)圖片驗證碼功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • 舉例講解Java設計模式編程中Decorator裝飾者模式的運用

    舉例講解Java設計模式編程中Decorator裝飾者模式的運用

    這篇文章主要介紹了Java設計模式編程中Decorator裝飾者模式的運用,裝飾者模式就是給一個對象動態(tài)的添加新的功能,裝飾者和被裝飾者實現(xiàn)同一個接口,裝飾者持有被裝飾者的實例,需要的朋友可以參考下
    2016-05-05
  • 解讀JDK8踩坑JCE加密限制版本問題

    解讀JDK8踩坑JCE加密限制版本問題

    這篇文章主要介紹了JDK8踩坑JCE加密限制版本問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 深入解析System.load 與 System.loadLibrary

    深入解析System.load 與 System.loadLibrary

    以下是對System.load與System.loadLibrary進行了詳細的分析介紹。需要的朋友可以過來參考下
    2013-08-08
  • SpringBoot 定制化返回數(shù)據(jù)的實現(xiàn)示例

    SpringBoot 定制化返回數(shù)據(jù)的實現(xiàn)示例

    這篇文章主要介紹了SpringBoot 定制化返回數(shù)據(jù)的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • 5分鐘快速學會spring boot整合Mybatis的方法

    5分鐘快速學會spring boot整合Mybatis的方法

    這篇文章主要給大家介紹了如何通過5分鐘快速學會spring boot整合Mybatis的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用spring boot具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-12-12

最新評論