Simplify Dynamic Bean Registration in Spring Boot 4 with the New BeanRegistrar API

This article explains how Spring Framework 7 introduces the BeanRegistrar interface to replace the cumbersome ImportBeanDefinitionRegistrar, demonstrating the new API with concise code examples, comparing old and new approaches, and providing a complete practical example for dynamically registering beans based on runtime configuration.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
Simplify Dynamic Bean Registration in Spring Boot 4 with the New BeanRegistrar API

What is BeanRegistrar?

The BeanRegistrar interface, added in Spring Framework 7, defines a single method

void register(BeanRegistry registry, Environment environment)

. It receives a BeanRegistry for bean registration and an Environment for reading configuration, eliminating the need for separate interfaces like EnvironmentAware.

BeanRegistry : API used to register beans programmatically.

Environment : Provides access to configuration properties.

BeanRegistry API Deep Dive

2.1 Basic Registration

New way (BeanRegistrar) – one line registration:

registry.registerBean("pigUserService", PigUserService.class);

Old way (ImportBeanDefinitionRegistrar) – requires a builder and string class name:

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition("com.pig4cloud.pigx.admin.service.impl.PigUserService");
registry.registerBeanDefinition("pigUserService", builder.getBeanDefinition());

2.2 Registration with Configuration

New API supports fluent configuration:

registry.registerBean("pigOrderService", PigOrderService.class, spec -> spec
    .prototype()
    .lazyInit()
    .description("PIG 订单服务"));

Old API requires multiple setter calls:

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition("com.pig4cloud.pigx.mall.service.impl.PigOrderService");
builder.setScope("prototype");
builder.setLazyInit(true);
builder.setDescription("PIG 订单服务");
registry.registerBeanDefinition("pigOrderService", builder.getBeanDefinition());

2.3 Custom Creation Logic with Dependency Injection

New API allows a supplier that receives the context:

registry.registerBean("pigGoodsService", PigGoodsService.class, spec -> spec.supplier(context -> {
    PigUserService userService = context.bean(PigUserService.class);
    return new PigGoodsService(userService);
}));

Old API needs manual constructor‑arg references and factory methods:

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition("com.pig4cloud.pigx.mall.service.impl.PigGoodsService");
builder.addConstructorArgReference("pigUserService");
registry.registerBeanDefinition("pigGoodsService", builder.getBeanDefinition());

2.4 Conditional Registration

New API can use any Java logic directly:

if (environment.matchesProfiles("production")) {
    registry.registerBean(PigCacheService.class);
}
if (environment.getProperty("pig.cache.enabled", Boolean.class, false)) {
    String cacheType = environment.getProperty("pig.cache.type", "redis");
    if ("redis".equals(cacheType)) {
        registry.registerBean(PigRedisCacheService.class);
    } else {
        registry.registerBean(PigLocalCacheService.class);
    }
}

Old API requires implementing EnvironmentAware and using the environment inside registerBeanDefinitions, adding boilerplate.

Practical Example: Dynamic Message Service Selection

Scenario: a multi‑tenant SaaS system chooses a message‑push implementation (email, DingTalk, or WeChat) based on a database‑stored configuration.

3.1 Service Interface

public interface PigMessageService {
    String sendMessage(String content);
    String getServiceType();
}

3.2 Implementations

public class PigEmailMessageService implements PigMessageService {
    @Override
    public String sendMessage(String content) {
        return "邮件发送成功,时间:" + LocalDateTime.now() + ",内容:" + content;
    }
    @Override
    public String getServiceType() { return "EMAIL"; }
}

public class PigSmsMessageService implements PigMessageService {
    @Override
    public String sendMessage(String content) {
        return "短信发送成功,时间:" + LocalDateTime.now() + ",内容:" + content;
    }
    @Override
    public String getServiceType() { return "SMS"; }
}

3.3 Old Registrar (ImportBeanDefinitionRegistrar)

public class PigMessageServiceRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    private Environment environment;
    @Override public void setEnvironment(Environment env) { this.environment = env; }
    @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        String type = environment.getProperty("pig.message.type", "email");
        BeanDefinitionBuilder builder;
        switch (type.toLowerCase()) {
            case "email":
                builder = BeanDefinitionBuilder.genericBeanDefinition("com.pig4cloud.pigx.common.message.impl.PigEmailMessageService");
                break;
            case "sms":
                builder = BeanDefinitionBuilder.genericBeanDefinition("com.pig4cloud.pigx.common.message.impl.PigSmsMessageService");
                break;
            default:
                throw new IllegalArgumentException("未知的消息类型:" + type);
        }
        builder.setScope("singleton");
        builder.setLazyInit(false);
        registry.registerBeanDefinition("pigMessageService", builder.getBeanDefinition());
    }
}

Problems: requires extra interface, verbose builder API, string class names, and multiple setter calls – about 54 lines of code.

3.4 New Registrar (BeanRegistrar)

public class PigMessageServiceRegistrar implements BeanRegistrar {
    @Override
    public void register(BeanRegistry registry, Environment environment) {
        String type = environment.getProperty("pig.message.type", "email");
        switch (type.toLowerCase()) {
            case "email" -> registry.registerBean(
                "pigMessageService",
                PigEmailMessageService.class,
                spec -> spec.description("PIG 邮件消息服务"));
            case "sms" -> registry.registerBean(
                "pigMessageService",
                PigSmsMessageService.class,
                spec -> spec.description("PIG 短信消息服务"));
            default -> throw new IllegalArgumentException("未知的消息类型:" + type);
        }
    }
}

This version reduces the code to 27 lines, uses type‑safe class references, and provides a fluent API for configuration.

3.5 Activation

@Configuration
@Import(PigMessageServiceRegistrar.class)
public class PigMessageAutoConfiguration {
    // other @Bean definitions can coexist
}

Configuration file (application.yml):

pig:
  message:
    type: email  # options: email, sms

Changing the value and restarting the application switches the implementation automatically.

Conclusion

Spring’s 20‑year‑old framework continues to evolve; the new BeanRegistrar interface in Spring Boot 4 streamlines programmatic bean registration, cuts boilerplate, and improves type safety. For any scenario requiring dynamic bean registration, adopting BeanRegistrar yields cleaner, more maintainable code.

PIG 项目中动态 bean 的实现
PIG 项目中动态 bean 的实现
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaSpringdynamic-bean-registrationBeanRegistrarSpring Boot 4
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.