Backend Development 9 min read

Mastering Feature Toggles in Spring Boot 3: 6 Practical Implementations

This article explains why feature flags are essential in Spring Boot projects and walks through six concrete ways to implement dynamic toggles—including @Profile, @Conditional, @RefreshScope, database‑backed scheduling, Apache Commons Configuration, and Togglz—complete with code snippets and usage tips.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Feature Toggles in Spring Boot 3: 6 Practical Implementations

In Spring Boot projects, feature flags (Feature Toggles) allow dynamic control of business logic or modules, enabling gray releases, risk isolation, and fine‑grained operations.

By decoupling flag configuration from code, developers can adjust status via configuration centers (Nacos, Consul), databases, or local files without restarting services. Typical scenarios include small‑scale validation before new feature rollout, emergency rollback of major events, A/B testing, and fault isolation.

This article presents six practical implementations of feature toggles in Spring Boot 3.4.2.

1. Using @Profile Annotation

@Profile marks a component to be registered only when specified profiles are active. Example:

<code>@Component
@Profile({"prod"})
public class CommonComponent {
    @PostConstruct
    public void init() {
        System.err.println("CommonComponent init...");
    }
}
</code>

The component registers only if spring.profiles.active includes "prod". When applied to @Configuration , all @Bean and @Import within are active only for the specified profile.

Logical operators can be used in @Profile , e.g. "!prod", "prod && ss".

<code>// Activate when profile is not prod
@Profile("!prod")
// Activate when both prod and ss are active
@Profile("prod && ss")
</code>

2. Using @Conditional Annotation

@Conditional (or its meta‑annotation @ConditionalOnProperty ) registers a bean only when all specified conditions are met.

<code>@Component
@ConditionalOnProperty(prefix = "pack.app", name = "enabled", havingValue = "true", matchIfMissing = true)
public class CommonComponent {
    @PostConstruct
    public void init() {
        System.err.println("CommonComponent init...");
    }
}
</code>

Only when pack.app.enabled=true (or missing) the component is created.

3. Using @RefreshScope (Spring Cloud)

Add Spring Cloud dependencies and enable bootstrap.yml . Annotate beans with @RefreshScope to allow runtime refresh via /actuator/refresh .

<code>@RefreshScope
@Component
@ConfigurationProperties(prefix = "pack.toggle")
public class CommonComponent {
    private boolean enabled;
    // getters and setters
}
</code>

Initial configuration in bootstrap.yml :

<code>pack:
  toggle:
    enabled: false
</code>

After changing the value to true and calling /refresh , the new property is applied.

4. Database‑Backed Toggle with @Scheduled

Persist toggle state in a database table and poll it periodically.

<code>@Entity
@Table(name = "t_prop_config")
public class PropConfig {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String propKey;
    private String propValue;
}
</code>

Repository query:

<code>@Query("select propValue from PropConfig")
String findByPropKey(String propKey);
</code>

Scheduled task reads the value:

<code>@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void init() {
    String propKey = this.propConfigRepository.findByPropKey("pack.toggle.enabled");
    if (StringUtils.hasLength(propKey)) {
        this.enabled = Boolean.valueOf(propKey);
        System.err.printf("pack.toggle.enabled = %s%n", this.enabled);
    }
}
</code>

5. Using Apache Commons Configuration

Commons Configuration2 can reload a properties file periodically.

<code>@Component
public class FeatureConfigManager {
    private ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder;
    private static final String filePath = "app.properties";

    @PostConstruct
    public void init() throws Exception {
        Parameters params = new Parameters();
        ClassPathResource resource = new ClassPathResource(filePath);
        builder = new ReloadingFileBasedConfigurationBuilder<>(PropertiesConfiguration.class)
                .configure(params.fileBased().setFile(resource.getFile()));
        PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(
                builder.getReloadingController(), null, 5, TimeUnit.SECONDS);
        trigger.start();
    }

    public boolean isFeatureEnabled(String key) {
        try {
            return builder.getConfiguration().getBoolean(key, false);
        } catch (ConfigurationException e) {
            return false;
        }
    }
}
</code>

Expose via a REST controller:

<code>@RestController
@RequestMapping("/config")
public class ConfigController {
    private final FeatureConfigManager config;
    public ConfigController(FeatureConfigManager config) {
        this.config = config;
    }

    @GetMapping("")
    public boolean get() {
        return this.config.isFeatureEnabled("pack.toggle.enabled");
    }
}
</code>

6. Using Togglz Library

Togglz provides a ready‑made feature‑toggle implementation. Refer to the linked article for detailed usage.

JavaBackend DevelopmentConfigurationfeature-toggleSpring Bootdynamic refresh
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login 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.