Spring Boot Auto-Configuration Reference¶
Spring Boot auto-configuration for FlagZen feature flag injection.
Module: flagzen-spring
Overview¶
The Spring Boot starter automatically:
- Creates a
FeatureDispatcherbean from the availableFlagProvider - Discovers all
@Featureinterfaces viaServiceLoader<FeatureMetadata> - Registers a Spring bean for each feature interface
- Enables
@Autowiredinjection of feature proxies into Spring beans
Installation¶
Requires Spring Boot 2.7+ and Java 17+.
Auto-Configuration Class¶
@AutoConfiguration
@ConditionalOnClass(FeatureDispatcher.class)
@Import(FeatureProxyRegistrar.class)
public class FlagZenAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public FeatureDispatcher featureDispatcher(
ObjectProvider<FlagProvider> flagProvider) {
FlagProvider provider = flagProvider.getIfAvailable(() ->
new InMemoryFlagProvider());
return new DefaultFeatureDispatcher(provider);
}
}
Beans Created¶
FeatureDispatcher¶
A singleton FeatureDispatcher bean that resolves feature proxies.
- Type:
com.flagzen.FeatureDispatcher - Scope: Singleton
- Lazy Init: No (created eagerly during auto-configuration)
- Condition:
@ConditionalOnMissingBean— if you register your ownFeatureDispatcherbean, the auto-configuration bean is skipped
Feature Proxy Beans¶
For each @Feature interface discovered, a Spring bean is registered with:
- Type: The feature interface class
- Scope: Singleton
- Lazy Init: Yes (created on first injection)
- Name: Decapitalized interface name (e.g.,
CheckoutFlow→checkoutFlow) - Instance: Resolved via
FeatureDispatcher.resolve(featureType)on first access
FlagProvider Detection¶
The auto-configuration looks for a FlagProvider bean in the Spring context:
- If a
FlagProviderbean is found: use it to createFeatureDispatcher - If no
FlagProvideris found: useInMemoryFlagProvider(all flags return empty)
Explicit FlagProvider¶
Register a FlagProvider bean to use a specific flag source:
@Configuration
public class FlagConfig {
@Bean
public FlagProvider flagProvider() {
return new EnvironmentVariableFlagProvider.builder()
.parser(FlagKeyParsers.screamingSnakeCase("FLAGZEN_"))
.format(FlagKeyFormats.kebabCase())
.build();
}
}
The FlagZenAutoConfiguration will automatically use this provider.
Multiple Providers (ServiceLoader)¶
If multiple FlagProvider implementations are on the classpath, the first one discovered by ServiceLoader is used (order is undefined). To guarantee a specific provider:
- Register a
FlagProviderbean explicitly (as above), or - Exclude other providers from the classpath
Feature Injection¶
With auto-configuration enabled, inject feature proxies directly into Spring beans:
@Service
public class OrderService {
private final CheckoutFlow checkoutFlow;
private final DarkMode darkMode;
public OrderService(CheckoutFlow checkoutFlow, DarkMode darkMode) {
this.checkoutFlow = checkoutFlow;
this.darkMode = darkMode;
}
public void processOrder() {
String result = checkoutFlow.execute();
String theme = darkMode.theme();
// ...
}
}
Or with field injection:
@Controller
public class UIController {
@Autowired
private CheckoutFlow checkoutFlow;
@Autowired
private DarkMode darkMode;
@GetMapping("/checkout")
public String checkout() {
return checkoutFlow.execute();
}
}
Customizing the Dispatcher¶
To use a custom FeatureDispatcher implementation or configuration, register your own bean:
@Configuration
public class FlagConfig {
@Bean
public FeatureDispatcher featureDispatcher(FlagProvider provider) {
// Custom dispatcher with specialized logic
return new CustomFeatureDispatcher(provider);
}
}
The auto-configuration's @ConditionalOnMissingBean ensures your custom bean is used instead.
Conditional Beans¶
All beans are conditional on the presence of FeatureDispatcher on the classpath:
If flagzen-core is not on the classpath, auto-configuration is skipped entirely.
Disabling Auto-Configuration¶
To disable auto-configuration entirely:
Or in @SpringBootTest:
Configuration Properties¶
FlagZen auto-configuration does not currently expose configuration properties. Configuration is done via:
- Explicit
FlagProviderbean registration (recommended) - Environment variables (for
EnvironmentVariableFlagProvider) - Java system properties (if using a custom provider)
Future releases may add properties like flagzen.fallback-strategy or flagzen.context-timeout.
Spring Integration Example¶
Complete example with Spring Boot:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web:3.0.0")
implementation("com.flagzen:flagzen-spring:1.1.0")
implementation("com.flagzen:flagzen-env:1.1.0")
}
@Feature("checkout-flow")
public interface CheckoutFlow {
String execute();
}
@Variant(value = "CLASSIC", of = CheckoutFlow.class)
public class ClassicCheckout implements CheckoutFlow {
@Override
public String execute() { return "classic"; }
}
@Variant(value = "PREMIUM", of = CheckoutFlow.class)
public class PremiumCheckout implements CheckoutFlow {
@Override
public String execute() { return "premium"; }
}
@Configuration
public class FlagConfig {
@Bean
public FlagProvider flagProvider() {
return new EnvironmentVariableFlagProvider.builder()
.parser(FlagKeyParsers.screamingSnakeCase("FLAGZEN_"))
.format(FlagKeyFormats.kebabCase())
.build();
}
}
@Service
public class OrderService {
private final CheckoutFlow checkout;
public OrderService(CheckoutFlow checkout) {
this.checkout = checkout;
}
public void processOrder() {
String result = checkout.execute();
System.out.println("Using: " + result);
}
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// With env var: FLAGZEN_CHECKOUT_FLOW=PREMIUM
// OrderService will inject PremiumCheckout
SpringApplication.run(Application.class, args);
}
}
Testing with Spring Boot¶
Use @SpringBootTest with FlagZenExtension:
@SpringBootTest
@ExtendWith(FlagZenExtension.class)
class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
@PinFlag(feature = "checkout-flow", variant = "CLASSIC")
void testClassicCheckout() {
orderService.processOrder();
// Verifies behavior with CLASSIC variant
}
@Test
@PinFlag(feature = "checkout-flow", variant = "PREMIUM")
void testPremiumCheckout() {
orderService.processOrder();
// Verifies behavior with PREMIUM variant
}
}
Troubleshooting¶
"No FlagProvider bean found" Warning¶
Cause: No FlagProvider bean is registered and no provider is found via ServiceLoader.
Fix: Register a FlagProvider bean explicitly (see "Explicit FlagProvider" section above).
Feature Proxy Not Injected¶
Symptom: NoSuchBeanDefinitionException or bean not found when autowiring a feature interface.
Causes:
- Feature interface is not in the classpath (missing dependency)
- Annotation processor did not generate metadata (missing
annotationProcessordependency in build) - Auto-configuration is disabled (check
spring.autoconfigure.exclude)
Fix: Verify annotationProcessor("com.flagzen:flagzen-core:1.1.0") is in your build.gradle.
Multiple FlagProvider Beans¶
Symptom: NoUniqueBeanDefinitionException when multiple FlagProvider beans are registered.
Fix: Annotate your primary FlagProvider bean with @Primary:
@Configuration
public class FlagConfig {
@Bean
@Primary
public FlagProvider primaryProvider() { ... }
@Bean
public FlagProvider secondaryProvider() { ... }
}
Related¶
FeatureDispatcher— resolves feature proxiesFlagProviderSPI — pluggable flag sourceflagzen-env— environment variable providerFlagZenExtension— testing support