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

Spring容器三級(jí)緩存的使用及說明

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

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

1、緩存介紹

1.1、緩存分類

1.一級(jí)緩存(singletonObjects)

用途

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

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

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

2.二級(jí)緩存(earlySingletonObjects)

用途

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

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

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

3.三級(jí)緩存(singletonFactories)

用途

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

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

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

如下圖所示:

1.2、聯(lián)系

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

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

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

2、循環(huán)依賴

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

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

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

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

2.2、解決流程圖示

有對(duì)象A和對(duì)象B,分別相互依賴。

如下圖所示:

1、A對(duì)象緩存查詢

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

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

通過反射,將a對(duì)象放到三級(jí)緩存里面。

3、A對(duì)象屬性填充

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

如下圖所示:

1、B對(duì)象緩存查詢

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

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

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

3、B對(duì)象屬性填充

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

B依賴A的緩存查詢

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

B依賴A的二級(jí)緩存

因此通過將三級(jí)緩存,放入二級(jí)緩存里,同時(shí)刪除三級(jí)的a對(duì)象。

4、B對(duì)象初始化

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

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

如下圖所示:

將b對(duì)象的三級(jí)緩存、二級(jí)緩存移除掉,同時(shí)寫入一級(jí)緩存里面。

4、A對(duì)象初始化

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

刪除A對(duì)象的三級(jí)緩存、二級(jí)緩存、同時(shí)寫入到1級(jí)緩存。

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

2.3、代碼示例

下面是一個(gè)簡單的 Java 代碼示例,模擬 Spring 容器的三級(jí)緩存機(jī)制來解決循環(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 {
    // 一級(jí)緩存
    private final Map<String, Object> singletonObjects = new HashMap<>();
    // 二級(jí)緩存
    private final Map<String, Object> earlySingletonObjects = new HashMap<>();
    // 三級(jí)緩存
    private final Map<String, Supplier<Object>> singletonFactories = new HashMap<>();
    // Bean 定義集合
    private final Map<String, BeanDefinition> beanDefinitions = new HashMap<>();

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

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

測(cè)試類:

// 測(cè)試類
public class Main {
    public static void main(String[] args) {
        BeanFactory beanFactory = new BeanFactory();
        // 注冊(cè) 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類用于存儲(chǔ) Bean 的定義信息,包括 Bean 的名稱和類。
  • BeanFactory類模擬了 Spring 容器的功能,包含了三級(jí)緩存和 Bean 定義集合。
  • getBean方法用于獲取 Bean 實(shí)例,首先從一級(jí)緩存中查找,如果找不到,再從二級(jí)緩存中查找,如果還是找不到,從三級(jí)緩存中獲取ObjectFactory并創(chuàng)建 Bean 的早期引用,放入二級(jí)緩存中。
  • Main類用于測(cè)試BeanFactory的功能,注冊(cè) Bean 定義并獲取 Bean 實(shí)例。

流程:

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

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

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

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

3.三級(jí)緩存位于最下層,存放ObjectFactory對(duì)象。

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

3、三級(jí)緩存

1、原因

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

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

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

具體原因如下:

1、支持 AOP 代理

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

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

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

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

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

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

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的實(shí)例化,無法提前暴露半成品。

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

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

5、建議

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

6、擴(kuò)展

1、多個(gè)AOP的順序怎么定

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

@Order(3)
public class UserProxy {}

@Order(1)
public class PersonProxy {}

2、如何讓兩個(gè)Bean按順序加載

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

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

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

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

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

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

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

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

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

總結(jié)

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

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

相關(guān)文章

最新評(píng)論