根據屬性動態註冊Spring Bean
1. 概述
在本教學中,我們將探討如何根據自訂屬性動態註冊 Bean。我們將探討BeanDefinitionRegistryPostProcessor
介面以及如何使用它來為應用程式上下文新增 bean。
2. 代碼設定
讓我們先建立一個簡單的 Spring Boot 應用程式。
首先,我們將定義一個要動態註冊的 bean。然後,我們將提供一個屬性來決定如何註冊 beans 。最後,我們將定義一個配置類,它將根據我們的自訂屬性註冊 bean。
2.1.依賴關係
讓我們先新增 Maven 依賴項:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>3.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>3.2.3</version> <scope>test</scope> </dependency>
我們需要加入spring-boot-starter
和spring-boot-starter-test
依賴項。
2.2.豆類
接下來,我們定義一個要根據自訂應用程式屬性註冊的 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 建立類別定義。相反,我們希望為每個 API 動態定義屬性並註冊 bean。
我們不應該使用@Component
或@Service
註解ApiClient
類,因為我們不想使用元件掃描將其註冊為 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 提供了一種使用BeanDefinitionRegistryPostProcessor
介面動態註冊 bean 的方法。此介面允許我們在註冊帶有註解的 bean 定義後新增或修改 bean 定義。由於它發生在 bean 實例化之前,因此 bean 在應用程式上下文完全初始化之前註冊。
3.1. BeanDefinitionRegistryPostProcessor
讓我們定義一個配置類,它將根據自訂屬性註冊ApiClient
beans :
`public class ApiClientConfiguration implements BeanDefinitionRegistryPostProcessor {
private static final String API_CLIENT_BEAN_NAME = "apiClient_";
List
public ApiClientConfiguration(Environment environment) {
Binder binder = Binder.get(environment);
List
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
beans。
我們使用BeanDefinitionBuilder.
它要求我們定義bean類別。然後它讓我們使用欄位名稱一一來設定 bean 屬性。
請注意,我們使用唯一的名稱註冊每個 bean – API_CLIENT_BEAN_NAME + client.getName()
。當我們想要從上下文中讀取我們選擇的 bean 時,這將很有幫助。
3.2.主要應用類
最後,我們需要定義主應用程式類別並使用@SpringBootApplication
註解:
`@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. 測試
現在 Bean 已註冊,讓我們測試它們是否具有連接到 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());
}
}`
在這裡,我們使用@SpringBootTest
註解來載入應用程式上下文。然後,我們使用ApplicationContext
物件透過getBean()
方法從上下文中取得 bean。 getBean()
方法將唯一的 bean 名稱作為參數,並從上下文中傳回該 bean。
此測試檢查 Bean 是否已正確註冊並設定了正確的連線屬性。
5. 結論
在本教學中,我們探討如何使用BeanDefinitionRegistryPostProcessor
介面根據自訂屬性動態註冊 Spring bean。我們還編寫了一個簡單的測試案例,展示如何從上下文中檢索 bean 並使用它們。
與往常一樣,範例的程式碼可以在 GitHub 上取得。