1. Overview
Spring beans are typically registered declaratively via annotations like @Component or @Bean. But in some cases, we need more flexibility that we only have with programmatic registration, e.g., registering beans based on properties.
Spring 7 introduces the BeanRegistrar as a powerful new feature.
In this tutorial, we’ll look at what makes BeanRegistrar such a welcome addition, how it compares to the old way of registering beans, and how we can use it in both Java and Kotlin projects.
2. Why We Need Something New
Before Spring 7, we could already register beans programmatically via the BeanDefinitionRegistryPostProcessor type:
@Configuration
public class BeanRegistrationsSpring6Configuration {
@Bean
BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
var beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MyService.class);
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
registry.registerBeanDefinition("myService", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// No-op
}
};
}
}
While flexible, it has drawbacks:
- It’s verbose and low-level.
- It’s difficult to read and test.
- It’s not friendly to native image (AOT) scenarios.
3. The New BeanRegistrar Type
BeanRegistrar is a functional interface that lets us register beans in a clean, declarative, and native-compatible way. The following example uses BeanRegistrar to register one bean of type MyService with default settings, and another bean named “myService2” with prototype scope and lazy initialization programmatically:
@Configuration
@Import(BeanRegistrationsConfiguration.MyBeanRegistrar.class)
public class BeanRegistrationsConfiguration {
static class MyBeanRegistrar implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
registry.registerBean(MyService.class);
registry.registerBean("myService2", MyService.class, spec -> spec
.prototype()
.lazyInit()
.primary()
);
}
}
}
We need to be aware that we cannot use the @Bean-style for the BeanRegistrar. We should instead use @Import because the bean must be discovered during Spring’s configuration processing time.
4. Kotlin DSL Support
For Kotlin, we can use BeanRegistrarDsl to have first-class support. The same example would then look like this:
@Configuration
@Import(BeanRegistrationsConfiguration.MyBeanRegistrar::class)
class BeanRegistrationsConfiguration {
class MyBeanRegistrar : BeanRegistrarDsl({
registerBean<MyService>()
registerBean( name = "myService2", prototype = true, lazyInit = true ){
MyService()
}
})
}
5. Conclusion
In this article, we’ve learned that BeanRegistrar is a feature that lets us register beans in a clean, declarative, and native-compatible way.
We can find the original discussion about this feature in the Spring Framework Project at GitHub.
As always, the code presented in this article is available over on GitHub.