skip to content

When would you write a custom @Conditional Condition versus using @Bean logic, @Profile, or a factory, and what are the practical limitations of @Conditional?

level: middleimportance: nice to knowfreq 25%

answer

  1. Structural presence at startup -> @Conditional; runtime value -> in-method/factory
  2. Profiles -> @Profile (sibling), not a hand-rolled condition
  3. One-time, static; false = definition removed -> tolerate absence
  4. AND-only; compose OR/NOT inside one condition
  5. Debug via Boot ConditionEvaluationReport (--debug)

basics

~20 s

Use @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
java
@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

for a junior

Say @Conditional decides at startup whether a bean exists; runtime choices go inside the bean/factory.

for a middle

Contrast with @Profile, in-method logic, ObjectProvider, and list the AND-only / static / removed-definition limitations.

for a senior

Add ordering sensitivity, ConfigurationCondition, and using the condition-evaluation report to debug back-off.

for a principal

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

context