Preface
In actual development, exception handling is a very important link. A reasonable exception handling mechanism can not only improve the robustness of the system, but also greatly improve the user experience. This article will introduce in detail several implementation methods of global exception handling in SpringBoot.
Why is global exception handling required?
If there is no unified exception handling mechanism, when an exception occurs in the system, the following problems may be caused.
- Poor user experience: Users may see some technical error messages, such as stack traces
- Safety hazards: Exposing the details of internal errors in the system may be exploited by the attacker
- Difficulty in maintenance: Exception handling codes scattered everywhere increase maintenance difficulty
- Inconsistent response format: The error format returned by different interfaces is not unified, which increases the difficulty of front-end processing
Below, let’s take a look at several ways to implement global exception handling in SpringBoot.
Method 1: @ControllerAdvice/@RestControllerAdvice + @ExceptionHandler
This is in SpringBootMost commonly usedglobal exception handling method.
First, define a unified return result class:
public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); (200); ("The operation is successful"); (data); return result; } public static <T> Result<T> error(Integer code, String message) { Result<T> result = new Result<>(); (code); (message); return result; } // Getter and setter methods are omitted}
Then, define the custom exception:
public class BusinessException extends RuntimeException { private Integer code; public BusinessException(String message) { super(message); = 500; } public BusinessException(Integer code, String message) { super(message); = code; } public Integer getCode() { return code; } }
Create a global exception handling class:
import ; import ; import ; @RestControllerAdvice public class GlobalExceptionHandler { /** * Handle custom business exceptions */ @ExceptionHandler() public Result<Void> handleBusinessException(BusinessException e) { return ((), ()); } /** * Handle parameter verification abnormalities */ @ExceptionHandler() public Result<Void> handleValidationException(ConstraintViolationException e) { return (400, "Parameter verification failed:" + ()); } /** * Exception not found for processing resource */ @ExceptionHandler() public Result<Void> handleNotFoundException(NoHandlerFoundException e) { return (404, "The requested resource does not exist"); } /** * Handle all other uncaught exceptions */ @ExceptionHandler() public Result<Void> handleException(Exception e) { return (500, "Internal Server Error:" + ()); } }
advantage
- Concise codeClear and easy to maintain
- Can be targetedDifferent exception typesDefine different processing methods
- Integrated with Spring MVC, you can get request and response context
Method 2: Implement HandlerExceptionResolver interface
HandlerExceptionResolver is an interface used in Spring MVC to resolve exceptions. We can customize exception handling logic by implementing this interface.
import ; import ; import ; import ; import ; import ; import ; import ; @Component public class CustomExceptionResolver implements HandlerExceptionResolver { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { Result<?> result; // Handle different types of exceptions if (ex instanceof BusinessException) { BusinessException businessException = (BusinessException) ex; result = ((), ()); } else { result = (500, "Internal Server Error:" + ()); } // Set the response type and status code ("application/json;charset=UTF-8"); (HttpServletResponse.SC_OK); try { // Write error message to the response PrintWriter writer = (); ((result)); (); (); } catch (IOException e) { (); } // Return an empty ModelAndView to indicate that the exception has been processed return new ModelAndView(); } }
advantage
- CanComplete controlThe process of exception handling
- You can directly operate HttpServletRequest and HttpServletResponse
- Suitable for scenarios that require special treatment
shortcoming
- The code is relatively complex
- Unable to take advantage of Spring MVC annotation advantages
Method 3: Use SimpleMappingExceptionResolver
SimpleMappingExceptionResolver is a simple implementation of HandlerExceptionResolver for scenarios that return error views.
import ; import ; import ; import ; @Configuration public class ExceptionConfig { @Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); // Set the default error page ("error/default"); // Set exception mapping Properties mappings = new Properties(); ((), "error/business"); ((), "error/runtime"); (mappings); // Set the exception attribute name, default to "exception" ("ex"); return resolver; } }
Corresponding error page template (using Thymeleaf):
<!-- templates/error/ --> <!DOCTYPE html> <html xmlns:th=""> <head> <meta charset="UTF-8"> <title>Business Errors</title> </head> <body> <h1>Business Errors</h1> <p th:text="${}">error message</p> </body> </html>
advantage
- Simple configuration
- Suitable for returning to error pageScene of
shortcoming
- Mainly suitable for return views, not for RESTful API
- Limited flexibility
Method 4: Customize ErrorController
Spring Boot provides a BasicErrorController to handle errors in the application, and we can customize the error handling logic by inheriting or replacing it.
import ; import ; import ; import ; import ; import ; import ; import ; import ; @Controller @RequestMapping("${:${:/error}}") public class CustomErrorController implements ErrorController { @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ResponseEntity<Result<Void>> handleError(HttpServletRequest request) { HttpStatus status = getStatus(request); Integer statusCode = (); String message = "Unknown Error"; switch (statusCode) { case 404: message = "The requested resource does not exist"; break; case 403: message = "No permission to access this resource"; break; case 500: message = "Internal Server Error"; break; default: break; } return new ResponseEntity<>((statusCode, message), status); } @RequestMapping(produces = MediaType.TEXT_HTML_VALUE) public String handleErrorHtml(HttpServletRequest request, Map<String, Object> model) { HttpStatus status = getStatus(request); // Add error message to the model ("status", ()); ("message", ()); // Return to the error page return "error/error"; } private HttpStatus getStatus(HttpServletRequest request) { Integer statusCode = (Integer) (".status_code"); if (statusCode == null) { return HttpStatus.INTERNAL_SERVER_ERROR; } try { return (statusCode); } catch (Exception ex) { return HttpStatus.INTERNAL_SERVER_ERROR; } } }
advantage
- CanHandle HTML and JSON responses simultaneously
- Provide a unified way to handle all unhandled errors
- You can get the error status code and detailed information
shortcoming
- Only handle HTTP errors that have occurred and cannot intercept custom exceptions
- Generally used as a guaranteed solution
Method 5: Use the error page template
Spring Boot supports displaying errors in specific status codes through static HTML pages or templates. Just create the corresponding status code page in the templates/error/ directory.
Directory structure:
src/
main/
resources/
templates/
error/
# Default error page
For example, the content:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>The page does not exist</title> </head> <body> <h1>404 - The page does not exist</h1> <p>您请求的The page does not exist,Check, pleaseURLIs it correct。</p> <a href="/" rel="external nofollow" >Return to homepage</a> </body> </html>
advantage
- ConfigurationExtremely simple, just create the corresponding page
- Suitable for simple web applications
shortcoming
- Limited flexibility
- Not suitable for RESTful API
- Unable to handle custom exceptions
Practical example: Complete exception handling system
The following provides a complete exception handling system example, combining multiple methods:
First, create an exception system:
// Basic exception classpublic abstract class BaseException extends RuntimeException { private final int code; public BaseException(int code, String message) { super(message); = code; } public int getCode() { return code; } } // Business exceptionpublic class BusinessException extends BaseException { public BusinessException(String message) { super(400, message); } public BusinessException(int code, String message) { super(code, message); } } // System exceptionpublic class SystemException extends BaseException { public SystemException(String message) { super(500, message); } public SystemException(int code, String message) { super(code, message); } } // Permission exceptionpublic class PermissionException extends BaseException { public PermissionException(String message) { super(403, message); } }
Create a global exception handler:
import org.; import org.; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = (); /** * Handle custom basic exceptions */ @ExceptionHandler() public Result<?> handleBaseException(BaseException e) { ("Business exception:{}", ()); return ((), ()); } /** * Handle parameter verification exception (@Valid) */ @ExceptionHandler() @ResponseStatus(HttpStatus.BAD_REQUEST) public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { List<FieldError> fieldErrors = ().getFieldErrors(); String errorMsg = () .map(error -> () + ": " + ()) .collect((", ")); ("Parameter verification error:{}", errorMsg); return (400, "Parameter verification error: " + errorMsg); } /** * Handle all other exceptions */ @ExceptionHandler() @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Result<?> handleException(Exception e) { ("System Exception:", e); return (500, "Internal error of the server, please contact the administrator"); } }
Example of usage:
@RestController @RequestMapping("/api") public class UserController { @GetMapping("/{id}") public Result<User> getUser(@PathVariable Long id) { if (id <= 0) { throw new BusinessException("User ID must be greater than 0"); } if (id > 100) { throw new SystemException("System maintenance"); } if (id == 10) { throw new PermissionException("No permission to view this user"); } // Simulate query user User user = new User(id, "user" + id, "user" + id + "@"); return (user); } }
Comparison and usage suggestions for various methods
Implementation method | Applicable scenarios | flexibility | Complexity |
---|---|---|---|
@ControllerAdvice + @ExceptionHandler | RESTful API, front-end and back-end separation project | high | Low |
HandlerExceptionResolver | Scenarios where exception handling process need to be carefully controlled | high | middle |
SimpleMappingExceptionResolver | Traditional web applications need to return to the error page | middle | Low |
Custom ErrorController | Scenarios where custom error pages and error responses are required | middle | middle |
Error page template | Simple web application, just customize the error page | Low | Low |
suggestion
For RESTful API or front-end separation projects: Preferred to @ControllerAdvice/@RestControllerAdvice + @ExceptionHandler method, it provides good flexibility and concise code structure.
For traditional web applications that need to return error pages: SimpleMappingExceptionResolver or error page template method can be used.
-
For complex systems: It can be used in combination, for example
- Use @ControllerAdvice to handle business exceptions
- Handle uncaught HTTP errors with custom ErrorController
- Use error page templates to provide friendly error pages
Best Practice Summary
Hierarchical exception handling: Design a reasonable exception inheritance system according to business needs to facilitate classification and processing.
Unified return format: Whether it is successful or failed, a unified return format is used to facilitate front-end processing.
Log reasonably: Recording logs in exception handling can help troubleshoot problems. Different levels of exceptions use logs at different levels.
Distinguish between development and production environments: Detailed error information can be returned in the development environment, while sensitive information should be hidden in the production environment.
-
Exception classification
- Business exception: Expectable exception caused by user operations
- System exception: internal system error
- Third-party service exception: failed to call external service, etc.
Don't ignore exceptions: Even if the exception is caught without processing, at least the log should be recorded.
Conclusion
In Spring Boot applications, global exception handling is an important part of improving system robustness and user experience. Through the implementation methods introduced in this article, developers can choose appropriate implementation solutions based on actual needs. In actual projects, it is often necessary to combine multiple methods to build a complete exception handling system.
The above is the detailed content of the 5 implementation methods of global exception handling in SpringBoot. For more information about SpringBoot’s global exception handling, please pay attention to my other related articles!