Spring Boot provides robust mechanisms for handling errors and exceptions in applications. It supports both global and local exception handling, making applications more user-friendly and maintainable.
1. What is Error and Exception Handling?
Error and exception handling involves managing application failures gracefully. Spring Boot provides a default error response mechanism and allows customization through controllers, @ControllerAdvice, and exception handlers.
2. Default Error Handling in Spring Boot
Spring Boot automatically provides a default error response for exceptions.
Default JSON Response:
{
"timestamp": "2025-01-19T10:00:00.000+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "NullPointerException",
"path": "/api/resource"
}
This response is generated by the BasicErrorController class.
3. Step-by-Step: Setting Up Global Exception Handling
Step 1: Create a Custom Exception Class
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
Step 2: Implement @ControllerAdvice for Global Handling
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleResourceNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGenericException(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred: " + ex.getMessage());
}
}
Step 3: Customizing Error Response Attributes
Extend the DefaultErrorAttributes class to modify default error attributes.
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
errorAttributes.put("customMessage", "A custom error occurred");
errorAttributes.put("developerMessage", "Contact support with this error ID");
return errorAttributes;
}
}
4. Setting Up Local Exception Handling
Example: Using @ExceptionHandler in a Controller
@RestController
@RequestMapping("/api")
public class ResourceController {
@GetMapping("/resource/{id}")
public String getResource(@PathVariable int id) {
if (id <= 0) {
throw new ResourceNotFoundException("Resource not found with id: " + id);
}
return "Resource data";
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleResourceNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
}
5. Custom Error Pages
Spring Boot allows serving custom error pages for specific HTTP status codes.
Steps to Configure Custom Error Pages:
- Place an HTML file in the
src/main/resources/templates/errorfolder. - Name the file as
error-<status-code>.html.
Example: Custom 404 Error Page
File: error-404.html
<!DOCTYPE html>
<html>
<head>
<title>Page Not Found</title>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>The requested page does not exist.</p>
</body>
</html>
6. Using @ResponseStatus
Map exceptions to HTTP status codes directly using @ResponseStatus.
Example:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
7. Logging Exceptions
Log exceptions using SLF4J with Logback for debugging and monitoring.
Example:
@RestController
@RequestMapping("/api")
public class LoggingController {
private static final Logger logger = LoggerFactory.getLogger(LoggingController.class);
@GetMapping("/test")
public String testLogging() {
try {
throw new RuntimeException("Simulated exception");
} catch (Exception ex) {
logger.error("An error occurred: ", ex);
throw ex;
}
}
}
8. Customizing Exception Handling for Applications
Customize Exception Details:
You can use the @ExceptionHandler to add details like error codes, timestamps, or developer messages.
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Map<String, Object>> handleResourceNotFound(ResourceNotFoundException ex) {
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("timestamp", LocalDateTime.now());
errorDetails.put("message", ex.getMessage());
errorDetails.put("errorCode", "ERR404");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorDetails);
}
Custom Error Response Object:
Create a custom response object to standardize error responses.
public class ErrorResponse {
private LocalDateTime timestamp;
private String message;
private String details;
public ErrorResponse(LocalDateTime timestamp, String message, String details) {
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
// Getters and setters
}
Use it in exception handling:
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
LocalDateTime.now(),
ex.getMessage(),
request.getDescription(false)
);
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
9. Best Practices for Exception Handling
- Use Custom Exceptions: Define meaningful custom exceptions for application-specific errors.
- Centralize Exception Handling: Use
@ControllerAdviceto manage exceptions globally. - Provide Consistent Responses: Standardize error response structures using DTOs or error classes.
- Avoid Leaking Internal Details: Do not expose sensitive implementation details in error messages.
- Log Exceptions: Always log exceptions for debugging and analysis.
- Test Error Scenarios: Write test cases to ensure proper handling of various exceptions.
Let me know if further examples or details are required!