Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically register Spring Beans based on properties

I'm using the latest Spring Boot version and trying to dynamically create n number of beans based upon what is defined in the application.yaml file. I would then like to inject these beans into other classes based upon the bean name.

The code below is a much simplified example of what I am trying to achieve. The auto configuration would normally be part of a spring boot starter library so the number of beans needed to be registered is unknown.

@Slf4j
@Value
public class BeanClass {

    private final String name;

    public void logName() {
        log.info("Name: {}", name);
    }

}
@Component
@RequiredArgsConstructor
public class ServiceClass {

    private final BeanClass fooBean;
    private final BeanClass barBean;

    public void log() {
        fooBean.logName();
        barBean.logName();
    }

}
@Value
@ConfigurationProperties
public class BeanProperties {

    private final List<String> beans;

}
@Configuration
public class AutoConfiguration {

    // Obviously not correct
    @Bean
    public List<BeanClass> beans(final BeanProperties beanProperties) {
        return beanProperties.getBeans().stream()
                .map(BeanClass::new)
                .collect(Collectors.toList());
    }

}
@EnableConfigurationProperties(BeanProperties.class)
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        final ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        final ServiceClass service = context.getBean(ServiceClass.class);
        service.log();
    }

}
beansToMake:
  - fooBean
  - barBean

I've tried multiple suggestions on google but nothing works and seems outdated. I'm hoping a new feature of Spring makes this straight forward.

like image 334
sooty1892 Avatar asked Mar 09 '26 21:03

sooty1892


1 Answers

You can implement BeanDefinitionRegistryPostProcessor interface to register BeanClass beans' definitions as follows:

public class DynamicBeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor {

  public static final String PROPERTIES_PREFIX = "beans";
  private final List<String> beanNames;

  public DynamicBeanDefinitionRegistrar(Environment environment) {
    beanNames =
        Binder.get(environment)
            .bind(PROPERTIES_PREFIX, Bindable.listOf(String.class))
            .orElseThrow(IllegalStateException::new);
  }

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
      throws BeansException {
    beanNames.forEach(
        beanName -> {
          GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
          beanDefinition.setBeanClass(BeanClass.class);
          beanDefinition.setInstanceSupplier(() -> new BeanClass(beanName));
          registry.registerBeanDefinition(beanName, beanDefinition);
        });
  }

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
      throws BeansException {}
}

Since properties are needed before beans are instantiated, to register BeanClass beans' definitions, @ConfigurationProperties are unsuitable for this case. Instead, Binder API is used to bind them programmatically.

Because BeanFactoryPostProcessor objects, in general, must be instantiated very early in the container lifecycle, @Bean methods should be marked as static in @Configuration classes to avoid lifecycle issues, according to Spring documentation.

@Configuration
public class DynamicBeanDefinitionRegistrarConfiguration {
  @Bean
  public static DynamicBeanDefinitionRegistrar beanDefinitionRegistrar(Environment environment) {
    return new DynamicBeanDefinitionRegistrar(environment);
  }
}

As a result, all beans you define in application.yml, are registered as BeanClass beans:

beans: 
    - fooBean
    - barBean

For reference: Create N number of beans with BeanDefinitionRegistryPostProcessor, Spring Boot Dynamic Bean Creation From Properties File

like image 152
Toni Avatar answered Mar 12 '26 11:03

Toni



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!