亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

1、緩存介紹

1.1、緩存分類

1.一級緩存(singletonObjects)

用途

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

數(shù)據(jù)結構

Map<String,Object>,鍵為 Bean 的名稱,值為對應的 Bean 實例。

2.二級緩存(earlySingletonObjects)

用途

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

數(shù)據(jù)結構

Map<String,Object>,鍵為 Bean 的名稱,值為對應的 Bean 實例。

3.三級緩存(singletonFactories)

用途

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

數(shù)據(jù)結構

Map<String,ObjectFactory<?>>,鍵為 Bean 的名稱,值為對應的ObjectFactory對象。

如下圖所示:

1.2、聯(lián)系

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

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

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

2、循環(huá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對象,因此重復剛才A對象的操作步驟。

如下圖所示:

1、B對象緩存查詢

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

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

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

3、B對象屬性填充

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

B依賴A的緩存查詢

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

B依賴A的二級緩存

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

4、B對象初始化

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

5、B對象緩存轉移

如下圖所示:

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

4、A對象初始化

5、A對象緩存轉移

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

總結: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 容器的三級緩存結構,

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

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

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

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

3、三級緩存

1、原因

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

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

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

具體原因如下:

1、支持 AOP 代理

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

如果只使用二級緩存,在早期曝光時放入的是原始 Bean 實例,那么在后續(xù)的屬性注入過程中,其他 Bean 引用的就是原始 Bean 而非代理對象,這會導致 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.構造器注入的循環(huán)依賴

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

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

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

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

5、建議

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

6、擴展

1、多個AOP的順序怎么定

通過**@Order注解來設置增強類優(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;
}

總結

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

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

相關文章

  • 老生常談java中的數(shù)組初始化

    老生常談java中的數(shù)組初始化

    下面小編就為大家?guī)硪黄仙U刯ava中的數(shù)組初始化。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Spring的連接數(shù)據(jù)庫以及JDBC模板(實例講解)

    Spring的連接數(shù)據(jù)庫以及JDBC模板(實例講解)

    下面小編就為大家?guī)硪黄猄pring的連接數(shù)據(jù)庫以及JDBC模板(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Spring?Boot?Admin?監(jiān)控指標接入Grafana可視化的實例詳解

    Spring?Boot?Admin?監(jiān)控指標接入Grafana可視化的實例詳解

    Spring Boot Admin2 自帶有部分監(jiān)控圖表,如圖,有線程、內(nèi)存Heap和內(nèi)存Non Heap,這篇文章主要介紹了Spring?Boot?Admin?監(jiān)控指標接入Grafana可視化,需要的朋友可以參考下
    2022-11-11
  • sqlserver和java將resultSet中的記錄轉換為學生對象

    sqlserver和java將resultSet中的記錄轉換為學生對象

    這篇文章主要介紹了如何利用sqlserver和java將resultSet中的記錄轉換為學生對象,附有超詳細的代碼,需要的朋友可以參考一下,希望對你有所幫助
    2021-12-12
  • Spring?Boot配置內(nèi)容加密實現(xiàn)敏感信息保護

    Spring?Boot配置內(nèi)容加密實現(xiàn)敏感信息保護

    之前我們講過的配置相關知識都是Spring?Boot原生就提供的,而今天我們將介紹的功能并非Spring?Boot原生就支持,但卻非常有用:配置內(nèi)容的加密
    2021-11-11
  • IntelliJ IDEA同步代碼時版本沖突而產(chǎn)生出的incoming partial文件問題的解決辦法

    IntelliJ IDEA同步代碼時版本沖突而產(chǎn)生出的incoming partial文件問題的解決辦法

    今天小編就為大家分享一篇關于IntelliJ IDEA同步代碼時版本沖突而產(chǎn)生出的incoming partial文件問題的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • Java Hibernate中的多種查詢方式及示例

    Java Hibernate中的多種查詢方式及示例

    Hibernate提供了多種查詢方式,包括HQL查詢、SQL查詢、QBC查詢和Criteria查詢等。HQL查詢是基于面向?qū)ο蟮牟樵冋Z言,可以方便地進行對象導航和多表查詢;SQL查詢允許用戶直接使用SQL語句進行查詢;QBC查詢是基于Criteria的查詢,具有類型安全和可重用性的優(yōu)勢
    2023-04-04
  • 詳解spring-boot集成elasticsearch及其簡單應用

    詳解spring-boot集成elasticsearch及其簡單應用

    本篇文章主要介紹了詳解spring-boot集成elasticsearch及其簡單應用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 使用IDEA向Gitee提交SpringBoot項目進行遠程管理

    使用IDEA向Gitee提交SpringBoot項目進行遠程管理

    本文主要介紹了使用IDEA向Gitee提交SpringBoot項目進行遠程管理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • Python爬蟲 12306搶票開源代碼過程詳解

    Python爬蟲 12306搶票開源代碼過程詳解

    這篇文章主要介紹了Python爬蟲 12306搶票開源代碼過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09

最新評論