Close Menu
    Facebook X (Twitter) Instagram
    Apkdot
    Facebook X (Twitter) Instagram
    Apkdot
    Backend

    Java Backend Development: Spring Boot Guide

    ijofedBy ijofedApril 21, 2025No Comments12 Mins Read

    Introduction to Spring Boot

    Spring Boot has revolutionized Java backend development by providing a convention-over-configuration approach that simplifies the creation of production-grade applications. This comprehensive guide will explore Spring Boot’s powerful features, helping you build robust, scalable, and maintainable backend applications.

    The Spring ecosystem offers a rich set of tools and libraries that make it an excellent choice for enterprise applications. Spring Boot’s auto-configuration, embedded servers, and production-ready features make it ideal for microservices and monolithic applications alike. Its integration with Spring Data, Spring Security, and Spring Cloud provides a complete solution for modern backend development.

    Project Structure and Architecture

    Spring Boot follows a layered architecture pattern that promotes separation of concerns and maintainability. Let’s examine a comprehensive project structure that follows best practices.

    project-root/
    ├── src/
    │   ├── main/
    │   │   ├── java/
    │   │   │   └── com/
    │   │   │       └── example/
    │   │   │           └── app/
    │   │   │               ├── config/
    │   │   │               │   ├── SecurityConfig.java
    │   │   │               │   ├── SwaggerConfig.java
    │   │   │               │   └── WebConfig.java
    │   │   │               ├── controller/
    │   │   │               │   ├── UserController.java
    │   │   │               │   └── ProductController.java
    │   │   │               ├── model/
    │   │   │               │   ├── User.java
    │   │   │               │   └── Product.java
    │   │   │               ├── repository/
    │   │   │               │   ├── UserRepository.java
    │   │   │               │   └── ProductRepository.java
    │   │   │               ├── service/
    │   │   │               │   ├── UserService.java
    │   │   │               │   └── ProductService.java
    │   │   │               ├── dto/
    │   │   │               │   ├── UserDTO.java
    │   │   │               │   └── ProductDTO.java
    │   │   │               ├── exception/
    │   │   │               │   ├── GlobalExceptionHandler.java
    │   │   │               │   └── ResourceNotFoundException.java
    │   │   │               └── Application.java
    │   │   └── resources/
    │   │       ├── application.yml
    │   │       ├── application-dev.yml
    │   │       ├── application-prod.yml
    │   │       └── logback-spring.xml
    │   └── test/
    │       └── java/
    │           └── com/
    │               └── example/
    │                   └── app/
    │                       ├── controller/
    │                       ├── service/
    │                       └── repository/
    ├── pom.xml
    └── README.md

    This structure follows Spring Boot’s best practices for project organization. The main package contains the application code, with separate packages for configuration, controllers, models, repositories, services, and DTOs. The resources directory contains configuration files for different environments. The test directory mirrors the main structure for unit and integration tests.

    Models and Database Integration

    Spring Data JPA provides a powerful ORM implementation for Java applications. Let’s implement comprehensive models with proper relationships and validation.

    package com.example.app.model;
    
    import jakarta.persistence.*;
    import jakarta.validation.constraints.*;
    import lombok.Data;
    import org.hibernate.annotations.CreationTimestamp;
    import org.hibernate.annotations.UpdateTimestamp;
    
    import java.time.LocalDateTime;
    import java.util.HashSet;
    import java.util.Set;
    
    @Entity
    @Table(name = "users")
    @Data
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @NotBlank
        @Size(min = 3, max = 50)
        @Column(unique = true)
        private String username;
    
        @NotBlank
        @Email
        @Column(unique = true)
        private String email;
    
        @NotBlank
        @Size(min = 8)
        private String password;
    
        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
        private Set roles = new HashSet<>();
    
        @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
        private Set products = new HashSet<>();
    
        @CreationTimestamp
        private LocalDateTime createdAt;
    
        @UpdateTimestamp
        private LocalDateTime updatedAt;
    
        @PrePersist
        @PreUpdate
        private void validate() {
            if (password.length() < 8) {
                throw new IllegalArgumentException("Password must be at least 8 characters long");
            }
        }
    }
    
    @Entity
    @Table(name = "products")
    @Data
    public class Product {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @NotBlank
        @Size(min = 3, max = 100)
        private String name;
    
        @NotBlank
        @Column(columnDefinition = "TEXT")
        private String description;
    
        @NotNull
        @DecimalMin("0.01")
        private BigDecimal price;
    
        @NotNull
        @Min(0)
        private Integer stock;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "user_id")
        private User user;
    
        @OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
        private Set reviews = new HashSet<>();
    
        @CreationTimestamp
        private LocalDateTime createdAt;
    
        @UpdateTimestamp
        private LocalDateTime updatedAt;
    }
    
    @Entity
    @Table(name = "reviews")
    @Data
    public class Review {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @NotNull
        @Min(1)
        @Max(5)
        private Integer rating;
    
        @Column(columnDefinition = "TEXT")
        private String comment;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "product_id")
        private Product product;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "user_id")
        private User user;
    
        @CreationTimestamp
        private LocalDateTime createdAt;
    
        @UpdateTimestamp
        private LocalDateTime updatedAt;
    }

    These models demonstrate Spring Data JPA’s powerful ORM capabilities. The User model includes proper validation, password constraints, and role management. The Product model includes proper relationships and validation. The Review model shows how to implement many-to-one relationships with proper constraints. Each model includes auditing fields and proper validation.

    Repositories and Services

    Spring Data JPA provides powerful repository abstractions, while Spring’s service layer handles business logic. Let’s implement comprehensive repositories and services.

    package com.example.app.repository;
    
    import com.example.app.model.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;
    import org.springframework.stereotype.Repository;
    
    import java.util.Optional;
    
    @Repository
    public interface UserRepository extends JpaRepository {
        Optional findByUsername(String username);
        Optional findByEmail(String email);
        boolean existsByUsername(String username);
        boolean existsByEmail(String email);
    
        @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.username = :username")
        Optional findByUsernameWithRoles(@Param("username") String username);
    }
    
    package com.example.app.service;
    
    import com.example.app.dto.UserDTO;
    import com.example.app.exception.ResourceNotFoundException;
    import com.example.app.model.User;
    import com.example.app.repository.UserRepository;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    @Service
    @Transactional
    public class UserService {
        private final UserRepository userRepository;
        private final PasswordEncoder passwordEncoder;
    
        public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
            this.userRepository = userRepository;
            this.passwordEncoder = passwordEncoder;
        }
    
        public UserDTO createUser(UserDTO userDTO) {
            if (userRepository.existsByUsername(userDTO.getUsername())) {
                throw new IllegalArgumentException("Username already exists");
            }
            if (userRepository.existsByEmail(userDTO.getEmail())) {
                throw new IllegalArgumentException("Email already exists");
            }
    
            User user = new User();
            user.setUsername(userDTO.getUsername());
            user.setEmail(userDTO.getEmail());
            user.setPassword(passwordEncoder.encode(userDTO.getPassword()));
    
            return convertToDTO(userRepository.save(user));
        }
    
        public UserDTO getUserById(Long id) {
            return userRepository.findById(id)
                .map(this::convertToDTO)
                .orElseThrow(() -> new ResourceNotFoundException("User not found"));
        }
    
        public List getAllUsers() {
            return userRepository.findAll().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        }
    
        public UserDTO updateUser(Long id, UserDTO userDTO) {
            User user = userRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    
            if (!user.getUsername().equals(userDTO.getUsername()) 
                && userRepository.existsByUsername(userDTO.getUsername())) {
                throw new IllegalArgumentException("Username already exists");
            }
    
            if (!user.getEmail().equals(userDTO.getEmail()) 
                && userRepository.existsByEmail(userDTO.getEmail())) {
                throw new IllegalArgumentException("Email already exists");
            }
    
            user.setUsername(userDTO.getUsername());
            user.setEmail(userDTO.getEmail());
            if (userDTO.getPassword() != null) {
                user.setPassword(passwordEncoder.encode(userDTO.getPassword()));
            }
    
            return convertToDTO(userRepository.save(user));
        }
    
        public void deleteUser(Long id) {
            if (!userRepository.existsById(id)) {
                throw new ResourceNotFoundException("User not found");
            }
            userRepository.deleteById(id);
        }
    
        private UserDTO convertToDTO(User user) {
            UserDTO dto = new UserDTO();
            dto.setId(user.getId());
            dto.setUsername(user.getUsername());
            dto.setEmail(user.getEmail());
            dto.setCreatedAt(user.getCreatedAt());
            dto.setUpdatedAt(user.getUpdatedAt());
            return dto;
        }
    }

    This implementation shows how to create comprehensive repositories and services in Spring Boot. The UserRepository demonstrates custom query methods and relationship fetching. The UserService handles business logic, including user creation, retrieval, updating, and deletion. The implementation includes proper transaction management, validation, and error handling.

    Controllers and DTOs

    Spring MVC provides a powerful framework for building REST APIs. Let’s implement comprehensive controllers and DTOs.

    package com.example.app.controller;
    
    import com.example.app.dto.UserDTO;
    import com.example.app.service.UserService;
    import jakarta.validation.Valid;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
    
    import java.net.URI;
    import java.util.List;
    
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        private final UserService userService;
    
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        @PostMapping
        public ResponseEntity createUser(@Valid @RequestBody UserDTO userDTO) {
            UserDTO createdUser = userService.createUser(userDTO);
            URI location = ServletUriComponentsBuilder
                .fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(createdUser.getId())
                .toUri();
            return ResponseEntity.created(location).body(createdUser);
        }
    
        @GetMapping("/{id}")
        public ResponseEntity getUser(@PathVariable Long id) {
            return ResponseEntity.ok(userService.getUserById(id));
        }
    
        @GetMapping
        public ResponseEntity> getAllUsers() {
            return ResponseEntity.ok(userService.getAllUsers());
        }
    
        @PutMapping("/{id}")
        public ResponseEntity updateUser(
            @PathVariable Long id,
            @Valid @RequestBody UserDTO userDTO
        ) {
            return ResponseEntity.ok(userService.updateUser(id, userDTO));
        }
    
        @DeleteMapping("/{id}")
        public ResponseEntity deleteUser(@PathVariable Long id) {
            userService.deleteUser(id);
            return ResponseEntity.noContent().build();
        }
    }
    
    package com.example.app.dto;
    
    import jakarta.validation.constraints.*;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    public class UserDTO {
        private Long id;
    
        @NotBlank
        @Size(min = 3, max = 50)
        private String username;
    
        @NotBlank
        @Email
        private String email;
    
        @NotBlank
        @Size(min = 8)
        private String password;
    
        private LocalDateTime createdAt;
        private LocalDateTime updatedAt;
    }

    This implementation shows how to create comprehensive controllers and DTOs in Spring Boot. The UserController demonstrates RESTful endpoints for user management, including proper HTTP status codes and location headers. The UserDTO includes validation annotations and proper field constraints. The implementation follows REST best practices and includes proper error handling.

    Security Configuration

    Spring Security provides comprehensive security features for Spring applications. Let’s implement a robust security configuration.

    package com.example.app.config;
    
    import com.example.app.security.JwtAuthenticationFilter;
    import com.example.app.security.JwtAuthenticationEntryPoint;
    import com.example.app.service.UserDetailsServiceImpl;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
    import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    @Configuration
    @EnableWebSecurity
    @EnableMethodSecurity
    public class SecurityConfig {
        private final UserDetailsServiceImpl userDetailsService;
        private final JwtAuthenticationEntryPoint unauthorizedHandler;
    
        public SecurityConfig(
            UserDetailsServiceImpl userDetailsService,
            JwtAuthenticationEntryPoint unauthorizedHandler
        ) {
            this.userDetailsService = userDetailsService;
            this.unauthorizedHandler = unauthorizedHandler;
        }
    
        @Bean
        public JwtAuthenticationFilter jwtAuthenticationFilter() {
            return new JwtAuthenticationFilter();
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authConfig
        ) throws Exception {
            return authConfig.getAuthenticationManager();
        }
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http
                .cors()
                    .and()
                .csrf()
                    .disable()
                .exceptionHandling()
                    .authenticationEntryPoint(unauthorizedHandler)
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .authorizeHttpRequests()
                    .requestMatchers("/api/auth/**").permitAll()
                    .requestMatchers("/api/public/**").permitAll()
                    .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
                    .anyRequest().authenticated();
    
            http.addFilterBefore(
                jwtAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class
            );
    
            return http.build();
        }
    }
    
    package com.example.app.security;
    
    import com.example.app.service.UserDetailsServiceImpl;
    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
    import org.springframework.util.StringUtils;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import java.io.IOException;
    
    public class JwtAuthenticationFilter extends OncePerRequestFilter {
        private final JwtTokenProvider tokenProvider;
        private final UserDetailsServiceImpl userDetailsService;
    
        public JwtAuthenticationFilter(
            JwtTokenProvider tokenProvider,
            UserDetailsServiceImpl userDetailsService
        ) {
            this.tokenProvider = tokenProvider;
            this.userDetailsService = userDetailsService;
        }
    
        @Override
        protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
        ) throws ServletException, IOException {
            try {
                String jwt = getJwtFromRequest(request);
    
                if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                    String username = tokenProvider.getUsernameFromJWT(jwt);
                    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                    
                    UsernamePasswordAuthenticationToken authentication = 
                        new UsernamePasswordAuthenticationToken(
                            userDetails,
                            null,
                            userDetails.getAuthorities()
                        );
                    authentication.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                    );
    
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } catch (Exception ex) {
                logger.error("Could not set user authentication in security context", ex);
            }
    
            filterChain.doFilter(request, response);
        }
    
        private String getJwtFromRequest(HttpServletRequest request) {
            String bearerToken = request.getHeader("Authorization");
            if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
                return bearerToken.substring(7);
            }
            return null;
        }
    }

    This implementation shows how to configure security in Spring Boot applications. The SecurityConfig class sets up JWT authentication, CORS, CSRF protection, and URL-based authorization. The JwtAuthenticationFilter handles token validation and user authentication. The implementation includes proper security headers and session management.

    Exception Handling

    Spring provides powerful exception handling mechanisms. Let’s implement comprehensive exception handling.

    package com.example.app.exception;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.validation.FieldError;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @RestControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(ResourceNotFoundException.class)
        public ResponseEntity handleResourceNotFoundException(
            ResourceNotFoundException ex
        ) {
            ErrorResponse error = new ErrorResponse(
                HttpStatus.NOT_FOUND.value(),
                ex.getMessage()
            );
            return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
        }
    
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public ResponseEntity> handleValidationExceptions(
            MethodArgumentNotValidException ex
        ) {
            Map errors = new HashMap<>();
            ex.getBindingResult().getAllErrors().forEach((error) -> {
                String fieldName = ((FieldError) error).getField();
                String errorMessage = error.getDefaultMessage();
                errors.put(fieldName, errorMessage);
            });
            return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
        }
    
        @ExceptionHandler(Exception.class)
        public ResponseEntity handleGlobalException(Exception ex) {
            ErrorResponse error = new ErrorResponse(
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                "An error occurred while processing your request"
            );
            return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
    @Data
    @AllArgsConstructor
    class ErrorResponse {
        private int status;
        private String message;
        private long timestamp = System.currentTimeMillis();
    }

    This implementation shows how to handle exceptions in Spring Boot applications. The GlobalExceptionHandler provides centralized exception handling for different types of exceptions, including validation errors, resource not found errors, and general exceptions. The implementation includes proper error response formatting and HTTP status codes.

    Testing

    Spring Boot provides excellent testing support. Let’s implement comprehensive tests for our application.

    package com.example.app.controller;
    
    import com.example.app.dto.UserDTO;
    import com.example.app.service.UserService;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.boot.test.mock.mockito.MockBean;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    
    import java.util.Arrays;
    import java.util.List;
    
    import static org.mockito.ArgumentMatchers.any;
    import static org.mockito.BDDMockito.given;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    @WebMvcTest(UserController.class)
    class UserControllerTest {
        @Autowired
        private MockMvc mockMvc;
    
        @Autowired
        private ObjectMapper objectMapper;
    
        @MockBean
        private UserService userService;
    
        @Test
        void createUser_ShouldReturnCreatedUser() throws Exception {
            UserDTO userDTO = new UserDTO();
            userDTO.setUsername("testuser");
            userDTO.setEmail("test@example.com");
            userDTO.setPassword("password123");
    
            given(userService.createUser(any(UserDTO.class))).willReturn(userDTO);
    
            mockMvc.perform(post("/api/users")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(userDTO)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.username").value(userDTO.getUsername()))
                .andExpect(jsonPath("$.email").value(userDTO.getEmail()));
        }
    
        @Test
        void getAllUsers_ShouldReturnUserList() throws Exception {
            List users = Arrays.asList(
                createUserDTO(1L, "user1", "user1@example.com"),
                createUserDTO(2L, "user2", "user2@example.com")
            );
    
            given(userService.getAllUsers()).willReturn(users);
    
            mockMvc.perform(get("/api/users"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].username").value("user1"))
                .andExpect(jsonPath("$[1].username").value("user2"));
        }
    
        private UserDTO createUserDTO(Long id, String username, String email) {
            UserDTO userDTO = new UserDTO();
            userDTO.setId(id);
            userDTO.setUsername(username);
            userDTO.setEmail(email);
            return userDTO;
        }
    }
    
    package com.example.app.service;
    
    import com.example.app.dto.UserDTO;
    import com.example.app.model.User;
    import com.example.app.repository.UserRepository;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.junit.jupiter.MockitoExtension;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    import java.util.Optional;
    
    import static org.junit.jupiter.api.Assertions.*;
    import static org.mockito.ArgumentMatchers.any;
    import static org.mockito.BDDMockito.given;
    import static org.mockito.Mockito.verify;
    
    @ExtendWith(MockitoExtension.class)
    class UserServiceTest {
        @Mock
        private UserRepository userRepository;
    
        @Mock
        private PasswordEncoder passwordEncoder;
    
        @InjectMocks
        private UserService userService;
    
        @Test
        void createUser_ShouldReturnSavedUser() {
            UserDTO userDTO = new UserDTO();
            userDTO.setUsername("testuser");
            userDTO.setEmail("test@example.com");
            userDTO.setPassword("password123");
    
            User user = new User();
            user.setUsername(userDTO.getUsername());
            user.setEmail(userDTO.getEmail());
            user.setPassword("encodedPassword");
    
            given(userRepository.existsByUsername(any())).willReturn(false);
            given(userRepository.existsByEmail(any())).willReturn(false);
            given(passwordEncoder.encode(any())).willReturn("encodedPassword");
            given(userRepository.save(any())).willReturn(user);
    
            UserDTO result = userService.createUser(userDTO);
    
            assertNotNull(result);
            assertEquals(userDTO.getUsername(), result.getUsername());
            assertEquals(userDTO.getEmail(), result.getEmail());
            verify(userRepository).save(any(User.class));
        }
    }

    This implementation shows how to write comprehensive tests for Spring Boot applications. The UserControllerTest demonstrates controller testing with MockMvc, including request/response validation. The UserServiceTest shows service layer testing with Mockito, including method invocation verification. Both test classes include proper setup and assertions.

    Deployment and Monitoring

    Spring Boot provides excellent deployment and monitoring capabilities. Let’s implement production-ready configuration.

    # application-prod.yml
    spring:
      datasource:
        url: ${SPRING_DATASOURCE_URL}
        username: ${SPRING_DATASOURCE_USERNAME}
        password: ${SPRING_DATASOURCE_PASSWORD}
        driver-class-name: org.postgresql.Driver
      jpa:
        hibernate:
          ddl-auto: validate
        properties:
          hibernate:
            dialect: org.hibernate.dialect.PostgreSQLDialect
      security:
        jwt:
          secret: ${JWT_SECRET}
          expiration: 86400000
    
    server:
      port: ${PORT:8080}
      compression:
        enabled: true
        mime-types: text/html,text/xml,text/plain,text/css,application/javascript,application/json
        min-response-size: 1024
    
    management:
      endpoints:
        web:
          exposure:
            include: health,info,metrics
      endpoint:
        health:
          show-details: always
        metrics:
          enabled: true
    
    logging:
      level:
        root: INFO
        com.example.app: DEBUG
      file:
        name: /var/log/app/application.log
      pattern:
        console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
        file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
    
    # Dockerfile
    FROM eclipse-temurin:17-jdk-alpine as build
    WORKDIR /workspace/app
    
    COPY mvnw .
    COPY .mvn .mvn
    COPY pom.xml .
    COPY src src
    
    RUN ./mvnw install -DskipTests
    RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
    
    FROM eclipse-temurin:17-jre-alpine
    VOLUME /tmp
    ARG DEPENDENCY=/workspace/app/target/dependency
    COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
    COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
    COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
    ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.app.Application"]
    
    # docker-compose.yml
    version: '3.8'
    services:
      app:
        build: .
        ports:
          - "8080:8080"
        environment:
          - SPRING_PROFILES_ACTIVE=prod
          - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/app
          - SPRING_DATASOURCE_USERNAME=app
          - SPRING_DATASOURCE_PASSWORD=app
          - JWT_SECRET=your-secret-key
        depends_on:
          - db
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
          interval: 30s
          timeout: 10s
          retries: 3
    
      db:
        image: postgres:14-alpine
        environment:
          - POSTGRES_DB=app
          - POSTGRES_USER=app
          - POSTGRES_PASSWORD=app
        volumes:
          - postgres_data:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U app"]
          interval: 10s
          timeout: 5s
          retries: 5
    
    volumes:
      postgres_data:

    This implementation shows how to configure Spring Boot applications for production deployment. The application-prod.yml includes database configuration, security settings, server configuration, and logging setup. The Dockerfile demonstrates how to create a production-ready Docker image. The docker-compose.yml shows how to orchestrate the application with its dependencies.

    ijofed

    Related Posts

    Go Backend Development: Gin and Echo Guide

    April 21, 2025

    Python Backend Development: Django and Flask Guide

    April 21, 2025

    Node.js Backend Development: A Comprehensive Guide

    April 21, 2025
    Leave A Reply Cancel Reply

    Facebook X (Twitter) Instagram Pinterest
    © 2025 ThemeSphere. Designed by ThemeSphere.

    Type above and press Enter to search. Press Esc to cancel.