知識庫 / Spring RSS 訂閱

Bean工廠處理器中的屬性

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

1. 概述

在 Spring 中,可以使用 @Value 註解直接注入屬性到我們的 Bean 中,通過 Environment 抽象訪問它們,或者通過 @ConfigurationProperties 將它們綁定到結構化對象上。 如果我們嘗試在 BeanFactoryPostProcessor 中使用這些常規方式注入屬性,它將不起作用! 這是因為這些註解由 BeanPostProcessor 處理,這意味着它們不能在 BeanPostProcessorBeanFactoryPostProcessor 類型中使用。

在本教程中,我們將學習如何使用 Environment 類在 BeanFactoryPostProcessor 中注入屬性。 為了演示其與 Spring Boot 的工作方式,我們將使用 Binder 代替 @ConfigurationProperties。 最後,我們將演示使用 @Bean 註解和 @Component 註解創建 BeanFactoryPostProcessor 的兩種方法。

2. BeanFactoryPostProcessor 中的屬性

要注入 BeanFactoryPostProcessor 中的屬性,我們需要檢索 Environment 對象。

  • 使用 getProperty() 方法獲取每個要檢索的屬性
  • 使用 BinderEnvironment 來檢索整個 ConfigurationFile

使用 getProperty() 方法可以方便地獲取少量屬性。如果需要檢索大量屬性,則使用 ConfigurationFile 會更簡單,假設所有屬性都存在於同一個文件中。 請注意,@ConfigurationFile 是 Spring Boot 的特性,因此在簡單的 Spring 應用中不可用。

3. BeanFactoryPostProcessor 中的屬性使用 @Bean 註解

現在我們來看如何使用 Environment 抽象來獲取屬性,當我們使用 @Bean 註解創建 BeanFactoryPostProcessor 時。

首先,我們將創建一個配置文件,其中定義一個類型為 BeanFactoryPostProcessor 的 Bean,並注入 Environment 作為參數。然後,我們可以使用 getProperty() 方法或 Binder,如下一節所示。

3.1. Environment 環境的 getProperty() 方法

使用 getProperty() 方法最適用於只需要獲取少量屬性的情況:

@Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor(Environment environment) {
    return beanFactory -> {
        String articleName = environment.getProperty("article.name", String.class);
        LOGGER.debug("Article name, using environment::getProperty: " + articleName);

        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        registry.registerBeanDefinition("articleNameFromBeanAnnotation", BeanDefinitionBuilder.genericBeanDefinition(String.class)
          .addConstructorArgValue(articleName)
          .getBeanDefinition());
    };
}

在上面的示例中,我們使用 environment.getProperty() 獲取 article.name 屬性,然後創建一個新的佔位符 Bean,名稱為 articleNameFromBeanAnnotation,用於存儲其值。

3.2. Binder

我們還可以將 BinderEnvironment 結合,以加載整個配置文件。 這樣,我們就可以利用 Spring Boot 應用的 @ConfigurationFile 註解。

@Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor(Environment environment) {
    return beanFactory -> {
        BindResult<ApplicationProperties> result = Binder.get(environment)
          .bind("application", ApplicationProperties.class);
        ApplicationProperties properties = result.get();
        LOGGER.debug("Application name, using binder to access ApplicationProperties: " + properties.getName());

        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        registry.registerBeanDefinition("applicationNameFromBeanAnnotation", BeanDefinitionBuilder.genericBeanDefinition(String.class)
          .addConstructorArgValue(properties.getName())
          .getBeanDefinition());
    };
}

在本示例中,我們使用 Binder 的方法 get()bind() 加載當前環境的配置文件。然後,我們使用配置文件的獲取器來檢索應用程序名稱。最後,我們將該值存儲在一個名為 applicationNameFromBeanAnnotation 的佔位符 Bean 中。

4. BeanFactoryPostProcessor 中的屬性:使用 @Component 註解

另一種創建 BeanFactoryPostProcessor 的方式是使用 @Component 註解。在這種情況下,與使用 @Bean 註解類似,我們需要 Environment 抽象。區別在於,我們無法注入 Environment,因為 BeanFactoryPostProcessor 僅提供無參數構造函數。解決方案是使用 EnvironmentAware 接口來注入環境:

@Component
public class PropertiesWithBeanFactoryPostProcessor implements BeanFactoryPostProcessor, EnvironmentAware {

    private Environment environment;

    @Override
    public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String articleName = environment.getProperty("article.name", String.class);
        LOGGER.debug("Article name, using environment::getProperty: " + articleName);

        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        registry.registerBeanDefinition("articleNameFromComponentAnnotation", BeanDefinitionBuilder.genericBeanDefinition(String.class)
          .addConstructorArgValue(articleName)
          .getBeanDefinition());
    }

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        this.environment = environment;
    }
}

此方法提供 setEnvironment(),它是 Spring 應用中注入 Bean 的另一種方式。然後,我們存儲注入的值到 Environment 字段中。在 postProcessBeanFactory() 方法中,我們可以調用 getProperty() 方法或使用 Binder,通過該字段。在上述代碼中,我們使用了前者。

4.1. 獲取Environment環境屬性的方法

我們可以使用 getProperty() 方法來檢索少量屬性:

@Override
public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String articleName = environment.getProperty("article.name", String.class);
    LOGGER.debug("Article name, using environment::getProperty: " + articleName);

    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    registry.registerBeanDefinition("articleNameFromComponentAnnotation", BeanDefinitionBuilder.genericBeanDefinition(String.class)
      .addConstructorArgValue(articleName)
      .getBeanDefinition());
}

在此示例中,我們使用 environment.getProperty() 從屬性中加載文章名稱並將其存儲在 articleNameFromComponentAnnotation Bean 中。

4.2. Binder

在 Spring Boot 應用中,我們可以使用 BinderEnvironment 檢索一個包含一組屬性的配置文件。

@Override
public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
    BindResult<ApplicationProperties> result = Binder.get(environment)
      .bind("application", ApplicationProperties.class);
    ApplicationProperties properties = result.get();
    LOGGER.debug("Application name, using binder to access ApplicationProperties: " + properties.getName());

    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    registry.registerBeanDefinition("applicationNameFromComponentAnnotation", BeanDefinitionBuilder.genericBeanDefinition(String.class)
      .addConstructorArgValue(properties.getName())
      .getBeanDefinition());
}

使用 <em >Binder.get().bind()</em >, 我們加載 <em >ApplicationProperties</em >,然後使用它的 getter 方法將應用程序名稱存儲在applicationNameFromComponentAnnotation` Bean 中。

5. 結論

本文討論了在 <em>BeanFactoryPostProcessor</em> 中直接注入屬性所面臨的問題。我們展示瞭如何利用諸如 <em>Environment</em> 抽象、<em>Binder</em><em>BeanNameAware</em>@ConfigurationFile 等概念來克服這些困難。最後,我們討論了每種方案的權衡利弊。

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

發佈 評論

Some HTML is okay.