Interviewers love this question because one wrong package or missing annotation can make a bean silently disappear.
Question: How does Component Scanning work?
Answer: Component scanning is Spring Boot’s way of finding classes in your codebase that should become Spring beans. It starts from the package of your main application class, looks for stereotypes like @Component, @Service, @Repository, and @Controller, and registers those classes in the application context. After that, Spring can inject them into each other by constructor or field injection.
Interview-Ready Answer: In Spring Boot, component scanning starts from the package of the class annotated with @SpringBootApplication, because that annotation includes @ComponentScan. Spring walks the classpath under that package, finds stereotype annotations like @Component, turns those classes into bean definitions, and then creates and wires them during context startup. A useful detail is that anything outside the base package is ignored unless I expand the scan with scanBasePackages or scanBasePackageClasses.
@SpringBootApplication is a meta-annotation, which means it is an annotation made from other annotations. One of those is @ComponentScan. That is why Boot can find your beans without you writing scan configuration in most apps. By default, Spring uses the package of your main class as the root and scans that package plus all subpackages.
@Component, @Service, @Repository, @Controller, and @Configuration.NoSuchBeanDefinitionException.Use component scanning when classes belong to the same application and you want low-boilerplate wiring. It is ideal for services, repositories, controllers, helpers, and configuration classes that naturally live under one root package. It is less ideal for third-party objects or classes that need custom construction logic, because those are usually better registered with @Bean methods.
@Bean| Aspect | Component scanning | @Bean method |
|---|---|---|
| Discovery | Automatic | Explicit |
| Best for | Your app classes | Third-party or custom objects |
| Control | Less direct | Very direct |
| Startup cost | Classpath scan at startup | No package scan for that bean |
Scanning is a startup-time cost, not a per-request cost. In small apps it is usually tiny, but in large monoliths with thousands of classes it can add noticeable startup time, often hundreds of milliseconds or more. Think of it as roughly linear work over the classes under the scanned packages, so a larger package tree usually means more startup work. Spring can optimize this in some builds with a generated component index, but the mental model is still: find candidates once, then wire them.
@Qualifier or @Primary.@Repository is treated like a component, but it also helps with persistence exception translation.Best practice: place your main application class in a top-level package like com.example, so all feature packages sit underneath it. If you must scan elsewhere, use @SpringBootApplication(scanBasePackages = ...) or scanBasePackageClasses for a type-safe option.
Imagine a checkout service for an e-commerce app. The team moves PaymentGatewayClient into a shared package so other services can reuse it, but they forget that the Spring Boot main class still scans only the old root package. The app starts, then fails while creating the checkout controller because the payment client bean cannot be found.
What goes wrong: startup logs show a constructor injection failure such as Parameter 0 of constructor ... required a bean of type ... or No qualifying bean of type ... available. Health checks never go green, pods restart, and users see checkout timeouts or 500 errors. The bug is not in business logic; it is a package layout problem that prevented component scanning from seeing the bean.
The fix is usually simple: move the main application class to a higher root package, expand the scan base package, or register the object explicitly with @Bean. That is why scanning is a packaging rule as much as it is an IoC feature.
package com.example.demo;
import java.util.Locale;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@SpringBootApplication
public class ComponentScanningDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ComponentScanningDemoApplication.class, args);
}
@Bean
LegacyTaxCalculator legacyTaxCalculator() {
// This bean is registered explicitly, not by scanning.
// Interview point: component scanning finds annotated classes;
// @Bean is how you add objects that are plain classes or third-party types.
return new LegacyTaxCalculator();
}
@Bean
ApplicationRunner demoRunner(CheckoutService checkoutService,
LegacyTaxCalculator legacyTaxCalculator,
ApplicationContext context) {
return args -> {
System.out.println(checkoutService.checkout("BOOK-123", 40.00));
System.out.println("Legacy tax on 100.00 = " + String.format(Locale.US, "%.2f", legacyTaxCalculator.applyTax(100.00)));
// Edge case: this class exists on the classpath, but it is not a bean.
// Spring ignores it because there is no stereotype annotation and no @Bean method.
try {
context.getBean(PlainHelper.class);
} catch (NoSuchBeanDefinitionException ex) {
System.out.println("Expected failure: " + ex.getClass().getSimpleName() + " -> " + ex.getMessage());
}
};
}
}
@Service
class CheckoutService {
private final PricingService pricingService;
private final InventoryRepository inventoryRepository;
CheckoutService(PricingService pricingService, InventoryRepository inventoryRepository) {
this.pricingService = pricingService;
this.inventoryRepository = inventoryRepository;
}
String checkout(String sku, double subtotal) {
if (!inventoryRepository.isInStock(sku)) {
return "Order rejected: " + sku + " is out of stock";
}
double total = pricingService.finalPrice(subtotal);
return String.format(Locale.US, "Order accepted: %s -> %.2f", sku, total);
}
}
@Component
class PricingService {
double finalPrice(double subtotal) {
return subtotal * 1.10;
}
}
@Repository
class InventoryRepository {
boolean isInStock(String sku) {
return sku != null && !sku.isBlank();
}
}
class LegacyTaxCalculator {
double applyTax(double amount) {
return amount * 1.18;
}
}
class PlainHelper {
String help() {
return "helper";
}
}Follow-up & Tricky Questions:
@Component, @Service, @Repository, @Controller, and @Configuration. The important idea is that these are all bean candidates, but they may have extra behavior on top of being plain components.@SpringBootApplication(scanBasePackages = ...) or @ComponentScan(basePackages = ...). I prefer moving the main class to a higher root package if possible, because that keeps configuration simpler.@Component and @Bean? @Component says Spring should discover the class automatically, while @Bean says I want to create and register this object explicitly from a configuration method.@Bean methods themselves found by component scanning? No. The configuration class containing them may be scanned, but the bean methods are processed separately by Spring once that configuration class is registered.@Repository just a renamed @Component? Not quite. It is still a component, but Spring also uses it as a persistence stereotype and can apply exception translation for data-access errors.@Component but is outside the base package, will Spring find it? No. Annotation alone is not enough; the package has to be inside the scan path or explicitly added.Common Mistakes:
@Bean.@Qualifier or @Primary.Memory Hook: Think of Spring scanning like a librarian checking only the shelves in one section of the library: if your book is on that shelf and has the right label, it gets cataloged; if it is in another room, Spring never sees it.
Cheat Sheet:
@SpringBootApplication includes @ComponentScan.@Bean for third-party or custom-created objects.scanBasePackages or scanBasePackageClasses when the default root is not enough.Practice Tasks:
@Service in a subpackage and confirm it is injected automatically.@Bean method and compare the startup behavior.