This error means Spring couldn’t decide which implementation of an interface to inject because you have multiple candidates and didn’t specify a preference.

Here’s why this happens and how to fix it:

  1. Multiple Implementations of an Interface: You’ve likely defined two or more classes that implement the same interface, and Spring is trying to inject an instance of that interface into another component.

    • Diagnosis: Run mvn dependency:tree or gradle dependencies to see your project’s dependency structure. Look for multiple classes implementing the same interface.
    • Fix: This isn’t a "fix" in itself, but the reason for the error. You need to tell Spring which one to use.
  2. Missing @Primary Annotation: When Spring finds multiple beans of the same type, it needs a hint. The @Primary annotation is the most common way to give Spring that hint.

    • Diagnosis: Check your bean definitions. You’ll see multiple classes implementing the same interface, none of which are marked with @Primary.
    • Fix: Add @Primary to the bean definition of the implementation you want Spring to prefer. For example, if you have MyService interface and implementations DatabaseService and CacheService, mark the one you want by default:
      @Service
      @Primary
      public class DatabaseService implements MyService {
          // ...
      }
      
    • Why it works: @Primary tells Spring, "When there’s ambiguity, pick this one first."
  3. Using @Qualifier Incorrectly: Another way to disambiguate is with @Qualifier, but it’s often misused when @Primary is more appropriate for a default.

    • Diagnosis: You might have @Autowired MyService myService; and then in another class, @Autowired @Qualifier("DatabaseService") MyService myService;. This is fine if you always want DatabaseService in that specific context. However, if you want DatabaseService to be the default everywhere unless explicitly overridden, @Primary is the correct tool.
    • Fix: If you want a default service, apply @Primary to that service’s bean definition. If you only want to specify a particular implementation in a specific injection point, use @Qualifier there.
      // In the bean definition
      @Service("databaseService") // or just @Service if no specific name needed
      public class DatabaseService implements MyService { ... }
      
      @Service
      public class CacheService implements MyService { ... }
      
      // In the class that needs injection
      @Autowired
      private MyService myService; // This would fail without @Primary
      
      // To inject the default (if DatabaseService is @Primary)
      @Autowired
      private MyService defaultService; // Injects DatabaseService
      
      // To inject a specific one when there's no @Primary, or to override @Primary
      @Autowired
      @Qualifier("cacheService") // Or @Qualifier("CacheService") depending on bean name
      private MyService specificService; // Injects CacheService
      
    • Why it works: @Qualifier allows you to target a bean by its name (either the default name derived from the class name or a name explicitly given via @Service("beanName") or @Qualifier("beanName") on the injection point).
  4. Spring Boot Auto-configuration Conflicts: Sometimes, third-party libraries or Spring Boot’s auto-configuration might introduce beans that conflict with your own, leading to multiple implementations of an interface.

    • Diagnosis: Use grep or your IDE’s search to find all classes implementing the problematic interface. Identify which ones are from your project and which are from dependencies.
    • Fix: If a dependency is providing an unwanted implementation, you can often exclude it from the dependency management or disable its auto-configuration. For example, if a DataSourceAutoConfiguration is causing issues:
      @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
      public class MyApplication {
          public static void main(String[] args) {
              SpringApplication.run(MyApplication.class, args);
          }
      }
      
    • Why it works: This prevents Spring from even creating the conflicting bean from the external library.
  5. Incorrect Configuration Properties: Certain configuration properties can implicitly enable or disable beans, leading to unexpected multiple implementations being available.

    • Diagnosis: Review your application.properties or application.yml file. Look for properties related to the interface or its implementations. For instance, if you’re dealing with multiple MessageSource beans, properties like spring.messages.basename might be relevant.
    • Fix: Adjust the configuration properties to ensure only one desired bean is activated. Often, this involves setting a specific property that targets the bean you want to use or disabling others. For example, if you have two RestTemplate configurations and want to use the one configured programmatically:
      # Ensure auto-configured RestTemplate is not preferred if you have a custom one
      spring.mvc.throw-exception-if-no-handler-found=true
      
      (Note: This is a simplified example; the exact property depends on the specific auto-configuration.)
    • Why it works: Configuration properties are Spring’s primary mechanism for controlling auto-configuration and bean behavior.
  6. Nested @ComponentScan Issues: If you have multiple @ComponentScan annotations, especially with different base packages, you might accidentally scan directories that contain duplicate implementations of an interface.

    • Diagnosis: Examine all @ComponentScan annotations in your application. Check the base packages they cover.
    • Fix: Consolidate your @ComponentScan annotations or ensure they cover distinct, non-overlapping sets of packages. If you have a main application class with @SpringBootApplication (which includes @ComponentScan), you usually don’t need additional @ComponentScan annotations unless you’re explicitly trying to scan outside the default hierarchy.
      @SpringBootApplication(scanBasePackages = {"com.example.myapp.controllers", "com.example.myapp.services"})
      public class MyApplication { ... }
      
    • Why it works: This ensures Spring only discovers the intended set of beans, preventing duplicate registrations.
  7. Spring Profiles: If different Spring profiles activate different implementations of the same interface, and you run the application without a profile or with multiple conflicting profiles, you might end up with ambiguity.

    • Diagnosis: Check your profile-specific configuration files (e.g., application-dev.properties, application-prod.properties) for bean definitions or configurations that create implementations of the problematic interface.
    • Fix: Ensure that for any given active profile, only one implementation of the interface is registered. This might involve using @ConditionalOnProperty or @Profile annotations carefully, or providing a default implementation with @Primary that is overridden by profile-specific beans.
      @Configuration
      public class ServiceConfig {
          @Bean
          @Primary
          public MyService defaultService() {
              return new DefaultServiceImpl();
          }
      
          @Bean
          @Profile("prod")
          public MyService prodService() {
              return new ProdServiceImpl();
          }
      }
      
    • Why it works: @Profile ensures that a bean is only created when the specified profile is active, controlling which implementation is available at runtime.

After applying @Primary to your desired bean, you might encounter a NoSuchBeanDefinitionException if you try to inject a bean that no longer has a default and hasn’t been explicitly qualified.

Want structured learning?

Take the full Spring-boot course →