Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access properties in BeanFactoryPostProcessor

I am trying to create something which will auto-create beans based on configurable properties (from application.yml and the like).

Since I can't just access the properties component like I normally would in the BeanFactoryPostProcessor, I'm kind of stumped how I can access them.

How can I access application properties in BeanFactoryPostProcessor?

like image 293
samanime Avatar asked Dec 23 '22 02:12

samanime


2 Answers

If you want to access properties in a type-safe manner in a BeanFactoryPostProcessor you'll need to bind them from the Environment yourself using the Binder API. This is essentially what Boot itself does to support @ConfigurationProperties beans.

Your BeanFactoryPostProcessor would look something like this:

@Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor(
        Environment environment) {
    return new BeanFactoryPostProcessor() {

        @Override
        public void postProcessBeanFactory(
                ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BindResult<ExampleProperties> result = Binder.get(environment)
                    .bind("com.example.prefix", ExampleProperties.class);
            ExampleProperties properties = result.get();
            // Use the properties to post-process the bean factory as needed
        }

    };
}
like image 93
Andy Wilkinson Avatar answered Jan 03 '23 10:01

Andy Wilkinson


I didn't want to use the solution above that used an @Bean producer method since it's contrary to the recommended approach of annotating a class with @Component and picking up via component scanning. Fortunately it's straightforward to do that by implementing EnvironmentAware:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ConditionalDependencyPostProcessor implements BeanFactoryPostProcessor, EnvironmentAware {
  /** Logger instance. */
  private final Logger logger = LoggerFactory.getLogger(ConditionalDependencyPostProcessor.class);

  /** Spring environment. */
  private Environment environment;


  @Override
  public void setEnvironment(final Environment env) {
    environment = env;
  }
  ...
  private boolean hasRequiredProfiles(final DependencyInfo info) {
    final Set<String> activeProfiles = new HashSet<>(Arrays.asList(environment.getActiveProfiles()));
    for (String profile : info.requiredProfiles) {
      if (!activeProfiles.contains(profile)) {
        return false;
      }
    }
    return true;
  }

I should note what did NOT work: trying to autowire an Environment constructor argument. BeanFactoryPostProcessors require a no-argument constructor and don't support autowiring, which is itself a feature implemented by another post processor, AutowiredAnnotationBeanPostProcessor.

like image 41
serac Avatar answered Jan 03 '23 12:01

serac