Skip to main content

Spring Framework

Structural Overview

Spring Framework

Inversion of Control and Dependency Injection

This is the foundational principle of the Spring Framework.

  • Inversion of Control (IoC): Traditionally, a component is responsible for creating or obtaining its own dependencies. The IoC principle inverts this control. Instead of the component managing its dependencies, an external container does it.
  • The IoC Container: The Spring container (ApplicationContext) is responsible for instantiating, configuring, and assembling objects known as beans.
  • Dependency Injection (DI): This is the pattern used to implement IoC. The container "injects" the dependencies (i.e., other objects a bean needs) into a bean when it is created.

Dependencies are typically declared using annotations:

  • @Component: A generic stereotype for any Spring-managed component.
  • @Service: Signifies a bean in the service layer (business logic).
  • @Repository: Signifies a bean in the data access layer.
  • @Controller / @RestController: Signifies a bean in the presentation layer.
  • @Autowired: Used to perform the injection of a bean.
@Service
public class ProductService {

private final ProductRepository productRepository;

// The ProductRepository dependency is injected by the Spring container
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}

// Business logic methods...
}

Spring MVC for Web Applications

For REST APIs, Spring uses its Model-View-Controller (MVC) module.

  • DispatcherServlet: Acts as the central "front controller". It receives all incoming HTTP requests and delegates them to the appropriate handlers (controllers).
  • @RestController: A specialized version of @Controller that assumes all methods return data directly in the response body (e.g., as JSON), eliminating the need for @ResponseBody on every method.

Spring Boot

Spring Boot is an evolution of Spring that radically simplifies the development of stand-alone, production-grade Spring-based applications.

1. Auto-Configuration

  • Spring Boot automatically configures your application based on the dependencies (JARs) you have added to the project.
  • For example, if spring-boot-starter-web is on the classpath, Spring Boot automatically configures Tomcat, Jackson (for JSON), and Spring MVC.

2. Starter Dependencies

Starters are convenient dependency descriptors that you can include in your build file (e.g., Maven's pom.xml). They group related dependencies together. Key starters for REST APIs include:

  • spring-boot-starter-web: For building RESTful web applications using Spring MVC and an embedded Tomcat server.
  • spring-boot-starter-data-jpa: For database access using Java Persistence API (JPA) with Hibernate.
  • spring-boot-starter-validation: For using Java's Bean Validation with Hibernate Validator.
  • spring-boot-starter-test: For testing with libraries like JUnit, Mockito, and Spring Test.

3. Embedded Server

Spring Boot includes an embedded server (Tomcat by default) so you can run your application as a standalone JAR file (java -jar my-app.jar). You no longer need to deploy a WAR file to an external web server.

4. The @SpringBootApplication Annotation

This is a convenience annotation that combines three others:

  • @Configuration: Tags the class as a source of bean definitions for the application context.
  • @EnableAutoConfiguration: Tells Spring Boot to start adding beans based on classpath settings.
  • @ComponentScan: Tells Spring to scan for other components, configurations, and services in the specified package.

5. Building a REST API: Key Components

Here are the primary components used to build a REST API in Spring Boot.

5.1. Controller Layer (@RestController)

This layer handles HTTP requests and maps them to specific methods.

Annotation/ClassDescription
@RequestMapping('/api/v1/products')Defines a base path for all methods in the controller.
@GetMapping("/{id}")Maps HTTP GET requests to a method.
@PostMapping()Maps HTTP POST requests (for creating resources).
@PutMapping("/{id}")Maps HTTP PUT requests (for updating resources).
@DeleteMapping("/{id}")Maps HTTP DELETE requests.
@PathVariable("id")Binds a method parameter to a URI template variable (e.g., {id}).
@RequestBody()Binds the HTTP request body to a method parameter (typically a DTO).
ResponseEntity<T>An object representing the entire HTTP response, including status code, headers, and body. Gives full control.
@RestController
@RequestMapping("/api/v1/products")
public class ProductController {
private final ProductService productService;

public ProductController(ProductService productService) {
this.productService = productService;
}

@GetMapping("/{id}")
public ResponseEntity<ProductDTO> getProductById(@PathVariable Long id) {
ProductDTO product = productService.findProductById(id);
return ResponseEntity.ok(product);
}

@PostMapping
public ResponseEntity<ProductDTO> createProduct(@Valid @RequestBody CreateProductDTO createProductDTO) {
ProductDTO newProduct = productService.createProduct(createProductDTO);
// Returns HTTP 201 Created status
return new ResponseEntity<>(newProduct, HttpStatus.CREATED);
}
}

5.2. Service Layer (@Service)

This layer contains the core business logic. It decouples the controller from the data access layer.

@Service
public class ProductService {

private final ProductRepository productRepository;
private final ModelMapper modelMapper; // A library for object mapping

// ... constructor injection

public ProductDTO findProductById(Long id) {
Product product = productRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + id));
return modelMapper.map(product, ProductDTO.class);
}

public ProductDTO createProduct(CreateProductDTO createProductDTO) {
Product product = modelMapper.map(createProductDTO, Product.class);
Product savedProduct = productRepository.save(product);
return modelMapper.map(savedProduct, ProductDTO.class);
}
}

5.3. Repository Layer (@Repository & Spring Data JPA)

This layer is responsible for data access. Spring Data JPA makes it incredibly easy by providing a repository abstraction.

  • JpaRepository<EntityType, IdType>: An interface you can extend. It comes with built-in CRUD methods like save(), findById(), findAll(), and deleteById().
  • Custom Queries: You can define custom queries by simply declaring a method signature in your repository interface. Spring Data will parse the method name and create the query for you.
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
// Spring Data JPA will automatically implement this method
// SELECT * FROM product WHERE category = ?
List<Product> findByCategory(String category);

// Example of a custom query using @Query
@Query("SELECT p FROM Product p WHERE p.price > :minPrice")
List<Product> findProductsAbovePrice(@Param("minPrice") double minPrice);
}

5.4. Entity (@Entity) and DTOs

  • Entity (@Entity): A Java class that is mapped to a database table. Each instance represents a row in the table.
  • DTO (Data Transfer Object):
    • A plain Java object used to transfer data between layers, especially between the service and controller.
    • Using DTOs is a best practice to avoid exposing internal database entities directly to the client.
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String name;

private double price;
private String category;
// Getters and Setters...
}

6. Exception Handling & Validation

  • @ControllerAdvice: Create a global exception handler class to manage exceptions across the whole application, avoiding scattered try-catch blocks in controllers.
  • @ExceptionHandler: A method-level annotation within a @ControllerAdvice class to handle specific exceptions and return a custom response.
  • Validation (@Valid): Use the @Valid annotation on @RequestBody parameters to trigger validation based on annotations (@NotNull, @Size, @Email, etc.) defined in your DTO classes.

Advanced Topics for Further Learning

Once you are comfortable with the basics, consider exploring these advanced topics to build more robust and scalable applications:

  • Asynchronous Operations: Use @Async to run long-running tasks in the background without blocking the main request thread.
  • Database Migrations: Manage your database schema changes systematically using tools like Flyway or Liquibase.
  • Caching: Integrate a caching layer using Spring's caching abstraction (@Cacheable, @CacheEvict) with providers like EhCache or Redis to improve performance.
  • Containerization: Package your Spring Boot application into a Docker container for consistent deployment and scalability.
  • Reactive Programming: Explore Spring WebFlux for building non-blocking, reactive APIs that can handle high concurrency with fewer resources.
  • Microservices Architecture: Learn about Spring Cloud, which provides tools for building distributed systems (e.g., Service Discovery with Eureka, API Gateway with Spring Cloud Gateway, Centralized Configuration with Spring Cloud Config).

References