知識庫 / Spring RSS 訂閱

Spring容器中重新初始化單例Bean

Spring
HongKong
6
11:48 AM · Dec 06 ,2025

1. 概述

本教程將探討如何在運行時重新初始化單例 Spring Bean 的方法。

默認情況下,具有 singleton 作用域的 Spring Bean 在應用程序生命週期中不會被重新初始化。但是,在某些情況下,可能需要重新創建 Bean——例如,當屬性被更新時。我們將介紹幾種實現此目的的方法。

2. 代碼設置

為了理解這一點,我們將創建一個小型項目。我們將創建一個 Bean,該 Bean 從配置文件讀取配置屬性並將其保存在內存中以進行更快速的訪問。如果文件的屬性發生更改,可能需要重新加載配置。

2.1. 單例 Bean

讓我們首先創建一個 ConfigManager 類:

@Service("ConfigManager")
public class ConfigManager {

    private static final Log LOG = LogFactory.getLog(ConfigManager.class);

    private Map<String, Object> config;

    private final String filePath;

    public ConfigManager(@Value("${config.file.path}") String filePath) {
        this.filePath = filePath;
        initConfigs();
    }

    private void initConfigs() {
        Properties properties = new Properties();
        try {
            properties.load(Files.newInputStream(Paths.get(filePath)));
        } catch (IOException e) {
            LOG.error("Error loading configuration:", e);
        }
        config = new HashMap<>();
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            config.put(String.valueOf(entry.getKey()), entry.getValue());
        }
    }

    public Object getConfig(String key) {
        return config.get(key);
    }
}

請注意以下幾點關於此類的:

  • 從構造函數中調用方法 initConfigs() 會在 Bean 被構造時立即加載文件。
  • 方法 initConfigs() 將文件的內容轉換為一個名為 configMap
  • 方法 getConfig() 用於通過其鍵讀取屬性。

另一個需要注意的點是構造函數依賴注入。稍後我們會使用它來替換 Bean。

配置文件的路徑為 src/main/resources/config.properties,其中包含一個單一的屬性:

property1=value1

2.2. 控制器

為了測試ConfigManager,我們創建一個控制器:

@RestController
@RequestMapping("/config")
public class ConfigController {

    @Autowired
    private ConfigManager configManager;

    @GetMapping("/{key}")
    public Object get(@PathVariable String key) {
        return configManager.getConfig(key);
    }
}

我們可以運行應用程序並通過訪問 URL http://localhost:8080/config/property1 讀取配置。

接下來,我們希望更改文件的屬性值,並在再次通過相同的 URL 讀取配置時反映該更改。下面我們將探討幾種實現方法。

3. 使用公共方法重新加載屬性

如果我們希望重新加載屬性而不重新創建對象本身,只需創建一個公共方法,該方法重新初始化地圖即可。 在我們的 ConfigManager 中,讓我們添加一個調用 initConfigs() 方法的方法:

public void reinitializeConfig() {
    initConfigs();
}

我們可以隨後調用此方法來重新加載屬性。 讓我們在控制器類中暴露另一個方法,該方法調用 reinitializeConfig() 方法:

@GetMapping("/reinitializeConfig")
public void reinitializeConfig() {
    configManager.reinitializeConfig();
}

我們現在可以運行該應用程序並進行測試,只需遵循以下幾個簡單步驟:

4. 重新初始化單例 Bean

另一種重新初始化 Bean 的方法是在上下文中重新創建它。重新創建可以通過使用自定義代碼和調用構造函數,或者刪除 Bean 並讓上下文自動重新初始化它來實現。下面我們分別來看這兩種方法。

4.1. 從上下文中替換 Bean

我們可以從上下文中刪除 Bean 並用一個新的 ConfigManager 實例替換它。 讓我們定義另一個方法在我們的控制器中執行此操作:

@GetMapping("/reinitializeBean")
public void reinitializeBean() {
    DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) applicationContext.getAutowireCapableBeanFactory();
    registry.destroySingleton("ConfigManager");
    registry.registerSingleton("ConfigManager", new ConfigManager(filePath)); 
}

首先,我們從應用程序上下文中獲取 DefaultSingletonBeanRegistry 實例。接下來,我們調用 destroySingleton() 方法來銷燬名為 ConfigManager 的 Bean 實例。最後,我們創建 ConfigManager 的新實例並將其註冊到工廠中,通過調用 registerSingleton() 方法實現。

為了創建新實例,我們使用了 ConfigManager 中定義的構造函數。 Bean 依賴於的其他任何依賴項必須通過構造函數傳遞。

registerSingleton() 方法不僅在上下文中創建 Bean,還將其自動注入到依賴對象中。

調用 /reinitializeBean 端點會更新控制器中的 ConfigManager Bean。我們可以使用與先前方法中相同的步驟來測試重新初始化行為。

4.2. 在上下文中銷燬 Bean

在之前的示例中,我們需要通過構造函數傳遞依賴項。有時,我們可能不需要創建 Bean 的新實例,或者可能無法訪問所需的依賴項。在這種情況下,僅僅銷燬 Bean 在上下文中也是一種可能性

上下文會在 Bean 再次請求時重新創建 Bean。在這種情況下,它將使用與初始 Bean 創建相同的步驟

為了演示這一點,讓我們創建一個銷燬 Bean 但不會重新創建它的新控制器方法:

@GetMapping("/destroyBean")
public void destroyBean() {
    DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) applicationContext.getAutowireCapableBeanFactory();
    registry.destroySingleton("ConfigManager");
}

這不會改變控制器已持有的 Bean 的引用。我們需要直接從上下文中讀取它。

讓我們創建一個新的控制器來讀取配置。這個控制器將依賴於上下文中最新的 Bean:

@GetMapping("/context/{key}")
public Object getFromContext(@PathVariable String key) {
    ConfigManager dynamicConfigManager = applicationContext.getBean(ConfigManager.class);
    return dynamicConfigManager.getConfig(key);
}

我們可以使用以下幾個簡單步驟測試上述方法:

5. 結論

在本文中,我們探討了如何重新初始化單例 Bean 的方法。我們研究了在不重新創建 Bean 的情況下修改 Bean 屬性的一種方式。我們還研究了在上下文中強制重新創建 Bean 的方法。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.