Explain @ConditionalOnProperty: its attributes (prefix, name, havingValue, matchIfMissing) and exactly when it matches.
answer
- prefix + name -> property key
- havingValue empty = match unless 'false'
- matchIfMissing default FALSE
- multiple names = AND
- evaluated at refresh, not runtime-reactive
basics
~20 s@ConditionalOnProperty enables config based on a configuration property. It matches when the property (prefix + name) equals havingValue. matchIfMissing=true makes it match even when the property is absent, so a feature can be on by default.
solid answer
~40 s@ConditionalOnProperty gates a bean/config on Environment property values. Key attributes: prefix + name (or just name) identify the property; havingValue is the required value (default empty means 'match any value that isn't false'); matchIfMissing controls behavior when the property is unset (default false = no match). So a common 'enabled by default' toggle is @ConditionalOnProperty(name="myfeature.enabled", havingValue="true", matchIfMissing=true) — on unless explicitly set to false. Values come from any PropertySource (application.yml, env vars, CLI). It's evaluated at context refresh, so it can't react to properties that change at runtime. With multiple names, all must match. This is the go-to switch for opt-in/opt-out features and is heavily used inside Boot's own auto-configuration.
code
java · 18 lines@Configuration(proxyBeanMethods = false)
public class FeatureToggleConfig {
// Enabled by default; disabled only with myapp.audit.enabled=false
@Bean
@ConditionalOnProperty(prefix = "myapp.audit", name = "enabled",
havingValue = "true", matchIfMissing = true)
AuditService auditService() {
return new AuditService();
}
// Strategy selection: only when myapp.store=redis
@Bean
@ConditionalOnProperty(name = "myapp.store", havingValue = "redis")
Store redisStore() {
return new RedisStore();
}
}go deeper
Know it's a property-based on/off switch and that matchIfMissing enables defaults.
Nail the exact match rules for havingValue empty/set and matchIfMissing.
Explain refresh-time evaluation, AND-of-names, and relaxed binding from all PropertySources.
Contrast with @ConditionalOnExpression and discuss why it can't react to @RefreshScope property changes.
## Purpose `@ConditionalOnProperty` (package `org.springframework.boot.autoconfigure.condition`) registers a bean or `@Configuration` only when one or more **configuration properties** have expected values. It's the standard way to build feature flags and opt-in/opt-out behavior. ## Attributes - **`prefix`** — optional common prefix, e.g. `spring.datasource`. Concatenated with each `name` (a `.` is inserted). - **`name`** (alias `value`) — one or more property keys. Multiple names are **AND**-ed: every listed property must match. - **`havingValue`** — the required value. If **empty (the default)**, the condition matches when the property is present and **not equal to `false`** (case-insensitive). If set (e.g. `"true"`, `"redis"`), the property's string value must equal it (case-insensitively). - **`matchIfMissing`** — what to do when the property is **absent**. Default `false` (absent ⇒ no match). Set `true` to match when unset — the pattern for 'enabled unless turned off'. ## Match logic, precisely For each named property: 1. If the property is **absent** ⇒ result is `matchIfMissing`. 2. If present and `havingValue` is empty ⇒ matches unless the value equals `false`. 3. If present and `havingValue` is set ⇒ matches iff `value.equalsIgnoreCase(havingValue)`. The overall condition matches only if **all** listed names match. ## Where values come from Any `PropertySource` in the `Environment`: `application.properties`/`application.yml`, OS environment variables (relaxed binding — `MYFEATURE_ENABLED` maps to `myfeature.enabled`), `--myfeature.enabled=true` CLI args, `@TestPropertySource`, Spring Cloud Config, etc. ## Common patterns ```java // Opt-OUT: on by default, off only if explicitly false @ConditionalOnProperty(prefix = "myapp.cache", name = "enabled", havingValue = "true", matchIfMissing = true) // Opt-IN: off by default, on only when explicitly true @ConditionalOnProperty(prefix = "myapp.beta", name = "enabled", havingValue = "true") // matchIfMissing defaults false // Strategy selection @ConditionalOnProperty(name = "myapp.store", havingValue = "redis") ``` ## Gotchas - **Evaluated once, at refresh.** Changing the property at runtime (or via `@RefreshScope`) does **not** re-evaluate the condition — the bean set is fixed after startup. - **`havingValue` empty ≠ 'any non-empty'.** Empty `havingValue` still excludes the literal value `false`; that's a deliberate 'presence means on unless explicitly false' semantics. - **matchIfMissing default is false.** Forgetting it means your bean silently disappears when the property isn't set — a frequent 'why is my bean missing?' bug. - **Multiple names are AND.** There is no OR form; for OR you need separate conditions or a custom `Condition`. - **Relaxed binding applies**, so env-var casing/underscores still work. - **Not for secrets or complex logic** — for expressions use `@ConditionalOnExpression`. ## When to use Feature toggles, choosing between implementations by config, and gating optional infrastructure. It's exactly how Boot exposes switches like `spring.jpa.open-in-view` guards and many `*.enabled` flags.
- A bean guarded by @ConditionalOnProperty(name="x.enabled") disappears when x.enabled is unset. Why, and how do you make it default-on?matchIfMissing defaults to false, so an absent property means no match. Add matchIfMissing = true (typically with havingValue="true") to enable it by default and require an explicit false to turn it off.
- If havingValue is left empty, what values cause a match?Any present value except the literal false (case-insensitive). Empty havingValue means 'property is present and not false'; if absent, the result falls back to matchIfMissing.
saying these in an interview costs you the question
- Thinking matchIfMissing defaults to true
- Believing the condition re-evaluates when the property changes at runtime
- Assuming multiple names are OR-ed
- Saying empty havingValue matches only non-empty strings (it excludes 'false' specifically)