3. Spring Core — IoC & Dependency Injection

Spring Beans & Component Scanning

Complete guide to ApplicationContext, Spring Beans, stereotype annotations (@Component, @Service, @Repository), explicit @Bean configuration, BeanDefinition metadata, and resolving bean ambiguity.

In the Spring ecosystem, managed application objects are called Spring Beans, and the environment that manages them is called the Spring IoC Container (ApplicationContext).

1. What Is a Spring Bean?

A Spring Bean is simply a Java object that is instantiated, assembled, wired, and managed by the Spring IoC Container, rather than manually created with new.

Plain Java Object (POJO):
  User user = new User(); // You manage creation, wiring, garbage collection.

Spring Bean:
  Managed by ApplicationContext (Lifecycle, Dependency Injection, Scope, AOP Proxies).

ApplicationContext vs BeanFactory

ApplicationContext is the advanced interface representing the Spring IoC Container:

BeanFactory (Basic Container Interface)

   │ (Extends)
ApplicationContext (Enterprise Container Interface)
   ├── Event Publication (ApplicationEvent)
   ├── Internationalization (MessageSource)
   ├── Environment / Profile Management (Environment)
   └── AOP & Annotation Integration

2. Stereotype Annotations (@Component & Derivatives)

To inform Spring that a Java class should be registered as a Spring Bean, you mark it with a Stereotype Annotation:

@Component (Base Stereotype) @Service (Business Logic Layer) @Repository (Data Access / DAO Layer) @Controller / @RestController (Presentation / Web Layer)
AnnotationApplication LayerSpecial Spring Behavior
@ComponentGeneric Utility / Any LayerGeneral-purpose component managed by Spring.
@ServiceBusiness Logic LayerIndicates class holds domain business logic and transactions.
@RepositoryData Access Layer (DAO)Catches SQL/Persistence exceptions and translates them into Spring's DataAccessException hierarchy.
@ControllerPresentation / Web LayerHandles HTTP requests, web routing, and view rendering.

3. Explicit Configuration: @Configuration & @Bean

Stereotype annotations (@Component) work great when you own the Java source code. What if you need to register a bean from an external third-party library (e.g., RestTemplate, ObjectMapper, or Redis Connection Factory)?

You cannot add @Component to third-party compiled .class files. Instead, you use explicit Java configuration via @Configuration and @Bean:

package com.kdev.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration // Marks class as a factory blueprint for Spring Beans
public class AppConfig {

    // Return value of this factory method is registered as a Spring Bean
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@Component vs @Bean Comparison

Dimension@Component (Class-Level)@Bean (Method-Level)
TargetPlaced on Class declarations.Placed on Factory Methods inside @Configuration.
Source OwnershipRequires access to source code.Works with third-party libraries and unmodifiable code.
Custom SetupAutomatic component scanning.Allows complex multi-line custom initialization logic.

4. BeanDefinition Metadata

Before Spring instantiates a bean, it reads annotations or XML configuration and creates a BeanDefinition object in memory.

A BeanDefinition acts as a recipe/blueprint specifying:

  • Class implementation type (com.kdev.demo.service.EmailService).
  • Scope (singleton, prototype).
  • Dependency wiring references (userRepository).
  • Primary/Qualifier priority flags.
  • Initialization (@PostConstruct) and Destruction (@PreDestroy) callbacks.

5. Resolving Bean Ambiguity (NoUniqueBeanDefinitionException)

Suppose you define an interface PaymentProcessor with two concrete implementations registered as Spring Beans:

public interface PaymentProcessor {
    void processPayment(double amount);
}

@Component
public class CreditCardProcessor implements PaymentProcessor { ... }

@Component
public class PaypalProcessor implements PaymentProcessor { ... }

If a service requests PaymentProcessor via Constructor Injection:

@Service
public class CheckoutService {

    private final PaymentProcessor paymentProcessor;

    public CheckoutService(PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }
}

Spring will fail on application startup with a fatal exception: NoUniqueBeanDefinitionException: No qualifying bean of type 'PaymentProcessor' available: expected single matching bean but found 2: creditCardProcessor, paypalProcessor

Solution 1: @Primary (Default Candidate)

Mark one implementation with @Primary. Spring will select it as the default whenever ambiguity occurs:

@Component
@Primary // Selected by default
public class CreditCardProcessor implements PaymentProcessor { ... }

Solution 2: @Qualifier (Explicit Target Selection)

Use @Qualifier to explicitly specify the target bean name at the injection site:

@Service
public class CheckoutService {

    private final PaymentProcessor paymentProcessor;

    // Explicitly requests the 'paypalProcessor' bean
    public CheckoutService(@Qualifier("paypalProcessor") PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }
}

❓ Knowledge Check

Knowledge Check

What exception will Spring throw at startup if two beans implement the same interface and neither @Primary nor @Qualifier is specified at the injection site?

Knowledge Check

When should a developer use @Configuration + @Bean factory methods instead of adding @Component to a class?

On this page