@ControllerAdvice handling exception
spring boot uses @ ControllerAdvice to handle exceptions thrown in the Controller.
In Spring 3.2, @ ControllerAdvice, @ RestControllerAdvice annotations are added, which can be used to define @ ExceptionHandler, @ InitBinder, @ ModelAttribute, and applied to all @ RequestMapping, @ PostMapping, @ GetMapping annotations.
Next, I'll show you how to use these annotations and handle exceptions through the code.
@ControllerAdvice, many beginners may not have heard of this annotation. In fact, this is a very useful annotation. As the name suggests, this is an enhanced Controller. Using this Controller, three functions can be realized:
- Global exception handling
- Global data binding
- Global data preprocessing
Flexible use of these three functions can help us simplify a lot of work. It should be noted that these functions are provided by spring MVC and can be used directly in Spring Boot. Let's look at them respectively.
@ControllerAdvice is an annotation declared on a class. Its usage mainly includes three points:
-
Combined with the method annotation @ ExceptionHandler, it is used to catch the specified types of exceptions thrown in the Controller, so as to achieve the purpose of different types of exception handling.
-
Combined with the method annotation @ InitBinder, it is used to register the custom parameter parsing method in the request, so as to customize the specified format parameters.
-
Combined with the method annotation @ ModelAttribute, the annotated method will be executed before the target Controller method is executed.
As can be seen from the above explanation, @ ControllerAdvice is basically used to declare it on a bean, and then use other annotations on the bean's methods to specify different weaving logic. However, @ ControllerAdvice does not use AOP to weave business logic here, but Spring has built-in support for the weaving methods of each logic.
Introduction to annotation -- global exception handling
//Use @ ControllerAdvice to realize global exception handling. You only need to define the class and add the annotation. The definition method is as follows: @ControllerAdvice public class MyGlobalExceptionHandler { @ExceptionHandler(Exception.class) public ModelAndView customException(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("message", e.getMessage()); mv.setViewName("myerror"); return mv; } } In this class, multiple methods can be defined, and different methods deal with different exceptions, such as methods specially dealing with null pointers and methods specially dealing with array out of bounds...,You can also handle all exception information in one method directly like the above code. @ExceptionHandler Annotation is used to indicate the exception handling type, that is, if it is specified here as NullpointerException,Then the array out of bounds exception will not be entered into this method.
First define a ControllerAdvice. The code is as follows
@ControllerAdvice public class MyExceptionHandler { /** * Apply to all @ RequestMapping annotation methods and initialize the data binder before its execution * @param binder */ @InitBinder public void initWebBinder(WebDataBinder binder){ //Unified processing of dates binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); //Add verification of data //binder.setValidator(); } /** * Bind the value to the Model so that the global @ RequestMapping can get the value * @param model */ @ModelAttribute public void addAttribute(Model model) { model.addAttribute("attribute", "The Attribute"); } /** * Capture CustomException * @param e * @return json Format type */ @ResponseBody @ExceptionHandler({CustomException.class}) //Specifies the type of exception to intercept @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //Custom browser return status code public Map<String, Object> customExceptionHandler(CustomException e) { Map<String, Object> map = new HashMap<>(); map.put("code", e.getCode()); map.put("msg", e.getMsg()); return map; } /** * Capture CustomException * @param e * @return view */ // @ExceptionHandler({CustomException.class}) // public ModelAndView customModelAndViewExceptionHandler(CustomException e) { // Map<String, Object> map = new HashMap<>(); // map.put("code", e.getCode()); // map.put("msg", e.getMsg()); // ModelAndView modelAndView = new ModelAndView(); // modelAndView.setViewName("error"); // modelAndView.addObject(map); // return modelAndView; // } }
It should be noted that the parameters passed in using the @ ExceptionHandler annotation can be an array, and when using this annotation, the parameters passed in cannot be the same, that is, two @ exceptionhandlers cannot be used to handle the same exception. If the passed in parameters are the same, the initialization of ExceptionHandler will fail.
For the @ ControllerAdvice annotation, let's take a look at the definition of the source code:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] assignableTypes() default {}; Class<? extends Annotation>[] annotations() default {}; }
We can pass the Annotation parameter specified by the declared class (an array) of basePackage. For details, please refer to: spring framework doc
Exception handling
Write a custom exception class
package com.developlee.errorhandle.exception; /** * @desc Custom exception class */ public class CustomException extends RuntimeException { private long code; private String msg; public CustomException(Long code, String msg){ this.code = code; this.msg = msg; } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
Write a global exception handling class
@ControllerAdvice("com.developlee.errorhandle") public class MyExceptionHandler { /** * Apply to all @ RequestMapping annotation methods and initialize the data binder before its execution * @param binder */ @InitBinder public void initWebBinder(WebDataBinder binder){ } /** * Bind the value to the Model so that the global @ RequestMapping can get the value * @param model */ @ModelAttribute public void addAttribute(Model model) { model.addAttribute("attribute", "The Attribute"); } /** * Capture CustomException * @param e * @return json Format type */ @ResponseBody @ExceptionHandler({CustomException.class}) //Specifies the type of exception to intercept @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //Custom browser return status code public Map<String, Object> customExceptionHandler(CustomException e) { Map<String, Object> map = new HashMap<>(); map.put("code", e.getCode()); map.put("msg", e.getMsg()); return map; } /** * Capture CustomException * @param e * @return view */ // @ExceptionHandler({CustomException.class}) // public ModelAndView customModelAndViewExceptionHandler(CustomException e) { // Map<String, Object> map = new HashMap<>(); // map.put("code", e.getCode()); // map.put("msg", e.getMsg()); // ModelAndView modelAndView = new ModelAndView(); // modelAndView.setViewName("error"); // modelAndView.addObject(map); // return modelAndView; // } }
test
Throw a custom exception in the controller
@Controller public class DemoController { /** * About @ ModelAttribute, * You can use ModelMap and @ ModelAttribute() to get parameter values. */ @GetMapping("/one") public String testError(ModelMap modelMap ) { throw new CustomException(500L, "500 exception occurred in the system!" + modelMap.get("attribute")); } @GetMapping("/two") public String testTwo(@ModelAttribute("attribute") String attribute) { throw new CustomException(500L, "500 exception occurred in the system!" + attribute); } }
Start the application. The range is localhost:8080/one The returned message is:
{"msg":"500 exception occurred in the system! The Attribute","code":500}
It can be seen that our @ InitBinder and @ ModelAttribute annotations take effect. And the custom exception was successfully intercepted. If all exception handling returns json, @ RestControllerAdvice can be used instead of @ ControllerAdvice, so there is no need to add @ ResponseBody to the method@ RestControllerAdvice has added @ ResponseBody to the annotation.