When would you write a custom @Conditional Condition versus using @Bean logic, @Profile, or a factory, and what are the practical limitations of @Conditional?
answer
- Structural presence at startup -> @Conditional; runtime value -> in-method/factory
- Profiles -> @Profile (sibling), not a hand-rolled condition
- One-time, static; false = definition removed -> tolerate absence
- AND-only; compose OR/NOT inside one condition
- Debug via Boot ConditionEvaluationReport (--debug)
basics
~20 sUse @Conditional when whether a bean definition exists at all should depend on the environment, classpath, or other beans — decided at startup. If you just need to choose an implementation at runtime, put logic inside the @Bean method or use a factory instead.
solid answer
~40 s@Conditional operates at bean-definition time: it decides whether a definition is registered, based on classpath, properties, resources, or existing beans. Reach for a custom Condition when you're authoring reusable/starter-style configuration where the very presence of a bean is contextual and you want clean back-off (e.g. 'provide a default only if none exists', 'only if library X is present'). Prefer in-method logic or an ObjectProvider/factory when the choice is per-runtime-value or per-request rather than structural. Prefer @Profile for environment/profile activation (a sibling mechanism). Limitations: conditions run once at startup (not dynamic), a false condition removes the definition so injections must tolerate absence, type-based conditions are order-sensitive and need ConfigurationCondition/REGISTER_BEAN, and complex boolean logic must be composed inside a condition since @Conditional AND-combines. Debugging is aided by Boot's ConditionEvaluationReport (the auto-config report).
code
java · 14 lines@Configuration
class PaymentConfig {
// Structural: whether the stub even exists depends on a property -> @Conditional
@Bean
@Conditional(OnStubModeCondition.class)
PaymentGateway stubGateway() { return new StubGateway(); }
// Value choice: bean always exists, pick impl inside the method -> no condition
@Bean
RetryPolicy retryPolicy(@Value("${payments.retries:3}") int retries) {
return retries > 0 ? new ExponentialBackoff(retries) : new NoRetry();
}
}go deeper
Say @Conditional decides at startup whether a bean exists; runtime choices go inside the bean/factory.
Contrast with @Profile, in-method logic, ObjectProvider, and list the AND-only / static / removed-definition limitations.
Add ordering sensitivity, ConfigurationCondition, and using the condition-evaluation report to debug back-off.
Discuss API design for reusable starters, composability (AnyNestedCondition), and trade-offs vs runtime feature flags.
**What @Conditional is for.** It answers a *structural* question: should this bean definition be registered? The decision is made once, at configuration/startup time, using the classpath, `Environment` properties, resources, or the presence/absence of other bean definitions. That makes it ideal for library, starter, and framework code where 'does this bean even exist?' depends on the surrounding context. **Alternatives and when they're better.** - **Logic inside the `@Bean` method / a factory bean.** If the bean always exists but its *internals* or chosen implementation depend on a runtime value, do the branching inside the method (`return flag ? new A() : new B();`) or use a `FactoryBean`. This keeps a single, always-present definition. Use this when the choice is a value, not a structural presence. - **`ObjectProvider`/`@Autowired(required=false)`.** If a dependency might be absent, injecting `ObjectProvider<Foo>` lets a consumer cope gracefully without you gating the consumer via a condition. - **`@Profile`.** For activating beans by environment/profile, use `@Profile` (a dedicated, higher-level mechanism owned by a sibling topic). It's more expressive and readable than reinventing profile logic in a custom condition. - **`@ConditionalOnProperty`, `@ConditionalOnClass`, `@ConditionalOnMissingBean` (Boot).** If you're on Boot, prefer these curated conditions over hand-rolled ones — less code, well-tested semantics, and they integrate with the condition-evaluation report. **Limitations and gotchas of @Conditional.** 1. **Static, one-time evaluation.** Conditions run at startup; they cannot flip a bean on/off at runtime. For dynamic behavior use feature-flag logic inside beans. 2. **A false condition removes the definition.** Anything expecting that type must tolerate its absence (optional injection, `ObjectProvider`, or another provider), or startup fails. 3. **AND-only combination.** `@Conditional({A,B})` requires all; there's no built-in OR/NOT — compose that inside one condition. 4. **Ordering sensitivity.** Conditions that inspect other beans see only what's registered so far; use `ConfigurationCondition` with `REGISTER_BEAN` and, for auto-config, `@AutoConfigureBefore/After`. 5. **Debuggability.** In Boot, enable the `ConditionEvaluationReport` (e.g. run with `--debug`) to see which conditions matched and why beans backed off — invaluable when 'my bean didn't get created'. **Rule of thumb.** Structural presence, decided at startup, in reusable config → custom `Condition` (or a Boot `@ConditionalOnX`). Value/implementation choice or per-request behavior → keep a single definition and branch inside it. Environment/profile activation → `@Profile`.
- You need a bean to change behavior per HTTP request. Is @Conditional the right tool?No. @Conditional decides bean presence once at startup, not per request. Keep a single bean and vary behavior at call time (method logic, a strategy chosen from request context, or request-scoped beans).
- How do you express OR/NOT logic across conditions given @Conditional is AND-only?Put the boolean logic inside a single Condition implementation (return the OR/NOT result from matches), or in Boot use AnyNestedCondition/AllNestedCondition/NoneNestedCondition helper base classes.
saying these in an interview costs you the question
- Using a custom Condition to reimplement profile activation instead of @Profile
- Expecting a condition to toggle a bean dynamically at runtime
- Gating with a false condition but still hard-@Autowiring the (now absent) bean without optional handling
- Assuming @Conditional supports OR directly across listed conditions