13 @ControllerAdvice
13 @ControllerAdvice
@ControllerAdvice是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:
全局异常处理
全局数据绑定
全局数据预处理
灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是 SpringMVC 提供的功能,在 Spring Boot 中可以直接使用
13.1全局异常处理
SpringMVC 框架处理异常的常用方式:使用@ExceptionHandler 注解处理异常。
13.1.1@ExceptionHandler 注解
使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
而被注解的方法,其返回值可以是 ModelAndView、 String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、 HttpServletRequest、 HttpServletResponse 等。系统会自动为这些方法参数赋值。
对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。
同时需要注意使用@ExceptionHandler(value=**.class) ,value中异常类不要重复,如果重复编译可以通过,但是当发生异常时会抛不清楚使用哪个方法处理错误。@ExceptionHandler 注解 不写value的只能有一个。
若不定义异常类,直接使用@ExceptionHandler注解方法,异常都会被此方法处理。
13.1.2全局异常使用
1.自定义异常类
定义三个异常类: NameException、AgeException、MyUserException。其中 MyUserException是另外两个异常的父类
public class MyUserException extends Exception { public MyUserException() { super(); } public MyUserException(String message) { super(message); } }
//表示当用户的姓名有异常,抛出NameException public class NameException extends MyUserException { public NameException() { super(); } public NameException(String message) { super(message); } }
//当年龄有问题时,抛出的异常 public class AgeException extends MyUserException { public AgeException() { super(); } public AgeException(String message) { super(message); } }
2.定义异常处理类@ControllerAdvice
/** * @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能) * 位置:在类的上面。 * 特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。 */ @ControllerAdvice public class GlobalExceptionHandler { //定义方法,处理发生的异常 /* 处理异常的方法和控制器方法的定义一样,可以有多个参数,可以有ModelAndView, String, void,对象类型的返回值 形参:Exception,表示Controller中抛出的异常对象。 通过形参可以获取发生的异常信息。 @ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时, 由当前方法处理 */ // 发生NameException异常由当前方法处理 @ExceptionHandler(value = NameException.class) public ModelAndView doNameException(Exception exception){ //处理NameException的异常。 /* 异常发生处理逻辑: 1.需要把异常记录下来,记录到数据库,日志文件。 记录日志发生的时间,哪个方法发生的,异常错误内容。 2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。 3.给用户友好的提示。 */ ModelAndView mv = new ModelAndView(); mv.addObject("msg","姓名必须是zs,其它用户不能访问"); mv.addObject("ex",exception); mv.setViewName("nameError"); return mv; } //处理AgeException异常 @ExceptionHandler(value = AgeException.class) public ModelAndView doAgeException(Exception exception){ //处理AgeException的异常。 /* 异常发生处理逻辑: 1.需要把异常记录下来, 记录到数据库,日志文件。 记录日志发生的时间,哪个方法发生的,异常错误内容。 2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。 3.给用户友好的提示。 */ ModelAndView mv = new ModelAndView(); mv.addObject("msg","你的年龄不能大于80"); mv.addObject("ex",exception); mv.setViewName("ageError"); return mv; } //处理其它异常, NameException, AgeException以外,不知类型的异常 @ExceptionHandler public ModelAndView doOtherException(Exception exception){ //处理其它异常 ModelAndView mv = new ModelAndView(); mv.addObject("msg","你的年龄不能大于80"); mv.addObject("ex",exception); mv.setViewName("defaultError"); return mv; } }
3.定义处理器
@Controller public class MyController { @RequestMapping(value = "/some.do") public ModelAndView doSome(String name,Integer age) throws MyUserException { ModelAndView mv = new ModelAndView(); //根据请求参数抛出异常 if (!"zs".equals(name)) { throw new NameException("姓名不正确!!!"); } if (age == null || age > 80) { throw new AgeException("年龄比较大!!!"); } mv.addObject("myname",name); mv.addObject("myage",age); mv.setViewName("show"); return mv; } }
13.2 全局数据绑定
全局数据绑定功能可以用来做一些初始化的数据操作,我们可以将一些公共的数据定义在添加了 @ControllerAdvice 注解的类中,这样在每一个 Controller 的接口中,就都能够访问导致这些数据。
使用步骤,首先定义全局数据,如下:
@ControllerAdvice public class MyGlobalExceptionHandler { @ModelAttribute(name = "md") public Map<String,Object> mydata() { HashMap<String, Object> map = new HashMap<>(); map.put("age", 99); map.put("gender", "男"); return map; } }
使用 @ModelAttribute 注解标记该方法的返回数据是一个全局数据,默认情况下,这个全局数据的 key 就是返回的变量名,value 就是方法返回值,当然开发者可以通过 @ModelAttribute 注解的 name 属性去重新指定 key。
定义完成后,在任何一个Controller 的接口中,都可以获取到这里定义的数据:
@RestController public class HelloController { @GetMapping("/hello") public String hello(Model model) { Map<String, Object> map = model.asMap(); System.out.println(map); int i = 1 / 0; return "hello controller advice"; } }
13.3 全局数据预处理
有两个实体类,Book 和 Author,分别定义如下:
public class Book { private String name; private Long price; //getter/setter } public class Author { private String name; private Integer age; //getter/setter }
此时,如果我定义一个数据添加接口,如下:
@PostMapping("/book") public void addBook(Book book, Author author) { System.out.println(book); System.out.println(author); }
这个时候,添加操作就会有问题,因为两个实体类都有一个 name 属性,从前端传递时 ,无法区分。此时,通过 @ControllerAdvice 的全局数据预处理可以解决这个问题
解决步骤如下:
1.给接口中的变量取别名
@PostMapping("/book") public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) { System.out.println(book); System.out.println(author); }
2.进行请求数据预处理
在 @ControllerAdvice 标记的类中添加如下代码:
@InitBinder("b") public void b(WebDataBinder binder) { binder.setFieldDefaultPrefix("b."); } @InitBinder("a") public void a(WebDataBinder binder) { binder.setFieldDefaultPrefix("a."); }
@InitBinder("b") 注解表示该方法用来处理和Book和相关的参数,在方法中,给参数添加一个 b 前缀,即请求参数要有b前缀.
3.发送请求
请求发送时,通过给不同对象的参数添加不同的前缀,可以实现参数的区分
本文来自博客园,作者:Lz_蚂蚱,转载请注明原文链接:https://www.cnblogs.com/leizia/p/15173521.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步