Hook: This is Spring Boot’s “backup singer” rule: only step on stage when no main singer is already there.
Question: Explain @ConditionalOnMissingBean.
Answer: @ConditionalOnMissingBean tells Spring Boot to create a bean only if the application context does not already contain a matching bean. It is most often used in auto-configuration to provide a safe default that users can override by defining their own bean. The check happens during startup against bean definitions, not later at request time.
Interview-Ready Answer: “I use @ConditionalOnMissingBean when I want Spring Boot to back off and create a default only if the user hasn’t already provided their own bean. It’s one of the main auto-configuration patterns: Boot checks the bean registry during startup, and if a matching bean is already present, it skips the fallback. That’s how starters give sensible defaults without blocking application-level customization.”
Detailed Explanation: Think of @ConditionalOnMissingBean as a back-off switch. A starter or auto-configuration class says, “I will create a default bean only if nobody else already registered one of the same kind.” The “same kind” is usually a type, but it can also be a specific bean name if you want tighter control.
This is why the annotation is perfect for defaults in libraries and starters. It is not a runtime if statement, and it is not checking live objects on every request. It is a startup-time decision.
DataSource-adjacent helper, mapper, client, or strategy.If the app absolutely requires a bean, do not hide the problem with a conditional default. In that case, fail fast or use @ConditionalOnBean on the consumer side so missing dependencies are obvious.
| Annotation | Purpose | Timing |
|---|---|---|
@ConditionalOnMissingBean | Create fallback only if absent | Startup registration |
@ConditionalOnBean | Create bean only if another exists | Startup registration |
@Primary | Choose preferred bean among many | Injection time |
The important difference is this: @ConditionalOnMissingBean controls whether the bean is registered, while @Primary controls which bean is selected when several already exist. A common interview trap is thinking they solve the same problem. They do not.
Memory Hook: “If the VIP is already in the room, the backup singer stays backstage.” That is the whole mental model.
Performance note: The cost is usually small and happens once at startup. It is roughly linear in the number of bean definitions Spring must inspect, so in a typical app with hundreds or even a few thousand beans, it is startup noise, not request-time overhead.
Real-World Example: A checkout service had a FraudCheckClient starter. In local dev, the starter should provide a simple no-op fallback, but production apps can replace it with a real partner implementation. One release forgot the missing-bean guard, so both the default client and the tenant-specific client were created. Pods started crash-looping with NoUniqueBeanDefinitionException, Kubernetes kept restarting them, and checkout traffic returned 503s because the app never finished booting. The fix was to make the fallback bean conditional so the tenant bean could win cleanly.
The subtle lesson: @ConditionalOnMissingBean is how library authors say, “I will be helpful by default, but I will never fight the application for control.”
package com.example.demo;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
public class ConditionalOnMissingBeanDemo {
public static void main(String[] args) {
runScenario("Scenario 1: no user bean", new Class<?>[]{DefaultGreetingAutoConfiguration.class});
runScenario("Scenario 2: user bean with @Primary", new Class<?>[]{UserGreetingConfiguration.class, DefaultGreetingAutoConfiguration.class});
runScenario("Scenario 3: user beans without @Primary", new Class<?>[]{AmbiguousUserGreetingConfiguration.class, DefaultGreetingAutoConfiguration.class});
}
private static void runScenario(String title, Class<?>[] configClasses) {
System.out.println("\n=== " + title + " ===");
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
// Register user beans first so the condition can see them.
// This mirrors the real Spring Boot idea: application beans should back off starter defaults.
for (Class<?> configClass : configClasses) {
context.register(configClass);
}
context.refresh();
GreetingService service = context.getBean(GreetingService.class);
System.out.println("Resolved bean: " + service.getClass().getSimpleName());
System.out.println(service.greet("Spring"));
System.out.println("GreetingService bean count: " + context.getBeanNamesForType(GreetingService.class).length);
// If @Primary is removed from the primary configuration below, the lookup above becomes ambiguous
// and Spring throws NoUniqueBeanDefinitionException because registration and selection are different steps.
} catch (NoUniqueBeanDefinitionException ex) {
System.out.println("Lookup failed: " + ex.getMessage());
}
}
interface GreetingService {
String greet(String name);
}
@Configuration(proxyBeanMethods = false)
static class DefaultGreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean(GreetingService.class)
GreetingService defaultGreetingService() {
// This bean is only created when no other GreetingService already exists.
return name -> "Hello, " + name + " (default fallback)";
}
}
@Configuration(proxyBeanMethods = false)
static class UserGreetingConfiguration {
@Bean
@Primary
GreetingService primaryGreetingService() {
return name -> "Hi, " + name + " (primary user bean)";
}
@Bean
GreetingService secondaryGreetingService() {
return name -> "Yo, " + name + " (secondary user bean)";
}
}
@Configuration(proxyBeanMethods = false)
static class AmbiguousUserGreetingConfiguration {
@Bean
GreetingService firstGreetingService() {
return name -> "Hey, " + name + " (first user bean)";
}
@Bean
GreetingService secondGreetingService() {
return name -> "Hola, " + name + " (second user bean)";
}
}
}Follow-up & Tricky Questions:
@ConditionalOnBean? @ConditionalOnBean creates something only when a dependency exists; @ConditionalOnMissingBean creates a fallback when it does not. They are opposites and often appear together in auto-configuration.@Bean method, it usually checks the bean method’s return type unless you specify a more exact type or name. That is why picking the right return type matters.@Primary affect the condition? No. @Primary helps Spring choose a bean during injection, but @ConditionalOnMissingBean only cares whether a matching bean exists at registration time.@Primary, will the fallback still be created? No. The fallback backs off as soon as any matching bean exists; @Primary only affects which existing bean gets injected.Common Mistakes:
@Primary. Correction: @Primary resolves ambiguity after beans exist; @ConditionalOnMissingBean prevents the fallback from being registered in the first place.Memory Hook: “VIP first, backup later.” If the application already brought the VIP bean, Spring Boot politely keeps the backup bean off the stage.
Cheat Sheet:
@ConditionalOnBean = create when present; @ConditionalOnMissingBean = create when absent.@Primary chooses a winner; it does not stop bean creation.Practice Tasks:
Clock bean that only appears when the app does not define one.@Primary, and observe the ambiguity error at injection time.