Spring Boot
ConfigurationProperties
Custom Starter
Java
Bean Creation

Custom Spring Boot 3 Starter does not create ConfigurationProperties beans

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A custom Spring Boot 3 starter does not create @ConfigurationProperties beans automatically just because the class exists on the classpath. In Boot 3, your starter has to register its auto-configuration correctly, and that auto-configuration must either enable the properties type or expose a bean that uses configuration-property binding.

What Usually Goes Wrong

There are two common causes.

First, the properties class is annotated with @ConfigurationProperties, but nothing tells Spring Boot to register it. @ConfigurationProperties describes binding metadata; it is not itself a bean-registration mechanism.

Second, the starter still uses the old spring.factories registration pattern from older Boot versions. Spring Boot 3 expects auto-configurations to be listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.

If either piece is missing, the application starts, the starter JAR is present, but no configuration properties bean appears.

A Correct Spring Boot 3 Starter Layout

Start with a properties class:

java
1package com.example.mystarter;
2
3import org.springframework.boot.context.properties.ConfigurationProperties;
4
5@ConfigurationProperties(prefix = "demo.client")
6public class DemoClientProperties {
7
8    private String baseUrl = "https://api.example.com";
9    private int timeoutSeconds = 5;
10
11    public String getBaseUrl() {
12        return baseUrl;
13    }
14
15    public void setBaseUrl(String baseUrl) {
16        this.baseUrl = baseUrl;
17    }
18
19    public int getTimeoutSeconds() {
20        return timeoutSeconds;
21    }
22
23    public void setTimeoutSeconds(int timeoutSeconds) {
24        this.timeoutSeconds = timeoutSeconds;
25    }
26}

Then create an auto-configuration class and explicitly enable that properties type:

java
1package com.example.mystarter;
2
3import org.springframework.boot.autoconfigure.AutoConfiguration;
4import org.springframework.boot.context.properties.EnableConfigurationProperties;
5import org.springframework.context.annotation.Bean;
6
7@AutoConfiguration
8@EnableConfigurationProperties(DemoClientProperties.class)
9public class DemoClientAutoConfiguration {
10
11    @Bean
12    DemoClient demoClient(DemoClientProperties properties) {
13        return new DemoClient(properties.getBaseUrl(), properties.getTimeoutSeconds());
14    }
15}

Finally, register the auto-configuration class in:

text
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

with this content:

text
com.example.mystarter.DemoClientAutoConfiguration

That file is what allows Spring Boot 3 to discover the starter’s auto-configuration.

How the Application Uses It

Once the starter is on the classpath, the consuming application can configure it with normal externalized properties.

yaml
1demo:
2  client:
3    base-url: https://internal-api.example.com
4    timeout-seconds: 10

When Boot processes auto-configuration, it registers DemoClientProperties, binds values from configuration, and injects that bean into demoClient.

Alternative Pattern: @ConfigurationPropertiesScan

@ConfigurationPropertiesScan is useful in an application module, but it is usually not the right fix for a starter. A starter should be self-contained and not require the consuming application to scan the starter package explicitly.

That is why @EnableConfigurationProperties inside the starter auto-configuration is the more reliable approach. It makes the starter work when present, which is exactly what auto-configuration is supposed to do.

Testing the Starter

A good starter should include an auto-configuration test so you can prove the properties bean exists.

java
1package com.example.mystarter;
2
3import org.junit.jupiter.api.Test;
4import org.springframework.boot.autoconfigure.AutoConfigurations;
5import org.springframework.boot.test.context.runner.ApplicationContextRunner;
6
7import static org.assertj.core.api.Assertions.assertThat;
8
9class DemoClientAutoConfigurationTests {
10
11    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
12        .withConfiguration(AutoConfigurations.of(DemoClientAutoConfiguration.class))
13        .withPropertyValues("demo.client.timeout-seconds=12");
14
15    @Test
16    void createsConfigurationPropertiesBean() {
17        contextRunner.run(context -> {
18            assertThat(context).hasSingleBean(DemoClientProperties.class);
19            assertThat(context.getBean(DemoClientProperties.class).getTimeoutSeconds()).isEqualTo(12);
20        });
21    }
22}

That test catches registration mistakes early, which is especially useful when refactoring starter packaging.

Common Pitfalls

The biggest pitfall is expecting @ConfigurationProperties alone to create a bean. It does not. You still need registration through @EnableConfigurationProperties, scanning, or an explicit bean method.

Another mistake is using spring.factories as if Boot 3 still discovered auto-configuration from there. For Boot 3 starters, use AutoConfiguration.imports.

Developers also put the properties class in the starter but forget that component scanning usually starts from the application package, not from arbitrary dependency packages. Relying on scan side effects is brittle.

Finally, do not skip tests. A starter that “looks right” in code can still fail quietly if its metadata files are misplaced or not packaged into the JAR correctly.

Summary

  • '@ConfigurationProperties describes binding; it does not automatically register a bean in a custom starter.'
  • In Spring Boot 3, register starter auto-configuration with META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
  • Use @AutoConfiguration plus @EnableConfigurationProperties in the starter to expose the properties bean reliably.
  • Do not depend on the consuming application to scan starter packages.
  • Add an ApplicationContextRunner test so packaging or registration mistakes are caught immediately.

Course illustration
Course illustration

All Rights Reserved.