Component scanning & stereotypes
#Backend

Component scanning & stereotypes

Backend Reporter
3 min read

Component scanning converts annotation marks into registered beans, and mastering its mechanics prevents startup failures and name clashes.

Featured image

Component scanning is the mechanism that bridges annotations and container registration. At application start the scanner walks a base package, inspects each class, and for every class carrying @Component — directly or via a stereotype — it creates a bean definition. The annotation declares intent; the scan acts on it, and both halves are required.

Where the search begins

The base package defines the root of the scan. A typical Spring Boot configuration hides @ComponentScan inside @SpringBootApplication, which defaults to the package of the class it decorates and everything beneath it. Placing the main class in the root package ensures the scan reaches all sub‑packages such as web, billing, and legacy modules. Moving the main class into a sub‑package narrows the scan, causing classes above that point to be ignored and resulting in NoSuchBeanDefinitionException at startup.

The classic trap

When the main class resides in a sub‑package, the scan starts there and never ascends. A class located in a parent package remains invisible, even though it carries a stereotype. The fix is to return the main class to the root package or to specify scan base packages explicitly with @SpringBootApplication(scanBasePackages = "com.shop").

Stereotype annotations

@Component is the generic marker. Spring provides three specialized stereotypes — @Service, @Repository, and @Controller — each annotated with @Component itself. Because the scanner treats "annotated with @Component, directly or through another annotation" as the trigger, a @Service bean is discovered exactly like a bare @Component. The stereotypes convey layer intent at a glance and give Spring tools a hook for special handling.

@Repository extra benefit

When a @Repository bean is scanned, Spring wraps it in a proxy that translates vendor‑specific persistence exceptions into Spring’s DataAccessException hierarchy. This single advantage means service code catches a consistent exception type regardless of whether JPA, JDBC, or another technology lies underneath. The practice of using @Repository on data‑access classes therefore provides a concrete reason beyond naming.

@Controller handling

The @Controller stereotype is also scanned like any @Component, but the web layer later picks it up to route HTTP requests. Its special handling occurs after scanning, so the scan itself treats it as a regular component.

Bean naming and clashes

During registration the container derives a bean name from the class name by lowercasing the first letter. OrderService becomes orderService by default. When two beans share the same simple name, the container throws ConflictingBeanDefinitionException at startup, enforcing fail‑fast behavior. To resolve the ambiguity, assign an explicit name with @Service("legacyOrderService").

Scanning scope and performance

The default scan covers everything under the base package. An overly broad base package pulls in unrelated classes such as stray @Configuration or test‑only beans, increasing startup time and risking unintended activation. Precise base packages keep the scan narrow, reduce work at startup, and limit accidental inclusion. When the default sweep grabs too much, @ComponentScan filters can exclude specific annotation types or patterns, offering fine‑grained control.

Putting it together

Component scanning converts annotation marks into registered beans by walking a base package, inspecting each class, and recording a definition for every @Component‑annotated class. The base package determines the scan root and direction — only downward. Stereotypes are meta‑annotated @Component and are processed identically, though @Repository adds exception translation and @Controller is later used by the web layer. Each bean receives a decapitalized name by default, and name collisions cause immediate startup failure. Proper placement of the main class and careful selection of base packages prevent the common pitfalls of missing beans and slow startup.

The next article will address how the container resolves which bean to inject when multiple candidates match a required type, covering @Autowired, @Qualifier, and @Primary.

Guardsquare image

Comments

Loading comments...