Sitemap
ITNEXT

ITNEXT is a platform for IT developers & software engineers to share knowledge, connect, collaborate, learn and experience next-gen technologies.

Programmatic Bean Registration with BeanRegistrar

--

Guangzhou City, China

Prior to Spring Framework 7, beans could be registered programmatically in several ways — for example, by implementing BeanDefinitionRegistryPostProcessor or ImportBeanDefinitionRegistrar, or by manipulating the ApplicationContext directly.

A custom BeanDefinitionRegistryPostProcessor might look like this:

public class MyBeanRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinitionBuilder postRepositoryBeanDef = BeanDefinitionBuilder.genericBeanDefinition(PostRepository.class);
registry.registerBeanDefinition("posts", postRepositoryBeanDef.getBeanDefinition());
}
}

This approach is somewhat verbose and requires familiarity with Spring internals.

Another example is to register beans directly with an AnnotationConfigApplicationContext which is introduced in Spring 5:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
PostRepository posts = new PostRepository();
PostHandler postHandler = new PostHandler(posts);
Routes routesBean = new Routes(postHandler);

context.registerBean(PostRepository.class, () -> posts);
context.registerBean(PostHandler.class, () -> postHandler);
context.registerBean(Routes.class, () -> routesBean);
context.registerBean(WebHandler.class, () -> RouterFunctions.toWebHandler(routesBean.routes(), HandlerStrategies.builder().build()));
context.refresh();

Here is the complete project of the above code snippet, see spring-reactive-sample/register-bean.

However, this technique also entails considerable boilerplate.

Spring Framework 7 introduces a more concise API for programmatic bean registration via a BeanRegistrar interface. For example, you might have two implementations of a PostRepository interface — one backed by R2DBC and another using an in-memory alternative.

Get Hantsy’s stories in your inbox

Join Medium for free to get updates from this writer.

The following example demonstrates conditional registration based on the active profile using BeanRegistrar:

class MyBeanRegistrar implements BeanRegistrar {

@Override
public void register(BeanRegistry registry, Environment env) {
if (env.matchesProfiles("h2")) {
// registry.registerBean(R2dbcConfig.class);
registry.registerBean(
PostRepository.class,
(BeanRegistry.Spec<PostRepository> spec) -> spec
.supplier(ctx -> new H2PostRepository(ctx.bean(DatabaseClient.class)))
);
} else {
registry.registerBean(
PostRepository.class,
(BeanRegistry.Spec<PostRepository> spec) -> spec
.supplier(ctx -> new InMemoryPostRepository())
);
}

registry.registerBean(PostHandler.class,
(BeanRegistry.Spec<PostHandler> spec) -> spec
.supplier(ctx -> new PostHandler(ctx.bean(PostRepository.class)))
);

registry.registerBean(Routes.class,
(BeanRegistry.Spec<Routes> spec) -> spec
.supplier(ctx -> new Routes(ctx.bean(PostHandler.class))));

registry.registerBean("webHandler", WebHandler.class,
(BeanRegistry.Spec<WebHandler> spec) -> spec
.prototype()
.supplier(ctx -> RouterFunctions.toWebHandler(ctx.bean(Routes.class).routes(), HandlerStrategies.builder().build()))
);
}
}

Import this custom MyBeanRegistrar into a configuration class to activate it:

@Configuration
@Import(MyBeanRegistrar.class)
public class CustomConfig {
}

BeanRegistrar provides fine-grained control over bean registration, including scope (singleton, prototype, etc.), lazy initialization, and custom suppliers for instantiation. It is also compatible with native-image compilation using GraalVM.

Annotating the test class with @ActiveProfiles("h2") activates the h2 profile, causing the H2PostRepository implementation to be registered and injected.

@SpringJUnitConfig(classes = {CustomConfig.class, R2dbcConfig.class})
@ActiveProfiles("h2")
class H2ApplicationTest {

@Autowired
PostRepository postRepository;

@Test
void testPostRepository() {
assertThat(this.postRepository).isInstanceOf(H2PostRepository.class);
}

@Test
void testCurdOperations() {
Post post = new Post(null, "Test Title", "Test Content");
Mono<UUID> savedPostId = this.postRepository.save(post);

savedPostId.flatMap(id -> this.postRepository.findById(id))
.as(StepVerifier::create)
.consumeNextWith(p -> {
assertThat(p.title()).isEqualTo("Test Title");
assertThat(p.content()).isEqualTo("Test Content");
})
.verifyComplete();

savedPostId.flatMap(id ->
this.postRepository.update(id, new Post(null, "Updated Title", "Updated Content"))
)
.subscribe(updatedCount -> System.out.println("Updated rows: " + updatedCount));

this.postRepository.findAll().subscribe(System.out::println);

savedPostId.flatMap(id -> this.postRepository.deleteById(id))
.subscribe(System.out::println);

this.postRepository.findAll().subscribe(System.out::println);
}
}

Elsewhere, the InMemoryPostRepository will be used instead.

@SpringJUnitConfig(CustomConfig.class)
class ApplicationTest {

@Autowired
PostRepository postRepository;

@Test
void testPostRepository() {
assertThat(this.postRepository).isInstanceOf(InMemoryPostRepository.class);
}
}

Spring 7 additionally offers BeanRegistrarDsl, a Kotlin extension that enables a more fluent registration style with Kotlin DSL.

Here is an example written in Kotlin and utilizing BeanRegistrarDsl:

@Configuration
@Import(MyBeanRegistrar::class)
class CustomConfig

class MyBeanRegistrar : BeanRegistrarDsl({
registerBean { InMemoryPostRepository() }
registerBean { PostHandler(bean()) }
registerBean { Routes(bean()) }

registerBean(
name = "webHandler",
backgroundInit = true,
prototype = false,
supplier = {
RouterFunctions.toWebHandler(bean<Routes>().routes(), HandlerStrategies.builder().build())
}
)
register(FoobarRegistrar())
})

class FoobarRegistrar : BeanRegistrarDsl({
profile("foo") {
registerBean<Bar> { Bar(bean<Foo>()) }
registerBean { Foo() }
}
})

class Bar(foo: Foo)
class Foo

See the repository examples for complete samples:

--

--

Hantsy
Hantsy

Written by Hantsy

Self-employed technical consultant, solution architect and full-stack developer, open source contributor, freelancer and remote worker

ITNEXT
ITNEXT

Published in ITNEXT

ITNEXT is a platform for IT developers & software engineers to share knowledge, connect, collaborate, learn and experience next-gen technologies.