Skip to content
ExponaBLOG
JAVA, SPRING FRAMEWORK & SPRING BOOT · DAY 093 / 100

Fixing 'No qualifying bean of type' in Spring explained

The most common Spring startup error, explained.

Expona Admin7 min read

Opening

Spring's dependency injection is powerful, but when it fails, the error message can feel cryptic. You're trying to start your application, and you hit "No qualifying bean of type" — a RuntimeException that stops everything cold. Fixing 'No qualifying bean of type' in Spring explained will walk you through the four most common causes we encounter in production codebases, along with how to diagnose and resolve each one quickly.

Understanding the error

Before we dig into solutions, let's understand what this error actually means. When Spring boots up, it scans your classpath for classes annotated with stereotype annotations like @Component, @Service, @Repository, or @Configuration. It then builds a registry of beans. Later, when a class needs a dependency injected, Spring looks up that bean by type. If Spring can't find exactly one matching bean, it throws NoSuchBeanDefinitionException with the message "No qualifying bean of type."

This error has four main causes. Let's examine each one with real-world scenarios you've probably encountered.

The class is outside the component scan package

This is the most common culprit. Spring doesn't automatically scan your entire codebase. By default, it only scans classes within the package of your main application class and its subpackages.

Imagine your project structure looks like this:

java
com/example/app/Application.java
com/example/app/service/UserService.java
com/external/ThirdPartyService.java

Your Application class is in com.example.app. Spring will scan com.example.app and find UserService. But ThirdPartyService in com.external won't be scanned. If another bean tries to inject ThirdPartyService, you'll see "No qualifying bean of type."

The fix is to explicitly configure component scanning in your main @SpringBootApplication class:

java
@SpringBootApplication(scanBasePackages = {
    "com.example.app",
    "com.external"
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Or use @ComponentScan for finer control:

java
@SpringBootApplication
@ComponentScan(basePackages = {
    "com.example.app",
    "com.external"
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

In our experience, this happens most often when integrating external libraries or when your team restructures packages without updating the scanning configuration. Always verify that the package containing your service is within the scan path.

Missing @Service or @Component annotation

A class exists in the right package, but someone forgot to add a stereotype annotation. The class is never registered as a bean, so Spring has nothing to inject.

Here's a typical scenario:

java
// This class is NOT annotated
public class PaymentProcessor {
    public void process(Order order) {
        // logic here
    }
}

Then in another service:

java
@Service
public class OrderService {
    @Autowired
    private PaymentProcessor processor;  // Spring can't find this bean
    
    public void createOrder(Order order) {
        processor.process(order);
    }
}

Spring has no bean for PaymentProcessor, so injection fails at startup.

The fix is straightforward — add the appropriate annotation:

java
@Service
public class PaymentProcessor {
    public void process(Order order) {
        // logic here
    }
}

Choose the right stereotype for your use case:

  • @Component — generic bean, use when the others don't apply
  • @Service — business logic layer
  • @Repository — data access layer
  • @Controller or @RestController — web layer

We've seen this happen frequently when someone writes a utility class or helper service without thinking about dependency injection. Always ask: "Will this need to be injected or managed by Spring?" If yes, annotate it.

Two beans of the same type — add @Primary or @Qualifier

You have multiple implementations of the same interface, and Spring doesn't know which one to inject.

Consider this scenario:

java
public interface NotificationService {
    void send(String message);
}

@Service
public class EmailNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        // send email
    }
}

@Service
public class SlackNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        // send Slack message
    }
}

Now when another service tries to inject NotificationService:

java
@Service
public class AlertService {
    @Autowired
    private NotificationService notificationService;  // Which one?
}

Spring finds two beans and throws "No qualifying bean of type" (or more accurately, "expected single matching bean but found 2").

You have two solutions. First, use @Primary to mark the default implementation:

java
@Service
@Primary
public class EmailNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        // send email
    }
}

Now Spring knows to inject EmailNotificationService when the type is ambiguous. But if you need specific beans in different places, use @Qualifier instead:

java
@Service("email")
public class EmailNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        // send email
    }
}

@Service("slack")
public class SlackNotificationService implements NotificationService {
    @Override
    public void send(String message) {
        // send Slack message
    }
}

Then inject by qualifier name:

java
@Service
public class AlertService {
    @Autowired
    @Qualifier("slack")
    private NotificationService slackNotifier;
    
    @Autowired
    @Qualifier("email")
    private NotificationService emailNotifier;
}

In production, we typically use @Qualifier for explicit control and @Primary only when one implementation is genuinely the default across the entire application. This makes dependency graphs clearer for your team.

A conditional configuration did not activate

You defined a bean in a @Configuration class, but the condition preventing its creation wasn't obvious.

Here's a real-world example from a multi-tenant SaaS application:

java
@Configuration
@ConditionalOnProperty(name = "features.analytics.enabled", havingValue = "true")
public class AnalyticsConfiguration {
    @Bean
    public AnalyticsService analyticsService() {
        return new AnalyticsService();
    }
}

Your application properties file has:

java
features.analytics.enabled=false

Now when another service tries to inject AnalyticsService:

java
@Service
public class ReportService {
    @Autowired
    private AnalyticsService analyticsService;  // Bean not created, condition failed
}

The fix depends on your intent. If the bean should always be available, remove the condition:

java
@Configuration
public class AnalyticsConfiguration {
    @Bean
    public AnalyticsService analyticsService() {
        return new AnalyticsService();
    }
}

If the bean is truly optional, make the dependent bean handle the absence gracefully:

java
@Service
public class ReportService {
    @Autowired(required = false)
    private AnalyticsService analyticsService;
    
    public void generateReport() {
        if (analyticsService != null) {
            analyticsService.track("report_generated");
        }
    }
}

Alternatively, use @ConditionalOnMissingBean to provide a fallback:

java
@Configuration
public class AnalyticsConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public AnalyticsService analyticsService() {
        return new NoOpAnalyticsService();  // Fallback implementation
    }
}

We've encountered this often in feature-flagged systems where certain beans are only created in production or for specific customer tiers. Always document the conditions clearly in comments, and consider logging when conditions prevent bean creation.

Common mistakes

Mistake 1: Confusing @Autowired with manual instantiation. Don't do this:

java
@Service
public class OrderService {
    private PaymentProcessor processor = new PaymentProcessor();  // Wrong!
}

Spring doesn't manage this instance, so it won't have its own dependencies injected. Use @Autowired to let Spring handle it.

Mistake 2: Forgetting that @SpringBootTest has its own component scan. A bean missing from your main application might also be missing from your test context. Always verify your test configuration.

Mistake 3: Assuming package names are case-sensitive for scanning. They are, but only at the filesystem level. If your IDE refactors a package name incorrectly, the scan path might not match anymore.

Key takeaways

  • Component scanning scope: Spring only scans packages within your main application class by default. Use scanBasePackages or @ComponentScan to extend the search.
  • Stereotype annotations are mandatory: @Component, @Service, @Repository, and @Controller register classes as beans. Without them, Spring ignores the class.
  • Multiple implementations require disambiguation: Use @Primary for a default or @Qualifier for explicit selection. Never leave it ambiguous.
  • Conditional beans need fallbacks: If a @Configuration is conditionally applied, either remove the condition or make dependent beans handle the absence with required = false.
  • Read the full stack trace: Spring's error message usually tells you the class that couldn't be injected. Start your debugging there, then trace backward to the missing bean.
  • Test early: These errors show up at startup, not runtime. A quick smoke test or integration test catches them immediately during development.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles