Appearance
Spring Boot @ConditionalOnProperty Annotation
The Smart Home Appliance Analogy
Imagine purchasing a luxury modern home equipped with smart appliances. In the ceiling sits an automated climate control system. The system contains circuitry for both an electric heat pump and a traditional natural gas furnace. However, during installation, the electrical inspector checks the basement utility connection. If the house has a natural gas line hooked up and the breaker switch labeled utilities.gas.enabled is switched to ON, the system registers the gas furnace bean and ignites gas heat. If the switch is OFF or no gas line is connected, the furnace bean is never loaded, preventing gas leaks and error codes.
In Spring Boot, @ConditionalOnProperty is that automated breaker switch. In modern enterprise applications, you often build features that should only exist under specific configuration conditions: a third party payment integration, a scheduled maintenance job, an internal metrics exporter, or an audit listener. Instead of instantiating beans that fail at runtime because their API keys are missing, @ConditionalOnProperty tells Spring to inspect application.properties during startup and conditionally register the bean only if a specified property matches your criteria.
This lecture covers the mechanics of @ConditionalOnProperty, its core attributes (name, havingValue, matchIfMissing), building dynamic feature flags, and handling fallback bean registration.
Why Conditional Bean Registration Matters
Without conditional configuration, applications must handle missing dependencies through complex branching logic inside methods:
java
// Anti-pattern: The bean is always instantiated even when feature is disabled
@Service
public class SmsNotificationService {
@Value("${notification.sms.enabled:false}")
private boolean enabled;
public void sendSms(String phone, String message) {
if (!enabled) {
// Waste of memory and initialization overhead
return;
}
// Send SMS logic...
}
}This anti pattern has serious flaws:
- The
SmsNotificationServiceis always instantiated, consuming memory. - If its constructor requires an SMS gateway API client, that client must be instantiated, failing if credentials are absent.
- Background scheduled tasks inside the bean still trigger unless guarded by repeated checks.
With @ConditionalOnProperty, Spring never creates the bean if the property is disabled:
java
@Service
@ConditionalOnProperty(name = "notification.sms.enabled", havingValue = "true")
public class SmsNotificationService implements NotificationService {
// Only created if notification.sms.enabled=true
}Core Attributes of @ConditionalOnProperty
The annotation provides five attributes:
| Attribute | Type | Description |
|---|---|---|
prefix | String | Common prefix applied to property names (e.g. app.features) |
name / value | String[] | The specific property key or keys to evaluate |
havingValue | String | The string value the property must match to activate the bean |
matchIfMissing | boolean | If true, the bean is created even when the property is completely absent |
java
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
String[] value() default {};
String prefix() default "";
String[] name() default {};
String havingValue() default "";
boolean matchIfMissing() default false;
}Understanding havingValue and matchIfMissing
The combination of havingValue and matchIfMissing dictates exactly how Spring evaluates condition logic:
Case 1: Standard Feature Flag (havingValue = "true", matchIfMissing = false)
java
@Bean
@ConditionalOnProperty(
prefix = "feature.export",
name = "pdf-enabled",
havingValue = "true",
matchIfMissing = false // default
)
public PdfExportService pdfExportService() {
return new PdfExportService();
}Behavior:
- Property is
feature.export.pdf-enabled=true-> Bean Loaded - Property is
feature.export.pdf-enabled=false-> Bean Skipped - Property is absent from configuration file -> Bean Skipped
Case 2: Opt Out Feature (matchIfMissing = true)
You want a feature to be active by default unless the developer explicitly turns it off:
java
@Bean
@ConditionalOnProperty(
prefix = "app.cache",
name = "enabled",
havingValue = "true",
matchIfMissing = true // Active by default!
)
public CacheManager cacheManager() {
return new RedisCacheManager();
}Behavior:
- Property is omitted entirely -> Bean Loaded (because
matchIfMissing=true) - Property is
app.cache.enabled=true-> Bean Loaded - Property is
app.cache.enabled=false-> Bean Skipped
Case 3: Boolean Truthiness Without havingValue
If you omit havingValue, Spring checks whether the property is present and not equal to "false":
java
@Bean
@ConditionalOnProperty(name = "analytics.tracker.enabled")
public Tracker analyticsTracker() {
return new AnalyticsTracker();
}Behavior:
- Property exists and is anything other than
"false"-> Bean Loaded - Property is
analytics.tracker.enabled=false-> Bean Skipped - Property is absent -> Bean Skipped
Pattern: Primary and Fallback Beans
You can combine @ConditionalOnProperty with fallback beans so your application always has an active implementation:
java
package com.example.orderservice.service;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
public interface FileStorageService {
void store(String filename, byte[] data);
}
// Production AWS S3 Storage: active only when storage.type=s3
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "s3")
public class S3FileStorageService implements FileStorageService {
public S3FileStorageService() {
System.out.println("Initialized AWS S3 Storage Client");
}
@Override
public void store(String filename, byte[] data) {
System.out.println("Uploading " + filename + " to Amazon S3 Bucket");
}
}
// Local Filesystem Storage: active only when storage.type=local (or default)
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "local", matchIfMissing = true)
public class LocalFileStorageService implements FileStorageService {
public LocalFileStorageService() {
System.out.println("Initialized Local Disk Storage Directory");
}
@Override
public void store(String filename, byte[] data) {
System.out.println("Writing " + filename + " to local /tmp directory");
}
}In application.properties:
- If you set
storage.type=s3, onlyS3FileStorageServiceis registered. - If you set
storage.type=localor omit the property, onlyLocalFileStorageServiceis registered. - There is zero bean collision, and downstream services can safely inject
FileStorageServicewith zero qualifiers.
Interview Questions & Pitfalls
Q1: What is the primary purpose of @ConditionalOnProperty in Spring Boot?
@ConditionalOnProperty enables conditional bean registration. It instructs Spring to inspect configuration properties at startup and instantiate a bean only if the specified property exists, matches a designated havingValue, or satisfies matchIfMissing rules.
Q2: What happens if matchIfMissing is set to true and the property is absent?
Spring treats the missing property as a condition match and instantiates the bean. This is the standard pattern for creating opt out features that should remain active by default unless explicitly disabled by setting the property to false.
Q3: Can @ConditionalOnProperty be applied to both classes and @Bean methods?
Yes. Placing it on a @Service or @Component class controls whether the entire class is registered. Placing it on a @Bean method inside a @Configuration class controls whether that specific bean definition method executes.
Q4: How does relaxed binding apply to property names in @ConditionalOnProperty?
@ConditionalOnProperty supports relaxed binding. Writing name = "max-threads" matches max-threads, maxThreads, max_threads, and environment variable MAX_THREADS.
Q5: What is the difference between @Profile and @ConditionalOnProperty?
@Profile is a broad, environment level coarse switch (e.g. dev, prod) that activates entire groups of files and beans. @ConditionalOnProperty is a granular, property specific feature flag that inspects specific key value configurations regardless of which overall environment profile is active.