Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Spring Boot REST API Development

Spring Boot Basics

  • What is Spring Boot?
  • Dependency Injection
  • Building REST Controllers

Validation & Error Handling

  • Bean Validation with @Valid
  • Global Exception Handling

Spring Framework Internals

  • IoC Container Fundamentals
  • Bean Lifecycle In Detail
  • Component Scanning & Configuration
  • Auto-Configuration Mechanism
  • Spring AOP
  • @Transactional Deep Dive

Microservices Architecture

  • Monolith to Microservices Decomposition
  • API Gateway
  • Service Discovery
  • Inter-Service Communication Choices
  • Event-Driven Architecture Patterns
  • Messaging Technology Choices

Resilience Patterns

  • Configuration Management
  • Circuit Breaker Pattern
  • Retry & Backoff Strategies
  • Bulkhead & Rate Limiting
  • Timeout Strategy
  • Why Microservices Fail

Distributed Data & Consistency Patterns

  • Two-Phase Commit
  • Saga Pattern
  • Outbox Pattern
  • Eventual Consistency Design
  • CQRS Basics
  • CAP Theorem

Observability & Operations

  • Centralized Logging
  • Distributed Tracing
  • Metrics & Monitoring
  • Alerting Strategy
  • Health Checks

Payment Systems

  • Payment — Requirements
  • Payment — Core Flow
  • Payment — Idempotency Implementation
  • Payment — Failure Handling & Reconciliation
  • Payment — Security

Order Management System

  • OMS — Requirements
  • OMS — State Machine Design
Chaturmind
← Spring Boot REST API Development

Spring Boot Basics

  • What is Spring Boot?
  • Dependency Injection
  • Building REST Controllers

Validation & Error Handling

  • Bean Validation with @Valid
  • Global Exception Handling

Spring Framework Internals

  • IoC Container Fundamentals
  • Bean Lifecycle In Detail
  • Component Scanning & Configuration
  • Auto-Configuration Mechanism
  • Spring AOP
  • @Transactional Deep Dive

Microservices Architecture

  • Monolith to Microservices Decomposition
  • API Gateway
  • Service Discovery
  • Inter-Service Communication Choices
  • Event-Driven Architecture Patterns
  • Messaging Technology Choices

Resilience Patterns

  • Configuration Management
  • Circuit Breaker Pattern
  • Retry & Backoff Strategies
  • Bulkhead & Rate Limiting
  • Timeout Strategy
  • Why Microservices Fail

Distributed Data & Consistency Patterns

  • Two-Phase Commit
  • Saga Pattern
  • Outbox Pattern
  • Eventual Consistency Design
  • CQRS Basics
  • CAP Theorem

Observability & Operations

  • Centralized Logging
  • Distributed Tracing
  • Metrics & Monitoring
  • Alerting Strategy
  • Health Checks

Payment Systems

  • Payment — Requirements
  • Payment — Core Flow
  • Payment — Idempotency Implementation
  • Payment — Failure Handling & Reconciliation
  • Payment — Security

Order Management System

  • OMS — Requirements
  • OMS — State Machine Design
HomeLearnSpring BootSpring Boot REST API DevelopmentSpring Framework Internals
✓ FreeAdvanced· 10 min read

Component Scanning & Configuration

Stereotype semantics, @Bean methods for code you don't own, why @Configuration classes get CGLIB-proxied, circular dependency resolution via the three-level cache, and @Qualifier vs @Primary.

Published September 23, 2026


Component Scanning & Configuration

Stereotype semantics

@Component, @Service, @Repository, @Controller all register a class as a Spring bean identically at the mechanical level — the differences are about intent and, for @Repository specifically, actual added behavior:

@Component   // generic — "this is a Spring-managed bean," no more specific meaning
@Service     // service-layer business logic — same mechanics as @Component, communicates architectural intent
@Repository  // data-access layer — ADDITIONALLY enables exception translation: a driver-specific exception
             // (e.g. a MongoDB-specific exception) gets translated into a Spring DataAccessException,
             // so calling code can catch a consistent exception type regardless of which database is behind it
@Controller  // web layer — handles HTTP requests, works with Spring MVC's view resolution

@Repository's exception translation is the one case where the stereotype isn't purely cosmetic — it's implemented via the same BeanPostProcessor mechanism from Bean Lifecycle In Detail, wrapping repository beans with translation logic.

@ComponentScan: base packages and filters

@SpringBootApplication // implicitly includes @ComponentScan of the current package and sub-packages
public class MyApplication { ... }

@ComponentScan(basePackages = "com.example.orders", excludeFilters = @Filter(type = FilterType.REGEX, pattern = ".*LegacyService"))

By default, Spring Boot's @SpringBootApplication scans the package it's declared in and everything below it — classes in sibling or parent packages are invisible to component scanning unless explicitly included via basePackages. This is a common source of "why isn't my bean being picked up" bugs: a class in a package outside the scan root simply never gets registered.

@Bean methods: for objects you don't own

Stereotype annotations only work on classes you can annotate directly — a third-party SDK client class can't be retrofitted with @Component. @Bean methods inside an @Configuration class solve this:

@Configuration
class ThirdPartyConfig {
    @Bean
    StripeClient stripeClient(@Value("\${stripe.api-key}") String apiKey) {
        return new StripeClient(apiKey); // you control construction explicitly, since you can't annotate StripeClient itself
    }
}

@Configuration proxying: why singleton enforcement needs CGLIB

@Configuration
class AppConfig {
    @Bean Engine engine() { return new Engine(); }
    @Bean Car car() { return new Car(engine()); } // calls engine() — but does this create a SECOND Engine?
}

If car() naively called the plain Java method engine(), it would invoke the raw method body and construct a second Engine — violating singleton scope, since engine() was also registered as its own bean. Spring prevents this by CGLIB-subclassing the @Configuration class at startup: the actual class Spring instantiates is a runtime-generated subclass of AppConfig that intercepts every inter-@Bean-method call, checking the container first — if engine() was already created as a singleton, the intercepted call returns the existing bean instead of running the method body again. This is why @Configuration classes can't be final (CGLIB needs to subclass them) and why calling a @Bean method from within another @Bean method in the same config class correctly returns the shared singleton instead of a fresh object.

Circular dependency resolution: the three-level cache

@Service class ServiceA { @Autowired ServiceB b; } // setter/field injection
@Service class ServiceB { @Autowired ServiceA a; }

For setter/field injection, Spring can resolve this circular reference using a three-level cache: as ServiceA is being constructed, Spring exposes an early, not-fully-initialized reference to it in a cache before ServiceA's fields are populated. When ServiceB needs a ServiceA to satisfy its own field injection, it retrieves that early reference from the cache rather than triggering another full construction — both objects end up correctly wired to each other once both finish initializing.

Why constructor injection can't be resolved this way: a constructor needs its argument fully available before the object can be constructed at all — there's no "early, not-yet-initialized reference" to hand out, because the object doesn't exist yet until the constructor returns. Two beans circularly requiring each other via constructor injection is a genuine, unresolvable circular dependency, and Spring throws BeanCurrentlyInCreationException at startup — this is, not coincidentally, one of the strongest practical arguments for constructor injection despite the general preference for it elsewhere: it converts a circular-dependency design smell into a startup failure, instead of silently working around it.

@Qualifier vs @Primary

@Component @Primary
class EmailNotifier implements Notifier { ... } // the DEFAULT choice when multiple candidates exist

@Component
class SmsNotifier implements Notifier { ... }

class AlertService {
    AlertService(@Qualifier("smsNotifier") Notifier notifier) { ... } // explicitly overrides @Primary for this specific injection site
}

@Primary sets a default candidate applied whenever multiple beans of a type exist and no more specific instruction is given. @Qualifier names a specific bean at the exact injection point, and takes precedence over @Primary when both are present — @Primary is a fallback rule, @Qualifier is an explicit override.

Follow-up questions this topic invites — and their answers

Q: What actually goes wrong if a class marked @Configuration is declared final? A: CGLIB proxying (needed for the inter-@Bean-method interception described above) requires subclassing the configuration class at runtime — a final class can't be subclassed, so Spring can't create the proxy, and inter-bean-method calls would bypass singleton enforcement entirely (silently creating duplicate instances) rather than failing loudly, making this a subtle bug rather than a startup error.

Q: Is @ComponentScan's default (scan-from-current-package-down) ever a problem in a multi-module project? A: Yes — if application code lives in a different package tree than the @SpringBootApplication class's package, those classes won't be auto-detected unless basePackages is explicitly widened, which is a common real onboarding confusion in projects with an unconventional package layout.

Q: Can @Repository's exception translation be disabled or bypassed? A: It's tied to the PersistenceExceptionTranslationPostProcessor bean being present (which Spring Boot auto-configures by default) — removing or not registering that post-processor would leave @Repository-annotated classes as functionally identical to @Component, with no translation behavior.

Q: If setter injection resolves circular dependencies via the three-level cache, is that actually a good thing to rely on? A: Generally no — most Spring guidance today treats a circular dependency, even a setter-injection-resolvable one, as a design smell worth fixing (usually by extracting shared behavior into a third bean both depend on) rather than a feature to lean on; the cache exists as a pragmatic escape hatch for legacy code, not as an endorsed pattern for new code.

Previous

Bean Lifecycle In Detail

Next

Auto-Configuration Mechanism

AI Tutor

Lesson: Component Scanning & Configuration

Quick actions

AI responses can be inaccurate. Verify critical information.