RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Spring Boot questions
TrickySpring Boot#196 min readJul 11, 2026

Difference between @Primary and @Qualifier.

practice
learning
Practice modeTest yourself instead of reading straight through

Interviewers love this one because it tests whether you understand how Spring chooses a bean when there are several candidates, not just whether you can remember annotation names.

Question: Difference between @Primary and @Qualifier.

Answer: Both annotations help Spring choose the right bean for dependency injection when more than one bean has the same type. @Primary marks one bean as the default choice, while @Qualifier says exactly which bean I want at a specific injection point. In simple words: @Primary is the default winner, and @Qualifier is the exact name tag.

Interview-Ready Answer: I use @Primary when I want one bean to be the default candidate for a type, so Spring picks it if nothing else is specified. I use @Qualifier when I want to choose a specific bean at a specific injection point, usually by bean name. The key difference is that @Primary is a global default for that type, while @Qualifier is an explicit local override. If both apply, the qualifier is more specific and wins.

🧠 Memory Map
Memory map — visual summary of this topic

Detailed Explanation: In Spring Boot, both annotations solve the same basic problem: which bean should be injected when more than one bean matches the type? The difference is where you give the instruction. @Primary marks the default bean at the bean-definition level, while @Qualifier marks the exact bean at the injection-point level.

What each annotation means

  • @Primary means: if Spring has to choose, prefer this bean.
  • @Qualifier means: I want this exact bean here.
  • A qualifier can be a bean name, such as smsNotificationSender, or a custom qualifier annotation in more advanced setups.
Aspect@Primary@Qualifier
MeaningDefault beanSpecific bean
Where appliedBean definitionInjection point
Typical useFallback choiceExact choice
PriorityLower than qualifierOverrides primary
Value styleMarker onlyName or tag

How Spring resolves the dependency

  1. Spring scans the application context and finds all beans that match the requested type.
  2. If only one candidate exists, it injects that bean immediately.
  3. If multiple candidates exist, Spring looks at the injection point for a qualifier.
  4. If a qualifier is present, Spring filters the candidates until it finds the matching bean name or qualifier metadata.
  5. If no qualifier is present, Spring checks whether exactly one candidate is marked @Primary.
  6. If there is still no unique answer, Spring fails fast with NoUniqueBeanDefinitionException.

When to use each one

  • Use @Primary when one implementation is the common default and most services should get it without extra noise.
  • Use @Qualifier when different services need different implementations, or when the choice depends on business meaning.
  • Use both together when one bean is the default, but a few places need a special bean.

Important edge cases

  • @Primary does not pick a bean for collection injections like List<PaymentGateway>; Spring injects all matching beans there.
  • If two beans are marked @Primary for the same type, Spring cannot use one as a unique default and startup can fail with ambiguity.
  • If the qualifier name is wrong, the application usually fails at startup rather than silently using another bean. That is good, because it catches mistakes early.
  • @Primary is stable across modern Spring Framework and Spring Boot 2.x and 3.x. Boot creates more beans through auto-configuration, so these rules matter even more, but the resolution logic stays the same.

Performance note: Bean selection is very fast. In practice, Spring only compares the small set of candidates for one type, often 2 to 5 beans in a real application. So the complexity is roughly O(n) for that type’s candidates, but the constant cost is tiny and happens at startup or injection time, not on every business method call.

Memory rule: Do not use @Primary to hide a bad design. If two beans represent truly different business choices, a qualifier is clearer because it documents the intent right where the choice matters.

Real-World Example: Imagine a checkout service in an e-commerce system with two payment implementations: an internal gateway for store credit and an external gateway for cards. The team marks the internal gateway as @Primary so most services get the default path, but the card-payment flow uses @Qualifier to request the external gateway explicitly.

What goes wrong when someone misunderstands this? A developer injects PaymentGateway by type into a refund service and assumes Spring will pick the card provider. Because the internal gateway is primary, refunds are sent to the wrong system, and the logs show the wrong class name being used. Users see failed refunds, support sees a spike in tickets, and the fix is usually either adding the right qualifier or removing the accidental primary from the wrong bean.

That is why this topic matters in production: the error is not a syntax mistake, it is a routing mistake.

Spring Boot
package com.example.demo;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@SpringBootApplication
public class PrimaryQualifierApplication implements CommandLineRunner {

    private final AlertService alertService;
    private final ApplicationContext applicationContext;

    public PrimaryQualifierApplication(AlertService alertService, ApplicationContext applicationContext) {
        this.alertService = alertService;
        this.applicationContext = applicationContext;
    }

    public static void main(String[] args) {
        SpringApplication.run(PrimaryQualifierApplication.class, args);
    }

    @Override
    public void run(String... args) {
        System.out.println(alertService.sendDefaultAlert("Database latency is high"));
        System.out.println(alertService.sendSmsAlert("Database latency is high"));

        // Edge case: asking for a bean name that does not exist fails fast.
        // This is the sort of mistake @Qualifier prevents when there are many candidates.
        try {
            applicationContext.getBean("pushNotificationSender");
        } catch (NoSuchBeanDefinitionException ex) {
            System.out.println("Lookup failed: " + ex.getClass().getSimpleName() + " - " + ex.getMessage());
        }

        // If @Primary were removed from EmailNotificationSender, the constructor injection
        // for defaultSender would become ambiguous and Spring would fail at startup.
    }
}

interface NotificationSender {
    String send(String message);
}

@Service
@Primary
class EmailNotificationSender implements NotificationSender {
    @Override
    public String send(String message) {
        return "Email sent: " + message;
    }
}

@Service("smsNotificationSender")
class SmsNotificationSender implements NotificationSender {
    @Override
    public String send(String message) {
        return "SMS sent: " + message;
    }
}

@Service
class AlertService {

    private final NotificationSender defaultSender;
    private final NotificationSender smsSender;

    AlertService(NotificationSender defaultSender,
                 @Qualifier("smsNotificationSender") NotificationSender smsSender) {
        this.defaultSender = defaultSender;
        this.smsSender = smsSender;
    }

    String sendDefaultAlert(String message) {
        // No qualifier here: Spring picks the @Primary bean for this type.
        return defaultSender.send(message);
    }

    String sendSmsAlert(String message) {
        // @Qualifier narrows the choice to the bean with this exact name.
        return smsSender.send(message);
    }
}

Follow-up & Tricky Questions:

Follow-up questions you may get next

  • When should I prefer @Primary? Use it when one implementation is the common default and most injections should work without extra annotations. It keeps constructors clean and reduces repetition.
  • When should I prefer @Qualifier? Use it when different injection points need different implementations, or when the choice has business meaning. It makes the dependency explicit at the exact place it is used.
  • Can @Qualifier and @Primary be used together? Yes. A primary bean gives the default, but a qualifier at the injection point can still select a different bean. The qualifier is the more specific rule.
  • What happens with List or Map injection? Spring injects all matching beans, so @Primary does not filter the collection. If you need only one bean, inject a single bean or use a qualifier.
  • Can I use bean names as qualifiers? Yes. The simplest form of @Qualifier is the bean name, and that is why naming beans clearly matters.

Tricky / gotcha questions

  • If two beans are marked @Primary, what happens? Spring no longer has one clear default, so dependency resolution becomes ambiguous and startup can fail. Primary only helps when it identifies a single winner.
  • Does @Qualifier create a bean? No. It does not register anything; it only tells Spring which existing bean to inject.
  • Is a constructor parameter name the same as @Qualifier? No. Parameter-name matching can sometimes help Spring, but it is not as explicit or as reliable as using @Qualifier. For interviews and production code, prefer the annotation when the choice matters.

Common Mistakes:

  • Mistake: Thinking @Primary and @Qualifier are interchangeable. Correction: @Primary is the default fallback, while @Qualifier is the exact choice at a specific injection point.
  • Mistake: Marking every bean as primary. Correction: Only one bean should usually be the default for a given type, otherwise you lose the point of having a default.
  • Mistake: Expecting @Primary to choose one bean from a list injection. Correction: Collections get all matching beans, so primary is ignored there.
  • Mistake: Using vague bean names like service1 and service2. Correction: Good names make qualifiers readable and make code self-explanatory.

Memory Hook: Primary is the default taxi at the rank; Qualifier is the taxi number you booked. Default choice versus exact reservation.

Cheat Sheet:

  • @Primary = default bean for a type.
  • @Qualifier = exact bean at the injection point.
  • Qualifier beats primary when both are present.
  • Primary does not filter List or Map injections.
  • Wrong qualifier or multiple primaries can cause startup failure.
  • Use primary for common defaults, qualifier for special cases.

Practice Tasks:

  • Add a third NotificationSender bean and inject it with a new qualifier.
  • Remove @Primary and observe the ambiguity error at startup, then add it back.
  • Change the bean name used in @Qualifier to a wrong value and see how Spring fails fast.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

package com.example.demo; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; @SpringBootApplication public class PrimaryQualifierApplication implements CommandLineRunner { private final AlertService alertService; private final ApplicationContext applicationContext; public PrimaryQualifierApplication(AlertService alertService, ApplicationContext applicationContext) { this.alertService = alertService; this.applicationContext = applicationContext; } public static void main(String[] args) { SpringApplication.run(PrimaryQualifierApplication.class, args); } @Override public void run(String... args) { System.out.println(alertService.sendDefaultAlert("Database latency is high")); System.out.println(alertService.sendSmsAlert("Database latency is high")); // Edge case: asking for a bean name that does not exist fails fast. // This is the sort of mistake @Qualifier prevents when there are many candidates. try { applicationContext.getBean("pushNotificationSender"); } catch (NoSuchBeanDefinitionException ex) { System.out.println("Lookup failed: " + ex.getClass().getSimpleName() + " - " + ex.getMessage()); } // If @Primary were removed from EmailNotificationSender, the constructor injection // for defaultSender would become ambiguous and Spring would fail at startup. } } interface NotificationSender { String send(String message); } @Service @Primary class EmailNotificationSender implements NotificationSender { @Override public String send(String message) { return "Email sent: " + message; } } @Service("smsNotificationSender") class SmsNotificationSender implements NotificationSender { @Override public String send(String message) { return "SMS sent: " + message; } } @Service class AlertService { private final NotificationSender defaultSender; private final NotificationSender smsSender; AlertService(NotificationSender defaultSender, @Qualifier("smsNotificationSender") NotificationSender smsSender) { this.defaultSender = defaultSender; this.smsSender = smsSender; } String sendDefaultAlert(String message) { // No qualifier here: Spring picks the @Primary bean for this type. return defaultSender.send(message); } String sendSmsAlert(String message) { // @Qualifier narrows the choice to the bean with this exact name. return smsSender.send(message); } }