Spring Boot
Architecture, Servlets, Directory Structure, and Execution
Spring Boot is a framework designed to simplify the development of enterprise-grade Java applications by providing an auto-configuration and abstraction layer on top of the Spring ecosystem.
1. Flow Architecture and Servlets in Spring Boot
In traditional Java EE web applications, HTTP requests were directly processed by Servlets registered manually in an application server. Spring Boot encapsulates this complexity through the embedded web server (Apache Tomcat by default) and the Front Controller pattern.
1.1. HTTP Request Flow
The flow illustrates the trajectory of an HTTP request across the application architecture:
- Client (Browser/Postman): Issues an HTTP request to a specific endpoint.
- Servlet Container (Embedded Tomcat): Receives the TCP socket and extracts the HTTP payload.
- DispatcherServlet: Acts as the central Front Controller. Intercepts all incoming requests and delegates execution to the appropriate controller handler.
- Controller Layer: Processes request parameters, validates input, and delegates business execution to the service layer.
- Service Layer: Contains core business logic, transaction isolation, and domain rules.
- Repository Layer: Abstracts storage communication through data interface contracts.
- Database / Model: Performs physical storage operations and returns the result back through the pipeline.
1.2. The Role of DispatcherServlet and SpringBootServletInitializer
Spring Boot uses DispatcherServlet as the core backbone of Spring MVC request processing:
DispatcherServlet: A special Servlet serving as a single entry point for HTTP requests. Handles handler mapping, JSON/XML conversion viaHttpMessageConverter, and global exception management.SpringBootServletInitializer: When running a Spring Boot application as an executable standalone JAR, embedded Tomcat starts automatically via@SpringBootApplication. However, if you need to package the application as a WAR file for deployment on an external traditional application server (such as standalone Tomcat, WildFly, or Payara), the main class must extendSpringBootServletInitializerand override theconfiguremethod.
package com.icesi.app;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
// This class enables an external Servlet container to configure the app when deployed as a WAR
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// Binds the external servlet configuration with the Spring Boot main class
return application.sources(DemoApplication.class);
}
}
2. Multi-Layer Architecture in Spring Boot
A clean architecture in Spring Boot promotes strict separation of concerns by splitting the project into clearly defined layers: Model, Repository, Service, and Controller.
- Model (Domain/Entity): Represents domain data or database tables.
- Repository: Interface extending persistence abstractions (such as
JpaRepository) to manage CRUD operations. - Service: Interface and implementation containing business logic, transaction isolation, and domain rules.
- Controller: Receives HTTP requests, validates parameters, and returns responses in JSON format.
2.1. Model Layer
package com.icesi.app.model;
public class User {
private Long id;
private String name;
private String email;
public User() {}
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
2.2. Repository Interface Layer
package com.icesi.app.repository;
import com.icesi.app.model.User;
import java.util.List;
import java.util.Optional;
// Repository interface contract to decouple data access logic
public interface UserRepository {
// Saves or updates an entity in storage
User save(User user);
// Finds a user by unique identifier
Optional<User> findById(Long id);
// Returns the complete list of users
List<User> findAll();
// Deletes a user by identifier
void deleteById(Long id);
}
2.3. Service Layer (Interface & Implementation)
Service Interface:
package com.icesi.app.service;
import com.icesi.app.model.User;
import java.util.List;
// Service contract defining domain use cases
public interface UserService {
User createUser(User user);
User getUserById(Long id);
List<User> getAllUsers();
}
Service Implementation:
package com.icesi.app.service.impl;
import com.icesi.app.model.User;
import com.icesi.app.repository.UserRepository;
import com.icesi.app.service.UserService;
import org.springframework.stereotype.Service;
import java.util.List;
// Marks the class as a Spring Service component for dependency injection
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
// Constructor injection
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public User createUser(User user) {
if (user.getEmail() == null || user.getEmail().isBlank()) {
throw new IllegalArgumentException("User email is mandatory");
}
return userRepository.save(user);
}
@Override
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found with ID: " + id));
}
@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}
}
2.4. Controller Layer
package com.icesi.app.controller;
import com.icesi.app.model.User;
import com.icesi.app.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
// Defines a REST controller exposing JSON endpoints
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
}
3. Typical Directory Structure in a Spring Boot Application
demo-app/
├── .mvn/ # Maven Wrapper configuration files
├── mvnw # Executable Maven Wrapper script (Linux/macOS)
├── mvnw.cmd # Executable Maven Wrapper script (Windows)
├── pom.xml # Maven build dependency file (or build.gradle)
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/icesi/app/
│ │ │ ├── DemoApplication.java # Main class annotated with @SpringBootApplication
│ │ │ ├── controller/ # REST Controllers (@RestController)
│ │ │ ├── service/ # Business Service interfaces
│ │ │ │ └── impl/ # Service implementations (@Service)
│ │ │ ├── repository/ # Persistence interfaces (@Repository / JpaRepository)
│ │ │ ├── model/ # JPA Entities (@Entity) and domain models
│ │ │ ├── dto/ # Data Transfer Objects
│ │ │ ├── config/ # Configuration beans (@Configuration, CORS, Security)
│ │ │ └── exception/ # Global exception handlers (@ControllerAdvice)
│ │ └── resources/
│ │ ├── application.properties # Main application properties
│ │ ├── application-dev.properties # Development profile configuration
│ │ ├── application-prod.properties# Production profile configuration
│ │ ├── static/ # Static assets (CSS, JS, images)
│ │ └── templates/ # HTML view templates (Thymeleaf, if applicable)
│ └── test/
│ └── java/com/icesi/app/ # Unit & Integration tests (@SpringBootTest)
The main class DemoApplication.java (annotated with @SpringBootApplication) must reside in the root package com.icesi.app. Spring Boot performs component scanning (@ComponentScan) downwards starting from this package. Placing controllers outside this package tree will cause Spring Boot to miss them.
4. Application Execution and Profile Management
4.1. Running the Application from Terminal
- Maven Wrapper
- Gradle Wrapper
# Linux / macOS:
./mvnw clean spring-boot:run
# Windows (PowerShell / CMD):
.\mvnw.cmd clean spring-boot:run
# Linux / macOS:
./gradlew clean bootRun
# Windows (PowerShell / CMD):
.\gradlew.bat clean bootRun
Before starting the web server with spring-boot:run or bootRun, it is recommended to verify that all dependencies and plugins compile cleanly by running:
- Maven:
./mvnw clean install(ormvn clean install) - Gradle:
./gradlew build(or./gradlew --refresh-dependencies)
4.2. Configuration with application.properties
# Application name
spring.application.name=demo-icesi-app
# Embedded Tomcat listening port
server.port=8080
# Context Path: Global prefix for request routing
server.servlet.context-path=/demo-api
# Logging configuration
logging.level.root=INFO
logging.level.com.icesi.app=DEBUG
logging.file.name=logs/application.log
4.3. Activating Environment Profiles via CLI
- Maven Wrapper
- Gradle Wrapper
- Executable JAR
# Linux / macOS
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
# Windows
.\mvnw.cmd spring-boot:run -Dspring-boot.run.profiles=dev
# Linux / macOS
./gradlew bootRun --args='--spring.profiles.active=dev'
# Windows
.\gradlew.bat bootRun --args="--spring.profiles.active=dev"
# Option 1: System property (-D)
java -Dspring.profiles.active=dev -jar target/demo-app-0.0.1-SNAPSHOT.jar
# Option 2: Application argument (--)
java -jar target/demo-app-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
# Option 3: Operating System Environment Variable
export SPRING_PROFILES_ACTIVE=dev
java -jar target/demo-app-0.0.1-SNAPSHOT.jar