最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring容器三級緩存的使用及說明

 更新時間:2025年06月29日 14:00:55   作者:找不到、了  
這篇文章主要介紹了Spring容器三級緩存的使用及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

Spring容器為了解決循環(huán)依賴問題,引入了三級緩存系統(tǒng)。這與Hibernate/MyBatis中的緩存概念不同,是Spring特有的設(shè)計。

1、緩存介紹

1.1、緩存分類

1.一級緩存(singletonObjects)

用途

存放完全初始化好的單例 Bean,這些 Bean 已經(jīng)完成了所有的屬性注入和初始化操作,可以直接使用。

數(shù)據(jù)結(jié)構(gòu)

Map<String,Object>,鍵為 Bean 的名稱,值為對應(yīng)的 Bean 實例。

2.二級緩存(earlySingletonObjects)

用途

存放早期曝光的 Bean 實例,這些 Bean 已經(jīng)被創(chuàng)建,但還沒有完成屬性注入和初始化操作。當(dāng)需要解決循環(huán)依賴時,可以從這個緩存中獲取 Bean 的早期引用。

數(shù)據(jù)結(jié)構(gòu)

Map<String,Object>,鍵為 Bean 的名稱,值為對應(yīng)的 Bean 實例。

3.三級緩存(singletonFactories)

用途

存放ObjectFactory對象,這些對象可以用來創(chuàng)建 Bean 的早期引用。也可以處理AOP代理等特殊情況

數(shù)據(jù)結(jié)構(gòu)

Map<String,ObjectFactory<?>>,鍵為 Bean 的名稱,值為對應(yīng)的ObjectFactory對象。

如下圖所示:

1.2、聯(lián)系

三級緩存是為了解決循環(huán)依賴問題而引入的,當(dāng)出現(xiàn)循環(huán)依賴時,首先會從一級緩存中查找 Bean,如果找不到,會嘗試從二級緩存中查找,如果還是找不到,會從三級緩存中獲取ObjectFactory并創(chuàng)建 Bean 的早期引用,放入二級緩存中。

二級緩存中的 Bean 是從三級緩存中創(chuàng)建出來的早期引用,這些 Bean 還沒有完成屬性注入和初始化操作。

一級緩存中的 Bean 是最終可用的 Bean,這些 Bean 已經(jīng)完成了所有的屬性注入和初始化操作。

2、循環(huán)依賴

關(guān)于以下文章介紹的是Spring單例bean的循環(huán)依賴解決方案。

2.1、循環(huán)依賴場景

@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

2.2、解決流程圖示

有對象A和對象B,分別相互依賴。

如下圖所示:

1、A對象緩存查詢

依次查詢一級、二級、三級查詢,由于開始,三種緩存里面分別沒有A對象的緩存。

2、A對象創(chuàng)建對象

通過反射,將a對象放到三級緩存里面。

3、A對象屬性填充

在填充屬性的時候,會發(fā)現(xiàn)A對象需要依賴B對象,因此重復(fù)剛才A對象的操作步驟。

如下圖所示:

1、B對象緩存查詢

先從緩存查詢B對象的三級緩存,由于首次查詢,b對象的一級、二級、三級緩存均為空。

2、B對象創(chuàng)建對象

然后,創(chuàng)建B對象,將b對象的引用放到三級緩存里,此時三級緩存里面同時存放了A、B對象的引用。

3、B對象屬性填充

在進行B對象填充屬性的時候,發(fā)現(xiàn)依賴于A。

B依賴A的緩存查詢

然后重復(fù)執(zhí)行緩存查詢的操作,此時由于前面A、B三級緩存分別在創(chuàng)建對象的時候,都放在了三級里面。

B依賴A的二級緩存

因此通過將三級緩存,放入二級緩存里,同時刪除三級的a對象。

4、B對象初始化

然后在進行b對象的初始化,此時@postConstruct就在這里執(zhí)行,完成B對象的初始化。

5、B對象緩存轉(zhuǎn)移

如下圖所示:

將b對象的三級緩存、二級緩存移除掉,同時寫入一級緩存里面。

4、A對象初始化

5、A對象緩存轉(zhuǎn)移

刪除A對象的三級緩存、二級緩存、同時寫入到1級緩存。

總結(jié):A、B循環(huán)依賴的流程圖如下所示:

2.3、代碼示例

下面是一個簡單的 Java 代碼示例,模擬 Spring 容器的三級緩存機制來解決循環(huán)依賴問題:

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

// 模擬 Bean 定義
class BeanDefinition {
    private String beanName;
    private Class<?> beanClass;

    public BeanDefinition(String beanName, Class<?> beanClass) {
        this.beanName = beanName;
        this.beanClass = beanClass;
    }

    public String getBeanName() {
        return beanName;
    }

    public Class<?> getBeanClass() {
        return beanClass;
    }
}

模擬Spring容器

// 模擬 Spring 容器
class BeanFactory {
    // 一級緩存
    private final Map<String, Object> singletonObjects = new HashMap<>();
    // 二級緩存
    private final Map<String, Object> earlySingletonObjects = new HashMap<>();
    // 三級緩存
    private final Map<String, Supplier<Object>> singletonFactories = new HashMap<>();
    // Bean 定義集合
    private final Map<String, BeanDefinition> beanDefinitions = new HashMap<>();

    // 注冊 Bean 定義
    public void registerBeanDefinition(BeanDefinition beanDefinition) {
        beanDefinitions.put(beanDefinition.getBeanName(), beanDefinition);
    }

    // 獲取 Bean
    public Object getBean(String beanName) {
        // 先從一級緩存中查找
        Object singletonObject = singletonObjects.get(beanName);
        if (singletonObject != null) {
            return singletonObject;
        }
        // 再從二級緩存中查找
        singletonObject = earlySingletonObjects.get(beanName);
        if (singletonObject != null) {
            return singletonObject;
        }
        // 從三級緩存中查找
        Supplier<Object> singletonFactory = singletonFactories.get(beanName);
        if (singletonFactory != null) {
            // 創(chuàng)建早期引用
            singletonObject = singletonFactory.get();
            // 將早期引用放入二級緩存
            earlySingletonObjects.put(beanName, singletonObject);
            // 從三級緩存中移除
            singletonFactories.remove(beanName);
            return singletonObject;
        }
        // 創(chuàng)建 Bean
        BeanDefinition beanDefinition = beanDefinitions.get(beanName);
        if (beanDefinition != null) {
            try {
                // 創(chuàng)建 Bean 實例
                Object bean = beanDefinition.getBeanClass().newInstance();
                // 將 Bean 工廠放入三級緩存
                singletonFactories.put(beanName, () -> bean);
                // 模擬屬性注入,可能會出現(xiàn)循環(huán)依賴
                // 這里簡單處理,不進行實際的屬性注入
                // ...
                // 屬性注入完成后,將 Bean 放入一級緩存
                singletonObjects.put(beanName, bean);
                // 從二級緩存中移除
                earlySingletonObjects.remove(beanName);
                return bean;
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

測試類:

// 測試類
public class Main {
    public static void main(String[] args) {
        BeanFactory beanFactory = new BeanFactory();
        // 注冊 Bean 定義
        beanFactory.registerBeanDefinition(new BeanDefinition("beanA", BeanA.class));
        beanFactory.registerBeanDefinition(new BeanDefinition("beanB", BeanB.class));
        // 獲取 Bean
        Object beanA = beanFactory.getBean("beanA");
        Object beanB = beanFactory.getBean("beanB");
        System.out.println("BeanA: " + beanA);
        System.out.println("BeanB: " + beanB);
    }
}

BeanA類:

// 示例 Bean A
class BeanA {
    private BeanB beanB;

    public BeanA() {
    }

    public BeanB getBeanB() {
        return beanB;
    }

    public void setBeanB(BeanB beanB) {
        this.beanB = beanB;
    }
}

BeanB類:

// 示例 Bean B
class BeanB {
    private BeanA beanA;

    public BeanB() {
    }

    public BeanA getBeanA() {
        return beanA;
    }

    public void setBeanA(BeanA beanA) {
        this.beanA = beanA;
    }
}

2.4、代碼解釋

  • BeanDefinition類用于存儲 Bean 的定義信息,包括 Bean 的名稱和類。
  • BeanFactory類模擬了 Spring 容器的功能,包含了三級緩存和 Bean 定義集合。
  • getBean方法用于獲取 Bean 實例,首先從一級緩存中查找,如果找不到,再從二級緩存中查找,如果還是找不到,從三級緩存中獲取ObjectFactory并創(chuàng)建 Bean 的早期引用,放入二級緩存中。
  • Main類用于測試BeanFactory的功能,注冊 Bean 定義并獲取 Bean 實例。

流程:

+---------------------+
| 一級緩存 (singletonObjects) |
| 存放完全初始化的 Bean  |
+---------------------+
           ^
           |
           |
+---------------------+
| 二級緩存 (earlySingletonObjects) |
| 存放早期曝光的 Bean  |
+---------------------+
           ^
           |
           |
+---------------------+
| 三級緩存 (singletonFactories) |
| 存放 ObjectFactory 對象 |
+---------------------+

這個圖展示了 Spring 容器的三級緩存結(jié)構(gòu),

1. 一級緩存位于最上層,存放完全初始化的 Bean;

2.二級緩存位于中間,存放早期曝光的 Bean;

3.三級緩存位于最下層,存放ObjectFactory對象。

4.當(dāng)需要解決循環(huán)依賴時,會從三級緩存中獲取ObjectFactory并創(chuàng)建 Bean 的早期引用,放入二級緩存中,最終將完全初始化的 Bean 放入一級緩存中。

3、三級緩存

1、原因

為什么要使用三級緩存,二級不可以嗎?

盡管二級緩存能解決部分循環(huán)依賴問題,但 Spring 引入三級緩存主要是為了支持 AOP(面向切面編程)。

若 Bean 需要 AOP 代理(如事務(wù)管理),代理對象需要在依賴注入時動態(tài)生成。三級緩存中的ObjectFactory可以延遲生成代理對象,確保依賴注入時使用代理后的實例。

具體原因如下:

1、支持 AOP 代理

在 Spring 中,當(dāng)一個 Bean 需要進行 AOP 代理時,代理對象和原始 Bean 對象可能是不同的。

如果只使用二級緩存,在早期曝光時放入的是原始 Bean 實例,那么在后續(xù)的屬性注入過程中,其他 Bean 引用的就是原始 Bean 而非代理對象,這會導(dǎo)致 AOP 失效。

而三級緩存(singletonFactories)存放的是ObjectFactory,可以在需要時通過ObjectFactory的getObject方法來創(chuàng)建代理對象,保證在出現(xiàn)循環(huán)依賴時,其他 Bean 引用的是正確的代理對象。

2、延遲創(chuàng)建代理對象

使用三級緩存可以實現(xiàn)延遲創(chuàng)建代理對象。只有在真正出現(xiàn)循環(huán)依賴且需要獲取早期引用時,才會調(diào)用ObjectFactory的getObject方法來創(chuàng)建代理對象,避免了不必要的代理對象創(chuàng)建,提高了性能。

綜上所述,雖然二級緩存能解決部分循環(huán)依賴問題,但為了支持 AOP 代理和延遲創(chuàng)建代理對象,Spring 引入了三級緩存機制。

4、使用范圍

Spring只能解決單例Bean通過Setter/字段注入的循環(huán)依賴。

1.構(gòu)造器注入的循環(huán)依賴

@Component
public class A {
    private B b;
    public A(B b) { this.b = b; }  // 構(gòu)造器注入
}
 
@Component
public class B {
    private A a;
    public B(A a) { this.a = a; }  // 構(gòu)造器注入
}

原因:構(gòu)造器注入需要先完成Bean的實例化,無法提前暴露半成品。

2.多例Bean(@Scope("prototype"))

Spring不緩存多例Bean,因此無法解決循環(huán)依賴。

5、建議

  • 盡量避免循環(huán)依賴:代碼結(jié)構(gòu)不合理時容易引發(fā)循環(huán)依賴,建議通過重構(gòu)解決。
  • 優(yōu)先使用Setter/字段注入:構(gòu)造器注入雖然安全,但無法解決循環(huán)依賴。
  • 利用@Lazy延遲加載:對某個Bean添加@Lazy,讓Spring延遲注入,打破循環(huán)。
@Component
public class A {
    @Lazy  // 延遲注入B
    @Autowired
    private B b;
}

6、擴展

1、多個AOP的順序怎么定

通過**@Order注解來設(shè)置增強類優(yōu)先級:這個值越小優(yōu)先級越高**!

@Order(3)
public class UserProxy {}

@Order(1)
public class PersonProxy {}

2、如何讓兩個Bean按順序加載

  • 1、使用 @DependsOn
@Component
@DependsOn({"beanB", "beanC"})  // 確保beanB和beanC先加載
public class BeanA {
    // ...
}

@Component
public class BeanB {
    // ...
}

@Component
public class BeanC {
    // ...
}
  • 2、實現(xiàn)PriorityOrdered或Ordered接口

對于實現(xiàn)了特定接口的Bean,可以控制它們的初始化順序:

@Component
public class BeanOne implements PriorityOrdered {
    @Override
    public int getOrder() {
        return 1;  // 數(shù)字越小優(yōu)先級越高
    }
}

@Component
public class BeanTwo implements Ordered {
    @Override
    public int getOrder() {
        return 2;
    }
}
  • 3、使用@Order注解

適用于某些特定場景,如攔截器、AOP切面等的順序控制

@Component
@Order(1)
public class FirstBean {
    // ...
}

@Component
@Order(2)
public class SecondBean {
    // ...
}
  • 4、讓后加載的類依賴先加載的類
@Component
public class A {
    @Autowire
    private B b;
}

總結(jié)

Spring通過三級緩存+提前暴露半成品對象解決循環(huán)依賴問題,核心目的是處理AOP代理對象的唯一性。雖然理論上兩級緩存可以解決部分場景,但三級緩存是Spring設(shè)計上的必要選擇。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解Spring boot操作文件的多種方式

    詳解Spring boot操作文件的多種方式

    這篇文章主要介紹了Spring boot操作文件的幾種方式,主要給大家介紹操作文件的三種方式,每種方式通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-11-11
  • Zuul 實現(xiàn)網(wǎng)關(guān)轉(zhuǎn)發(fā)的五種方式小結(jié)

    Zuul 實現(xiàn)網(wǎng)關(guān)轉(zhuǎn)發(fā)的五種方式小結(jié)

    這篇文章主要介紹了Zuul 實現(xiàn)網(wǎng)關(guān)轉(zhuǎn)發(fā)的五種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring Security 框架最佳實踐

    Spring Security 框架最佳實踐

    Spring Security 是一個功能強大、高度可定制的身份驗證(Authentication)和訪問控制(Authorization)框架,是保護基于 Spring 的應(yīng)用的事實標準,本文給大家介紹Spring Security框架最佳實踐,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • Java SimpleDateFormat中英文時間格式化轉(zhuǎn)換詳解

    Java SimpleDateFormat中英文時間格式化轉(zhuǎn)換詳解

    這篇文章主要為大家詳細介紹了Java SimpleDateFormat中英文時間格式化轉(zhuǎn)換,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • springboot默認日志框架選擇源碼解析(推薦)

    springboot默認日志框架選擇源碼解析(推薦)

    這篇文章主要介紹了springboot默認日志框架選擇源碼解析(推薦),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • java異常:異常處理--try-catch結(jié)構(gòu)詳解

    java異常:異常處理--try-catch結(jié)構(gòu)詳解

    今天小編就為大家分享一篇關(guān)于Java異常處理之try...catch...finally詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2021-09-09
  • idea新建springboot項目pom文件報錯問題及解決

    idea新建springboot項目pom文件報錯問題及解決

    這篇文章主要介紹了idea新建springboot項目pom文件報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • java驗證碼生成具體代碼

    java驗證碼生成具體代碼

    這篇文章主要為大家分享了java驗證碼生成具體代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • 關(guān)于SpringBoot的異常回滾和事務(wù)的使用詳解

    關(guān)于SpringBoot的異常回滾和事務(wù)的使用詳解

    這篇文章主要介紹了關(guān)于SpringBoot的異?;貪L和事務(wù)的使用詳解,Spring中 @Transactional 注解,默認情況下,只對拋出的RuntimeException 異常,才會事務(wù)回滾,需要的朋友可以參考下
    2023-05-05
  • java數(shù)學(xué)歸納法非遞歸求斐波那契數(shù)列的方法

    java數(shù)學(xué)歸納法非遞歸求斐波那契數(shù)列的方法

    這篇文章主要介紹了java數(shù)學(xué)歸納法非遞歸求斐波那契數(shù)列的方法,涉及java非遞歸算法的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07

最新評論

莎车县| 龙门县| 社会| 盐山县| 游戏| 偏关县| 文昌市| 阿鲁科尔沁旗| 舟山市| 定州市| 西平县| 正宁县| 黔西县| 南投县| 湘潭县| 石门县| 正蓝旗| 荔浦县| 大悟县| 天水市| 铜陵市| 如皋市| 额敏县| 岳池县| 锡林浩特市| 石渠县| 奉新县| 扎囊县| 舞钢市| 宁都县| 旬阳县| 隆化县| 伊金霍洛旗| 屯门区| 科技| 陇南市| 德昌县| 株洲市| 平塘县| 深圳市| 潢川县|