在 Spring 中,可以使用@Value注释将属性直接注入到我们的 bean 中,通过Environment抽象访问属性,或者通过@ConfigurationProperties将属性绑定到结构化对象。
如果我们尝试使用这些常规方法在BeanFactoryPostProcessor中注入属性,它将无法工作!
这是因为这些注释是由BeanPostProcessor处理的,这意味着它们不能在BeanPostProcessor或BeanFactoryPostProcessor类型中使用。
在本教程中,我们将学习使用Environment类在BeanFactoryPostProcessor中注入属性。为了展示它如何与 Spring Boot 配合使用,我们将使用Binder代替@ConfigurationProperties。最后,我们将演示使用@Bean注释和@Component注释创建BeanFactoryPostProcessor的两个选项。
BeanFactoryPostProcessor中的属性
为了在BeanFactoryPostProcessor中注入属性,我们需要检索Environment对象。然后,使用它,我们可以:
- 对我们希望获取的每个属性使用getProperty()方法
- 使用Binder和Environment检索整个ConfigurationFile
getProperty ()方法可以方便地获取少量属性。如果我们希望获取更多属性,使用 ConfigurationFile 会更简单,假设所有属性都存在于同一个文件中。请注意,@ConfigurationFile是 Spring Boot 的一项功能,因此在简单的 Spring 应用程序中不可用。
1、使用@Bean注解的BeanFactoryPostProcessor中的属性
现在让我们看看当我们使用@Bean注释创建BeanFactoryPostProcessor时如何使用Environment抽象来获取属性。
首先,我们将创建一个配置文件,在其中定义一个BeanFactoryPostProcessor类型的 bean ,并将Environment作为参数注入。然后,我们可以使用getProperty()方法或Binder,如下一节所示。
1.环境的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属性,然后创建一个名为articleNameFromBeanAnnotation的新虚拟 bean来保存其值。
2.Binder
我们还可以结合Binder和Environment来加载整个配置文件。这样,我们可以利用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()方法加载当前环境的配置文件。然后,我们使用配置文件 getter 检索应用程序名称。最后将其值存储在名为applicationNameFromBeanAnnotation的虚拟 bean 中。
2、使用@Component注释的BeanFactoryPostProcessor中的属性
创建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。在上面的代码中,我们使用了前者。
1.环境的getProperty() 方法
我们可以使用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 中。
2.Binder
在 Spring Boot 应用程序中,我们可以使用Binder与Environment来检索包含一组属性的配置文件:
@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()); }
|
使用Binder.get().bind(),我们加载ApplicationProperties,然后使用它的 getter 将应用程序名称存储在applicationNameFromComponentAnnotation bean 中。