Centralized and Loosely Coupled Exception Handling in Spring Boot

Centralized and Loosely Coupled Exception Handling in Spring Boot

As developers, we strive to create applications that are maintainable and scalable. One critical aspect of achieving this is to handle exceptions and errors in a centralized and loosely coupled manner. In a Spring Boot application, this can be efficiently implemented using a few straightforward steps. Here's how:

1. Define Custom Exception Classes

Start by defining custom exception classes to represent different error conditions in your application.

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

public class InvalidInputException extends RuntimeException {
    public InvalidInputException(String message) {
        super(message);
    }
}        

2. Create an Exception Response Class

Next, define a class to represent the structure of your error responses. This class encapsulates the error message, details, and a timestamp.

import java.util.Date;

public class ErrorResponse {
    private String message;
    private String details;
    private Date timestamp;

    public ErrorResponse(String message, String details) {
        this.message = message;
        this.details = details;
        this.timestamp = new Date();
    }

    // Getters and setters
}
        

3. Implement a Centralized Exception Handler

Use @ControllerAdvice to create a global exception handler. This will intercept exceptions thrown by any controller and handle them in a centralized manner.

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
        ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), request.getDescription(false));
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(InvalidInputException.class)
    public ResponseEntity<ErrorResponse> handleInvalidInputException(InvalidInputException ex, WebRequest request) {
        ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), request.getDescription(false));
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, WebRequest request) {
        ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), request.getDescription(false));
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
        

4. Use Exceptions in Your Services and Controllers

Throw the custom exceptions in your services and controllers where necessary. This ensures that your application logic remains clean and the error handling is managed centrally.

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/resources")
public class ResourceController {

    @GetMapping("/{id}")
    public ResponseEntity<Resource> getResourceById(@PathVariable Long id) {
        Resource resource = resourceService.findById(id);
        if (resource == null) {
            throw new ResourceNotFoundException("Resource not found with id " + id);
        }
        return new ResponseEntity<>(resource, HttpStatus.OK);
    }

    @PostMapping
    public ResponseEntity<Resource> createResource(@RequestBody Resource resource) {
        if (resource.getName() == null || resource.getName().isEmpty()) {
            throw new InvalidInputException("Resource name is required");
        }
        Resource createdResource = resourceService.save(resource);
        return new ResponseEntity<>(createdResource, HttpStatus.CREATED);
    }
}        

5. Test Your Exception Handling

Finally, write unit and integration tests to verify that your exception handling works as expected.

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;

@SpringBootTest
public class ResourceControllerTests {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testResourceNotFound() throws Exception {
        mockMvc.perform(get("/api/resources/999"))
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.message").value("Resource not found with id 999"));
    }

    @Test
    public void testInvalidInput() throws Exception {
        Resource resource = new Resource();
        mockMvc.perform(post("/api/resources")
                .contentType(MediaType.APPLICATION_JSON)
                .content(new ObjectMapper().writeValueAsString(resource)))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.message").value("Resource name is required"));
    }
}        

Conclusion

Centralizing exception handling in Spring Boot using @ControllerAdvice not only simplifies the management of errors but also ensures that your application remains loosely coupled. By defining custom exceptions and handling them globally, you can create more maintainable and robust applications. This approach allows different components to depend on well-defined exceptions without being tied to specific error handling logic, making your code cleaner and easier to maintain.


#SpringBoot #Java #ExceptionHandling #SoftwareDevelopment #Programming #CodeQuality #CleanCode #TechTips #Developers #BackendDevelopment #JavaProgramming

要查看或添加评论,请登录

Narinder Rana的更多文章

社区洞察

其他会员也浏览了