skip to content

What does @ComponentScan do, and how do you tell it which packages to scan?

level: juniorimportance: must knowfreq 80%

answer

  1. scan packages → find @Component → register bean
  2. basePackages (String) vs basePackageClasses (type-safe)
  3. default = declaring class's package (Boot root package)
  4. recursive into subpackages
  5. @Service/@Repository meta-annotated with @Component

basics

~10 s

@ComponentScan tells Spring to search packages for classes annotated with @Component (and @Service, @Repository, @Controller) and register them as beans. You point it at packages with basePackages, e.g. @ComponentScan(basePackages = "com.app").

solid answer

~40 s

@ComponentScan is a configuration annotation that triggers classpath scanning: Spring walks the given packages, finds classes marked with stereotype annotations (@Component and its specializations @Service, @Repository, @Controller, plus anything meta-annotated with @Component), and registers each as a bean definition. You choose packages via basePackages (String names) or the type-safe basePackageClasses (marker classes; Spring scans their package). If you specify neither, it defaults to the package of the class that declares @ComponentScan — which is exactly why @SpringBootApplication (which includes @ComponentScan) is placed in a root package. Scanning is recursive into subpackages. @Configuration classes are themselves @Component-annotated, so they're picked up too. This is the mechanism behind classpath/annotation-based configuration versus explicit @Bean methods.

code

java · 8 lines
java
@Configuration
@ComponentScan(basePackages = {"com.app.web", "com.app.core"})
// type-safe alternative:
// @ComponentScan(basePackageClasses = {WebMarker.class, CoreMarker.class})
public class AppConfig { }

@Service // meta-annotated with @Component -> detected, bean name "orderService"
public class OrderService { }

go deeper

for a junior

Should know @ComponentScan finds @Component/@Service/etc. and registers them, and that basePackages selects packages.

for a middle

Should mention basePackageClasses type-safety, the default-package rule tied to @SpringBootApplication, recursion, and default bean naming.

for a senior

Should contrast scanning vs explicit @Bean, discuss meta-annotation detection, and when broad scanning is a smell.

for a principal

Should frame scanning as one strategy in the bean-definition-registration pipeline and reason about package/module boundaries and startup cost.

## What @ComponentScan is `@ComponentScan` is a Spring annotation, normally placed on a `@Configuration` class, that turns on **classpath scanning**. Instead of you declaring every bean by hand with a `@Bean` method, Spring searches ("scans") a set of packages, finds classes that carry a **stereotype annotation**, and automatically registers each one as a **bean definition** in the `ApplicationContext`. ### Stereotype annotations it detects - `@Component` — generic component. - `@Service`, `@Repository`, `@Controller`, `@RestController`, `@Configuration` — these are all **meta-annotated** with `@Component`, so scanning finds them too. Spring detects any class whose annotations transitively include `@Component`. ### Choosing packages - `basePackages` — one or more package names as Strings: `@ComponentScan(basePackages = {"com.app.web", "com.app.core"})`. `value` is an alias, so `@ComponentScan("com.app")` works. - `basePackageClasses` — type-safe alternative: you pass classes (often empty marker interfaces) and Spring scans the package **each class lives in**. Refactoring/renaming a package won't silently break this, unlike a hardcoded String. - **Default when nothing is given:** the package of the class that declares the annotation. That's the crucial default behind Spring Boot — `@SpringBootApplication` is a composed annotation that includes `@ComponentScan`, so putting your main class in a top-level package (e.g. `com.app`) makes Spring scan `com.app` and everything beneath it. ### Recursion Scanning is **recursive**: naming `com.app` also scans `com.app.web`, `com.app.core.impl`, etc. A common bug is placing a bean **outside** the scanned root (e.g. `com.other`) — it silently won't be registered. ### How it relates to bean definitions Each detected class becomes a `BeanDefinition`. By default the **bean name** is the simple class name with the first letter lowercased (`OrderService` → `orderService`), produced by `AnnotationBeanNameGenerator`. You can override the name via the stereotype's `value`, e.g. `@Service("orders")`. ### XML equivalent In XML config the same thing is `<context:component-scan base-package="com.app"/>`. ### When to use - Use component scanning for **your own application classes** you control and annotate. - Use explicit `@Bean` methods for **third-party classes** you can't annotate, or when you need constructor arguments/conditional wiring. ### Gotchas - Scanning too broad a package (e.g. an empty String or a very high package) is slow and may pick up unintended beans; scan only what you own. - A class must be **concrete and instantiable** and have a usable constructor to become a bean; abstract classes/interfaces are skipped (unless matched by custom filters as candidates). - Only annotated classes are found by default — a plain POJO with no stereotype is invisible to scanning.

  • Why does the location of the @SpringBootApplication class matter?
    @SpringBootApplication bundles @ComponentScan with no basePackages, so it defaults to the main class's own package and scans it recursively. Put the class in a root package or beans in sibling packages won't be found.
  • What's the default bean name for a scanned class?
    The uncapitalized simple class name (OrderService -> orderService), generated by AnnotationBeanNameGenerator. Override it via the stereotype value, e.g. @Service("orders").

saying these in an interview costs you the question

  • Thinking @ComponentScan registers every class on the classpath rather than only annotated stereotypes
  • Believing scanning is not recursive into subpackages
  • Claiming a plain POJO without any annotation gets picked up by default scanning
  • Not knowing the default is the declaring class's package

context