ControllerAdvice注解用于实现Controller层的全局异常捕获,可以自定义捕获特定异常进行处理。
官方文档 ControllerAdvice文档
Serves as a specialization of @Component, allowing for implementation classes to be autodetected through classpath scanning.
It is typically used to define @ExceptionHandler, @InitBinder, and @ModelAttribute methods that apply to all @RequestMapping methods.
Note that those checks are done at runtime, so adding many attributes and using multiple strategies may have negative impacts (complexity, performance).
官方文档说通常用于定义@ExceptionHandler、@InitBinder 和@ModelAttribute方法,应用于所有的@RequestMapping方法。这句话说明全局异常只应用于Controller层,对于Service层的异常只能抛出,返回给Controller处理。最后一句是提示这个检查是在程序运行时执行的,所以定于全局异常是如果做太多的处理,可能会对程序有不利的影响。
@InitBinder 首先,该注解被解析的时机,是该匹配Controller的请求执行映射的方法之前; 同时 @InitBinder标注的方法执行是多次的,一次请求来就执行一次。
当某个Controller上的第一次请求由SpringMvc前端控制器匹配到该Controller之后,根据Controller的 class 类型查找,所有方法上标注了@InitBinder的方法,并且存入RequestMappingHandlerAdapter的 initBinderCache,下次一请求执行对应业务方法之前时,可以走initBinderCache缓存,而不用再去解析@InitBinder; 所以 initBinder是controller级别的,一个Controller实例中的所有@initBinder 只对该Controller有效;
@ModelAttribute 被@ModelAttribute注释的方法会在此controller每个方法执行前被执行。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 @Controller public class HelloWorldController { @ModelAttribute public void populateModel (@RequestParam String abc, Model model) { model.addAttribute("attributeName" , abc); } @RequestMapping(value = "/helloWorld") public String helloWorld () { return "helloWorld" ; } }
这个例子,在获得请求/helloWorld 后,populateModel方法在helloWorld方法之前先被调用,它把请求参数(/helloWorld?abc=text)加入到一个名为attributeName的model属性中,在它执行后 helloWorld被调用,返回视图名helloWorld和model已由@ModelAttribute方法生产好了。
这个例子中model属性名称和model属性对象由model.addAttribute()实现,不过前提是要在方法中加入一个Model类型的参数。
全局异常示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import org.springframework.ui.Model;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.Map; @ControllerAdvice public class MyControllerAdvice { @InitBinder public void initBinder (WebDataBinder binder) {} @ModelAttribute public void addAttributes (Model model) { model.addAttribute("Date" , new Date ()); } @ResponseBody @ExceptionHandler(value = Exception.class) public Map errorHandler (Exception ex) { Map map = new HashMap (); map.put("code" , 100 ); map.put("msg" , ex.getMessage()); return map; } }
启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 被 @RequestMapping 注解的方法上。
@ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,上面拦截了 Exception.class 这种异常。
自定义异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import com.markhub.common.lang.Result;import lombok.extern.slf4j.Slf4j;import org.apache.shiro.ShiroException;import org.springframework.http.HttpStatus;import org.springframework.validation.BindingResult;import org.springframework.validation.ObjectError;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseStatus;import org.springframework.web.bind.annotation.RestControllerAdvice;@Slf4j @RestControllerAdvice public class GlobalException { @ResponseStatus(HttpStatus.UNAUTHORIZED) @ExceptionHandler(value = ShiroException.class) public Result handler (ShiroException e) { log.error("运行时异常:--------------{}" , e); return Result.fail(401 ,e.getMessage(),null ); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = MethodArgumentNotValidException.class) public Result handler (MethodArgumentNotValidException e) { log.error("实体校验异常:--------------{}" , e); BindingResult bindingResult = e.getBindingResult(); ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get(); return Result.fail(objectError.getDefaultMessage()); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = IllegalArgumentException.class) public Result handler (IllegalArgumentException e) { log.error("Assert异常:--------------{}" , e); return Result.fail(e.getMessage()); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = RuntimeException.class) public Result handler (RuntimeException e) { log.error("运行时异常:--------------{}" , e); return Result.fail(e.getMessage()); } }