知識庫 / Spring RSS 訂閱

基於屬性動態註冊 Spring Bean

Spring
HongKong
9
11:17 AM · Dec 06 ,2025

1. 概述

本教程將探討如何根據自定義屬性動態註冊 Bean。我們將深入研究 <em >BeanDefinitionRegistryPostProcessor</em> 接口,並瞭解如何使用它將 Bean 添加到應用程序上下文。

2. 代碼設置

讓我們從創建一個簡單的 Spring Boot 應用程序開始。

首先,我們將定義一個想要動態註冊的 Bean。然後,我們將提供一個屬性,用於決定如何註冊 Bean。最後,我們將定義一個配置類,該配置類將根據我們的自定義屬性來註冊 Bean。

2.1. 依賴項

讓我們先添加 Maven 依賴項:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>3.3.2</version>
    <scope>test</scope>
</dependency>

我們需要添加 spring-boot-starterspring-boot-starter-test 依賴。

2.2. Bean 類

接下來,我們定義一個 API 客户端,並根據我們的自定義應用程序屬性進行註冊:

public class ApiClient {
    private String name;
    private String url;
    private String key;
    // standard getters, setters and constructors
    
    public String getConnectionProperties() {
        return "Connecting to " + name + " at " + url;     
    }
}

假設我們想使用這個 Bean 來連接到不同的 API,根據我們提供的屬性。我們不想為每個 API 創建類定義。相反,我們希望定義屬性並動態註冊該 Bean 用於每個 API。

我們不應使用 ApiClient 類標記為 @Component@Service,因為我們不想使用組件掃描將其註冊為 Bean。

2.3. 屬性

我們將添加一個屬性,用於確定 Bean 應該註冊到哪些 API。 我們將在 application.yml 文件中定義此屬性:

api:
  clients:
    - name: example  
      url: https://api.example.com
      key: 12345
    - name: anotherexample
      url: https://api.anotherexample.com
      key: 67890

在這裏,我們定義了兩個客户端及其各自的屬性。我們將使用這些屬性在註冊 Bean 時。

3. 動態註冊 Bean

Spring 提供了一種使用 <em>BeanDefinitionRegistryPostProcessor</em> 接口動態註冊 Bean 的方式。<strong>該接口允許我們在註解 Bean 定義註冊後添加或修改 Bean 定義。</strong> 由於它在 Bean 實例化之前執行,因此 Bean 在應用程序上下文完全初始化之前就已經註冊。

3.1. <em>BeanDefinitionRegistryPostProcessor</em>

定義一個配置類,用於根據自定義屬性註冊 <em>ApiClient</em> bean

public class ApiClientConfiguration implements BeanDefinitionRegistryPostProcessor {
    private static final String API_CLIENT_BEAN_NAME = "apiClient_";
    List<ApiClient> clients;

    public ApiClientConfiguration(Environment environment) {
        Binder binder = Binder.get(environment);
        List<HashMap> properties = binder.bind("api.clients", Bindable.listOf(HashMap.class)).get();
        clients = properties.stream().map(client -> new ApiClient(String.valueOf(client.get("name")),
                String.valueOf(client.get("url")), String.valueOf(client.get("key")))).toList();
    }    

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        clients.forEach(client -> {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ApiClient.class);
            builder.addPropertyValue("name", client.getName());
            builder.addPropertyValue("url", client.getUrl());
            builder.addPropertyValue("key", client.getkey());
            registry.registerBeanDefinition(API_CLIENT_BEAN_NAME + client.getName(), builder.getBeanDefinition());
        });
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

在這裏,我們實現了 BeanDefinitionRegistryPostProcessor 接口。我們覆蓋了 postProcessBeanDefinitionRegistry 方法,該方法負責根據我們的自定義屬性註冊 Bean。

首先,我們定義了一個常量 API_CLIENT_BEAN_NAME,該常量將用作 Bean 名稱的前綴。在構造函數中,我們使用 Binder API 從 Environment 對象中讀取屬性。然後,我們使用屬性創建 ApiClient 對象。

在實現 postProcessBeanDefinitionRegistry() 方法時,我們迭代屬性並使用 BeanDefinitionRegistry 對象註冊 ApiClient Bean。

我們使用 BeanDefinitionBuilder 創建 Bean。這需要我們定義 Bean 類。然後,它允許我們通過字段名稱一次設置 Bean 屬性。

請注意,我們使用唯一的名稱註冊每個 Bean – API_CLIENT_BEAN_NAME + client.getName()。這將有助於我們從上下文中讀取我們選擇的 Bean。

3.2. 主應用程序類

最後,我們需要定義主應用程序類並使用 <em @SpringBootApplication</em> 註解對其進行標註:

@SpringBootApplication
public class RegistryPostProcessorApplication {

    public static void main(String[] args) {
        SpringApplication.run(RegistryPostProcessorApplication.class, args);
    }

    @Bean
    public ApiClientConfiguration apiClientConfiguration(ConfigurableEnvironment environment) {
        return new ApiClientConfiguration(environment);
    }
}

在這裏,我們定義了 ApiClientConfiguration Bean,並將 ConfigurableEnvironment 對象傳遞到構造函數中。 這將幫助我們讀取 ApiClientConfiguration 類中的屬性。

4. 測試

現在豆子已註冊完成,讓我們測試它們是否具有正確的屬性以連接到 API。 為了進行測試,我們將編寫一個簡單的測試類:

@SpringBootTest
class ApiClientConfigurationTest {
    @Autowired
    private ApplicationContext context;
    
    @Test
    void givenBeansRegistered_whenConnect_thenConnected() {
        ApiClient exampleClient = (ApiClient) context.getBean("apiClient_example");
        Assertions.assertEquals("Connecting to example at https://api.example.com", exampleClient.getConnectionProperties());
        
        ApiClient anotherExampleClient = (ApiClient) context.getBean("apiClient_anotherexample");
        Assertions.assertEquals("Connecting to anotherexample at https://api.anotherexample.com", anotherExampleClient.getConnectionProperties());
    }
}

在這裏,我們使用<em>@SpringBootTest</em>註解加載應用程序上下文。然後,我們使用<em>ApplicationContext</em>對象通過<em>getBean()</em>方法從上下文中獲取bean。<em>getBean()</em>方法接受唯一的bean名稱作為參數,並返回上下文中的bean。

該測試檢查bean是否正確註冊,並且連接屬性是否設置正確。

5. 結論

在本教程中,我們探討了如何使用自定義屬性動態註冊 Spring Bean 的方法,利用了 <em >BeanDefinitionRegistryPostProcessor</em> 接口。我們還編寫了一個簡單的測試用例,演示瞭如何從上下文中檢索 Bean 並使用它們。

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

發佈 評論

Some HTML is okay.