Spring Boot: Core Concepts and Implementation Patterns

Getting Started with Spring Boot

To develop a web application using Spring Boot that returns "hello world" when accessing /hello, follow these steps:

  1. Create a Spring Boot project with web dependencies
  2. Define a controller class with a method and appropriate annotations
  3. Run and test the application

Request Handler Implementation

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {
    @RequestMapping("/greeting")
    public String greet() {
        System.out.println("Hello from Spring Boot");
        return "hello world";
    }
}

Parameter Handling

Simple Parameters

When parameter names match method parameter names, they are automatically mapped and type conversion is performed:

@RequestMapping("/simple-params")
public String processSimpleParams(String name, int age) {
    System.out.println("Received parameters: " + name + ", " + age);
    return "Parameters received successfully";
}

If parameter names don't match, use @RequestParam for mapping:

@RequestMapping("/mapped-params")
public String processMappedParams(@RequestParam(name="username") String name, Integer age) {
    System.out.println("Mapped parameters: " + name + ", " + age);
    return "Mapped parameters: " + name + ", " + age;
}

Entity Parameters

For simple POJOs, request parameters with matching property names are automatically mapped:

@RequestMapping("/entity-param")
public String processEntityParam(User user) {
    System.out.println(user);
    return "Entity processed successfully";
}
public class User {
    private String name;
    private Integer age;
    // Getters and setters
}

Complex Entity Objects

Nested POJOs can be handled using dot notation in parameter names:

@RequestMapping("/complex-entity")
public String processComplexEntity(User user) {
    System.out.println(user);
    return "Complex entity processed: " + user;
}
public class User {
    private String name;
    private Integer age;
    private Address address;
    // Getters and setters
}
public class Address {
    private String province;
    private String city;
    // Getters and setters
}

Array and Collection Parameters

For arrays, parameter names must match and multiple values should be provided:

@RequestMapping("/array-param")
public String processArrayParam(String[] hobbies) {
    for (String hobby : hobbies) {
        System.out.print(hobby + " ");
    }
    return "Array processed";
}

For collections, @RequestParam is required:

@RequestMapping("/collection-param")
public String processCollectionParam(@RequestParam List<String> interests) {
    for (String interest : interests) {
        System.out.print(interest + " ");
    }
    return "Collection processed";
}

Date Parameters

Use @DateTimeFormat for date parameter conversion:

@RequestMapping("/date-param")
public String processDateParam(@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") LocalDateTime timestamp) {
    System.out.println("Received timestamp: " + timestamp);
    return "Date processed: " + timestamp;
}

JSON Parameters

For JSON data, use @RequestBody to map to POJOs:

@RequestMapping("/-param")
public String processJsonParam(@RequestBody User user) {
    System.out.println(user);
    return user.toString();
}

Path Parameters

Extract parameters directly from the URL path using @PathVariable:

@RequestMapping("/path/{id}")
public String processPathParam(@PathVariable Integer id) {
    System.out.println("ID received: " + id);
    return "Processed ID: " + id;
}

Multiple parameters:

@RequestMapping("/path/{userId}/{userName}")
public String processMultiplePathParams(@PathVariable Integer userId, @PathVariable String userName) {
    System.out.println("User ID: " + userId + ", Name: " + userName);
    return "Processed user: ID=" + userId + ", Name=" + userName;
}

Response Handling

@ResponseBody Annotation

The @ResponseBody annotation (included in @RestController) converts return values to JSON responses:

@RequestMapping("/users")
public List<User> getAllUsers() {
    User user1 = new User("Alice", 25, new Address("CA", "San Francisco"));
    User user2 = new User("Bob", 30, new Address("NY", "New York"));
    User user3 = new User("Charlie", 28, new Address("TX", "Austin"));
    
    List<User> users = new ArrayList<>();
    users.add(user1);
    users.add(user2);
    users.add(user3);
    
    return users;
}

Unified Response Format

Implement a consistent response structure:

public class ApiResponse {
    private Integer status;
    private String message;
    private Object data;
    
    public ApiResponse() {}
    
    public ApiResponse(Integer status, String message, Object data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }
    
    // Getters and setters
    
    public static ApiResponse success(Object data) {
        return new ApiResponse(200, "Success", data);
    }
    
    public static ApiResponse success() {
        return new ApiResponse(200, "Success", null);
    }
    
    public static ApiResponse error(String message) {
        return new ApiResponse(500, message, null);
    }
}

Layered Architecture

Three-Layer Architecture

  • Controller Layer: Handles HTTP requests, processes input, and returns responses
  • Service Layer: Implements business logic and rules
  • Data Access Layer (DAO): Manages database operations and persistence

Inversion of Control (IOC) and Dependency Injection

IOC transfers object creation control from the program to an external container. Dependency Injection provides resources to the application at runtime.

Bean Management

Declare beans using annotations:

Annotation Description Usage
@Component Basic annotation for declaring beans General purpose components
@Controller @Component derivative for controllers Web controller classes
@Service @Component derivative for services Business logic classes
@Repository @Component derivative for data access Data access classes

Dependency Injection

Use @Autowired for dependency injection:

@RestController
public class UserController {
    @Autowired
    private UserService userService;
    
    @RequestMapping("/users")
    public ApiResponse getAllUsers() {
        List<User> users = userService.retrieveUsers();
        return ApiResponse.success(users);
    }
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    
    @Override
    public List<User> retrieveUsers() {
        List<User> users = userDao.fetchUsers();
        // Additional business logic
        return users;
    }
}
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public List<User> fetchUsers() {
        // Data access logic
        return Arrays.asList(
            new User("Alice", 25, new Address("CA", "San Francisco")),
            new User("Bob", 30, new Address("NY", "New York"))
        );
    }
}

HTTP Request Handling

GET Requests

Implement GET requests using @RequestMapping or @GetMapping:

@RestController
public class DepartmentController {
    // Using @RequestMapping
    @RequestMapping(value = "/departments", method = RequestMethod.GET)
    public ApiResponse getDepartments() {
        // Implementation
        return ApiResponse.success();
    }
    
    // Using @GetMapping
    @GetMapping("/departments")
    public ApiResponse listDepartments() {
        // Implementation
        return ApiResponse.success();
    }
}

Spring Boot Configuration

Configuration Options

Spring Boot supports multiple configuration formats:

application.properties

server.port=8080
server.address=127.0.0.1

application.yml

server:
  port: 8080
  address: 127.0.0.1

YAML Syntax Rules

  • Case-sensitive
  • Values must be separated by spaces
  • Use spaces for indentation (no tabs)
  • Same-level elements must be left-aligned
  • # indicates comments

Configuration Properties

Inject configuration values directly into class properties:

@Data
@Component
@ConfigurationProperties(prefix="storage.cloud")
public class CloudStorageConfig {
    private String endpoint;
    private String accessKey;
    private String secretKey;
    private String bucketName;
}

@Value vs @ConfigurationProperties

  • @Value: Injects individual configuration values
  • @ConfigurationProperties: Batches multiple configuration values into a bean

Session Management

Session Tracking Technologies

Cookies

@GetMapping("/set-cookie")
public ApiResponse setCookie(HttpServletResponse response) {
    response.addCookie(new Cookie("user_token", "abc123"));
    return ApiResponse.success();
}

@GetMapping("/get-cookie")
public ApiResponse getCookie(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    String tokenValue = "";
    
    for (Cookie cookie : cookies) {
        if ("user_token".equals(cookie.getName())) {
            tokenValue = cookie.getValue();
        }
    }
    
    return ApiResponse.success(tokenValue);
}

HTTP Sessions

@GetMapping("/set-session")
public ApiResponse setSession(HttpSession session) {
    session.setAttribute("currentUser", "admin");
    return ApiResponse.success("Session set");
}

@GetMapping("/get-session")
public ApiResponse getSession(HttpServletRequest request) {
    HttpSession session = request.getSession();
    String user = (String) session.getAttribute("currentUser");
    return ApiResponse.success(user);
}

JWT Tokens

JWT (JSON Web Tokens) provide a secure way to transmit information:

@GetMapping("/generate-token")
public ApiResponse generateToken() {
    Map claims = new HashMap<>();
    claims.put("userId", 123);
    claims.put("username", "testuser");
    
    String jwt = Jwts.builder()
        .signWith(SignatureAlgorithm.HS256, "secret-key")
        .setClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + 3600000))
        .compact();
    
    return ApiResponse.success(jwt);
}

Exception Handling

Global Exception Handler

Implement a centralized exception handler:

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ApiResponse handleException(Exception ex) {
        ex.printStackTrace();
        return ApiResponse.error("An error occurred: " + ex.getMessage());
    }
    
    @ExceptionHandler(ResourceNotFoundException.class)
    public ApiResponse handleResourceNotFound(ResourceNotFoundException ex) {
        return ApiResponse.error(ex.getMessage());
    }
}

Transaction Management

@Transactional Annotation

Use @Transactional to ensure atomic operations:

@Service
public class DepartmentServiceImpl implements DepartmentService {
    @Autowired
    private DepartmentMapper departmentMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    
    @Transactional
    @Override
    public void deleteDepartment(Integer id) {
        departmentMapper.delete(id);
        employeeMapper.deleteByDepartmentId(id);
    }
}

Transaction Properties

Rollback Behavior

@Transactional(rollbackFor = Exception.class)
public void performOperation() {
    // Operations that should rollback on any exception
}

Propagation Behavior

Value Description
REQUIRED Requires transaction, joins if exists
REQUIRES_NEW Creates new transaction regardless
SUPPORTS Supports transaction, runs without if none
NOT_SUPPORTED Does not support transaction, suspends if exists

Aspect-Oriented Programming (AOP)

AOP Fundamentals

AOP allows cross-cutting concerns to be modularized:

Implementing AOP

@Component
@Aspect
@Slf4j
public class ExecutionTimeAspect {
    
    @Around("execution(* com.example.demo.service.*.*(..))")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object result = joinPoint.proceed();
        
        long duration = System.currentTimeMillis() - start;
        log.info("Method {} executed in {} ms", 
                joinPoint.getSignature().getName(), duration);
        
        return result;
    }
}

Advice Types

  • @Around: Advice around the method execution
  • @Before: Advice before method execution
  • @After: Advice after method execution
  • @AfterReturning: Advice after successful method execution
  • @AfterThrowing: Advice after method throws exception

Pointcut Expressions

// Method execution in any service package
@Around("execution(* com.example.service.*.*(..))")

// Method execution in specific class
@Around("execution(* com.example.service.OrderService.*(..))")

// Method with specific annotation
@Around("@annotation(com.example.annotation.Loggable)")

Spring Boot Principles

Configuration Priority

Spring Boot configuration sources in priority order:

  1. Command line arguments
  2. Java system properties
  3. application.properties
  4. application.yml
  5. application.yaml

Bean Scopes

Scope Description
singleton Single instance per Spring container (default)
prototype New instance each time requested
request Single instance per HTTP request
session Single instance per HTTP session

Conditional Configuration

Use @Conditional for conditional bean registration:

@Bean
@ConditionalOnClass(name = "com.example.SomeClass")
public SomeBean someBean() {
    return new SomeBean();
}
@Bean
@ConditionalOnMissingBean
public Gson gson(GsonBuilder gsonBuilder) {
    return gsonBuilder.create();
}

Tags: Spring Boot java web development REST API Dependency Injection

Posted on Tue, 23 Jun 2026 16:06:38 +0000 by Syto