自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring 三級緩存機(jī)制深度解析

開發(fā)
本文基于一段代碼示例講解了三級緩存的Spring框架中的使用,通過對于AOP的增強(qiáng)時期了解到三級緩存是保證單例bean的關(guān)鍵,希望對你有幫助。?

筆者在很早整理過一篇關(guān)于AOP的源碼的文章,閱讀起來晦澀難懂,在復(fù)盤時就有了想重構(gòu)的想法,所以就借著這一話題重新聊一聊Spring中的三級緩存。

一、給出本文的代碼示例

因?yàn)槿壘彺娴脑O(shè)計是為了解決代理對象多例創(chuàng)建問題,所以在講解三級緩存之前,我們需要給出一段關(guān)于AOP的示例代碼以便有著一個直觀的切入點(diǎn)來理解這個問題,我們首先給出代理類AService  :

@Service("aService")
@Slf4j
public class AService  {

    @Autowired
    private BService bService;

    public void hello(){
        log.info("hello world");
    }

}

然后再給出代理對象的代碼示例:

@Aspect
@Slf4j
@Component
public class AopProxy {

    @Before("execution(* com.sharkChili.service.AService.hello())")
    public void proxyFunction() {
        log.info("aService被代理了");
    }

如此一來,我們的服務(wù)被調(diào)用后,代理對象就會攔截hello方法,在其執(zhí)行前打印aService被代理了:

2024-05-14 23:35:12.456  INFO 6652 --- [           main] com.sharkChili.aspect.AopProxy           : aService被代理了
2024-05-14 23:35:12.462  INFO 6652 --- [           main] com.sharkChili.service.AService          : hello world

二、基于源碼詳解三級緩存設(shè)計

1.三級緩存的定義

我們可以在DefaultSingletonBeanRegistry這個類的定義中看到三級緩存對象,它們分別是:

  • singletonObjects :即單例緩存Map,其內(nèi)部存儲的都是以bean的名稱為key,已經(jīng)完全創(chuàng)建的bean作為value的Map。
  • singletonFactories :該緩存都bean的工廠方法,可能有些讀者不太理解,我們就以上文的AService為例,在Spring進(jìn)行每一個bean初始化時,都會為這個bean封裝一個工廠方法用于bean的創(chuàng)建,這一點(diǎn)筆者也會在后續(xù)的代碼中提及,這里我們只需要知道這個緩存是以bean的名稱作為key,創(chuàng)建該bean的工廠方法為value的Map即可。
  • earlySingletonObjects :其內(nèi)部緩存的都是未完全完成創(chuàng)建的bean,就比如某個Service中的Mapper還未被注入,那么這個Service就會被緩存到這個Map中。

對應(yīng)的我們給出DefaultSingletonBeanRegistry中對于這三級緩存定義:

 /** 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 ConcurrentHashMap<>(16);

2.Spring 如何解決循環(huán)依賴問題

我們以兩個互相依賴的bean來探討Spring在進(jìn)行加載時如何完成循環(huán)依賴的bean的初始化:

@Service("aService")
@Slf4j
public class AService  {

    @Autowired
    private BService bService;  

}


@Service("bService")
public class BService {
    @Autowired
    private AService aService;

}

假設(shè)我們現(xiàn)在先創(chuàng)造aService  進(jìn)行各項(xiàng)初始化之前,容器會先將其加載至三級緩存也就是工廠緩存中:

隨后在屬性填充階段發(fā)現(xiàn)要填充bService,發(fā)現(xiàn)容器中各級緩存都沒有bService,于是到容器中嘗試加載bService,bService初始化過程與aService同理先加載到3級緩存中:

與此同時bService發(fā)現(xiàn)需要加載aService,此時在三級緩存中看到aService將其生成半成品bean放至二級緩存中并注入到bService中,此時bService完成加載稱為一個完成的對象,并直接存放至一級緩存中:

最后回到aService完成bService的注入,加載到一級緩存中,由此解決循環(huán)依賴的加載問題:

3.基于源碼詳解三級緩存的作用

我們還是以創(chuàng)建Aservice為例注入bService同時進(jìn)行增強(qiáng)為例講解一下這個過程,當(dāng)Spring容器進(jìn)行Aservice創(chuàng)建時代碼就走到AbstractAutowireCapableBeanFactory的doGetBean方法,對應(yīng)步驟為:

(1) 因?yàn)槭浅醮蝿?chuàng)建,所以代碼調(diào)用addSingletonFactory方法為Aservice創(chuàng)建工廠方法并將其存入singletonFactories這個三級工廠緩存中。

(2) 就會調(diào)用populateBean進(jìn)行屬性填充完成相關(guān)依賴注入,在此期間bService會將工廠緩存中的aService基于各種SmartInstantiationAwareBeanPostProcessor 生成增強(qiáng)類完成注入:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
  //拿到aService的一級緩存
  Object exposedObject = bean;
  //基于各種SmartInstantiationAwareBeanPostProcessor 生成增強(qiáng)類
  if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
   for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
    exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
   }
  }
  return exposedObject;
 }

(3) 在者循環(huán)期間就會找到一個AnnotationAwareAspectJAutoProxyCreator的動態(tài)代理bean,其內(nèi)部會通過策略模式找到AnnotationAwareAspectJAutoProxyCreator擴(kuò)展點(diǎn)內(nèi)部會判斷是否需要代理,然后通過策略模式走到CGLIB進(jìn)行增強(qiáng),自此aservice就被代理了。

@Override
 public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
  if (bean != null) {
   Object cacheKey = getCacheKey(bean.getClass(), beanName);
   //查看是否因?yàn)樽⑷氲仍騽?chuàng)建了代理,如果創(chuàng)建了則直接返回當(dāng)前bean,后續(xù)邏輯從容器中獲取代理對象
   if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    //判斷是否需要代理,如果需要則通過策略定位到加強(qiáng)類完成加強(qiáng)
    return wrapIfNecessary(bean, beanName, cacheKey);
   }
  }
  return bean;
 }

(4) 完成這步驟后bService就完整了,然后回到aService發(fā)現(xiàn)二級緩存已經(jīng)有了代理類,直接拿著這個代理返回存入一級緩存中:

if (earlySingletonExposure) {
  //發(fā)現(xiàn)二級緩存中有代理類,直接拿著這個代理類存入一級緩存中   
   Object earlySingletonReference = getSingleton(beanName, false);
   if (earlySingletonReference != null) {
    if (exposedObject == bean) {
     exposedObject = earlySingletonReference;
    }

}

4.為什么需要三級緩存

看過很多網(wǎng)上的解答,都是認(rèn)為通過三級緩存解決循環(huán)依賴中出現(xiàn)的各種錯誤,筆者認(rèn)為針對循環(huán)依賴問題,通過二級乃至一級緩存都可以解決問題。這里筆者假設(shè)是二級,aService也可以按照如下順序完成:

  • 將自己放入一級緩存。
  • 查找依賴的bService,創(chuàng)建其一級緩存,然后創(chuàng)建成代理對象存入一級。
  • 注入bService。

實(shí)際上Spring引入三級緩存的原因并不是解決這些所謂的"問題",而是在設(shè)計之初引入AOP時為保證所有的代理生成工作應(yīng)該盡量在bean初始化階段的后置初始化postProcessAfterInitialization等擴(kuò)展點(diǎn)完成,由此引入三級緩存,通過稍稍打破原則即通過三層緩存提前為需要代理對象的實(shí)例的bean生成代理對象完成依賴注入。

小結(jié)

本文基于一段代碼示例講解了三級緩存的Spring框架中的使用,通過對于AOP的增強(qiáng)時期了解到三級緩存是保證單例bean的關(guān)鍵,希望對你有幫助。

責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2024-03-04 08:47:17

Spring框架AOP

2023-12-12 17:44:13

三級緩存Bean

2010-11-25 09:37:14

MySQL查詢緩存機(jī)制

2022-05-08 19:23:28

Spring循環(huán)依賴

2022-12-02 12:01:30

Spring緩存生命周期

2022-03-01 18:03:06

Spring緩存循環(huán)依賴

2023-02-26 11:15:42

緩存循環(huán)依賴

2020-02-06 13:40:35

編程緩存優(yōu)化

2024-03-18 00:00:00

SpringBean設(shè)計

2024-04-12 07:51:05

SpringBean初始化

2021-01-29 14:14:47

動態(tài)代理緩存

2021-09-04 07:29:57

Android

2011-08-02 18:07:03

iPhone 內(nèi)省 Cocoa

2024-10-12 12:55:26

2025-02-25 10:21:15

2019-08-08 15:47:03

HTTP緩存CDN

2024-04-15 08:17:21

Spring依賴注入循環(huán)依賴

2020-02-23 15:45:02

大數(shù)據(jù)疫情運(yùn)營

2009-09-24 11:16:22

CCNA和CCNP

2010-11-10 09:13:37

綜合布線綜合布線改造
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號