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-starter 和 spring-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 並使用它們。