Spring Boot Dependency Injection and Stereotypes Explained with Practical Code Examples

3 min read

Spring Boot Dependency Injection and Stereotypes Explained with Practical Code Examples

1. Introduction

In modern enterprise applications, Dependency Injection (DI) and Stereotypes play a key role in building maintainable, testable, and loosely coupled systems. In the Spring Framework, these concepts are implemented through annotations that define and manage beans in the application context. This article demonstrates how Dependency Injection and Stereotypes work together to simplify development, using the key Spring annotations: @Autowired, @Qualifier, @Service, @Repository, @Controller, and @RestController.

2. Problem

Before the introduction of annotations and dependency injection, developers had to manually create and manage dependencies between classes. This approach led to tight coupling, poor testability, and complex configuration using XML files.

For example, if a controller class creates its own service instance using the new keyword, it becomes hard to switch implementations or test the code independently. Developers need a more modular way to manage dependencies while still clearly defining application layers (Controller, Service, Repository).

3. Solution

Spring provides a powerful solution through Dependency Injection and Stereotype annotations:

  • Dependency Injection (DI): Instead of manually creating objects, Spring automatically injects required beans at runtime using annotations like @Autowired and @Qualifier.
  • Stereotypes: Stereotype annotations such as @Service, @Repository, and @RestController mark classes as specific Spring-managed components, helping organize the application by layers.

This combination helps achieve loose coupling, better modularity, and cleaner architecture.

4. Implementation

Let’s implement a simple example of a Customer Management System to demonstrate dependency injection and stereotypes.

4.1. Project Structure

com.example.demo
 ├── controller
 │     └── CustomerController.java
 ├── service
 │     ├── CustomerService.java
 │     └── CustomerServiceImpl.java
 ├── repository
 │     ├── CustomerRepository.java
 │     └── CustomerRepositoryImpl.java
 └── DemoApplication.java

4.2. Repository Layer

package com.example.demo.repository;

import org.springframework.stereotype.Repository;

@Repository
public class CustomerRepositoryImpl implements CustomerRepository {

    @Override
    public String getCustomerNameById(int id) {
        // Simulating database access
        if (id == 1) return "Alice Johnson";
        if (id == 2) return "Bob Smith";
        return "Unknown Customer";
    }
}
package com.example.demo.repository;

public interface CustomerRepository {
    String getCustomerNameById(int id);
}

Here, @Repository marks the class as a Data Access Object (DAO). Spring automatically detects it during component scanning and makes it available for injection.

4.3. Service Layer

package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.example.demo.repository.CustomerRepository;

@Service
public class CustomerServiceImpl implements CustomerService {

    private final CustomerRepository customerRepository;

    // Constructor Injection
    @Autowired
    public CustomerServiceImpl(@Qualifier("customerRepositoryImpl") CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    @Override
    public String getCustomerDetails(int id) {
        return "Customer Details: " + customerRepository.getCustomerNameById(id);
    }
}
package com.example.demo.service;

public interface CustomerService {
    String getCustomerDetails(int id);
}

In this layer:

  • @Service marks the class as a business logic component.
  • @Autowired performs dependency injection, letting Spring inject a suitable bean automatically.
  • @Qualifier ensures that if multiple beans of the same type exist, the correct one is injected.

4.4. Controller Layer

package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import com.example.demo.service.CustomerService;

@RestController
@RequestMapping("/api/customers")
public class CustomerController {

    private final CustomerService customerService;

    @Autowired
    public CustomerController(CustomerService customerService) {
        this.customerService = customerService;
    }

    @GetMapping("/{id}")
    public String getCustomer(@PathVariable int id) {
        return customerService.getCustomerDetails(id);
    }
}

Here:

  • @RestController is a shortcut for @Controller + @ResponseBody, making the class a REST API controller.
  • @RequestMapping defines the base URL path.
  • @GetMapping handles HTTP GET requests.

When a user calls GET /api/customers/1, Spring automatically injects the required dependencies and returns the JSON response:

Customer Details: Alice Johnson

4.5. Application Entry Point

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@SpringBootApplication triggers component scanning, ensuring that all @Repository, @Service, and @RestController beans are detected and managed by Spring’s application context.

5. Conclusion

Using Dependency Injection and Stereotype annotations, developers can build applications that are clean, modular, and easy to test.

  • @Autowired and @Qualifier simplify dependency management.
  • @Service, @Repository, and @RestController clearly define application layers and responsibilities.
  • Spring’s container takes care of wiring, freeing developers to focus on business logic rather than boilerplate setup.

By adopting these concepts, teams can significantly improve the scalability, maintainability, and testability of their Spring-based applications.

🤞 Never miss a story from us, get weekly updates to your inbox!

Leave a Reply

Your email address will not be published. Required fields are marked *