在spring 3.2中,新增了@ControllerAdvice 注解,并且配套有三个注解@ExceptionHandler、@InitBinder、@ModelAttribute,以此来对@RequestMapping注解下的方法进行“切面”环绕。参考:@ControllerAdvice 文档
- 全局异常处理@ExceptionHandler
- 全局数据绑定@ModelAttribute
- 全局数据预处理@InitBinder
一、最常用的全局异常处理
1、自定义异常类
package com.demo.demo.exception;
@Data
public class MyException extends RuntimeException {
public MyException(String msg) {
this.msg = msg;
}
public MyException(String code, String msg) {
this.code = code;
this.msg = msg;
}
private String code;
private String msg;
}
2、自定义全局异常处理类
package com.demo.demo.controller;
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;
/**
* 如果全部异常处理返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局异常捕捉处理
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public Map errorHandler(Exception ex) {
Map map = new HashMap();
map.put("code", 500);
map.put("msg", ex.getMessage());
return map;
}
/**
* 拦截捕捉自定义异常 MyException.class
*/
@ResponseBody
@ExceptionHandler(value = MyException.class)
public Map myErrorHandler(MyException ex) {
Map map = new HashMap();
map.put("code", ex.getCode());
map.put("msg", ex.getMsg());
return map;
}
/**
* 捕捉到异常后,不是返回json而是返回某个渲染的页面
*/
@ExceptionHandler(value = MyException2.class)
public ModelAndView myErrorHandler(MyException2 ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("code", ex.getCode());
modelAndView.addObject("msg", ex.getMsg());
return modelAndView;
}
}
3、Controller拦截类
@RequestMapping("/getException")
public String getException() throws Exception {
throw new MyException("500", "自定义异常错误");
//最后返回前端以下json信息
//{"msg":"自定义异常错误","code":"500"}
}
二、全局数据绑定
1、自定义全局数据绑定类
package com.demo.demo.controller;
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 GlobalController {
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
*/
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("author", "Magical Sam");
}
@ModelAttribute(value = "message")
public String globalModelAttribute() {
System.out.println("global model attribute.");
return "this is from model attribute";
}
}
2、在@RequestMapping下获取定义的值
@RequestMapping("/getValue")
public String getValue(ModelMap modelMap,) {
System.out.println(modelMap.get("author"));//Magical Sam
}
@RequestMapping("/getValue2")
public String getValue2(@ModelAttribute("message") String message/**感觉像是给一个全局方法返回值取的别名*/) {
System.out.println(message);//global model attribute.this is from model attribute
}
三、全局数据预处理
解决两个实体类有相同属性名,从前端接收两个相同属性会拼接接收的问题
1、有相同属性的实体类
2、预处理类
package com.junya.areyouok;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
@ControllerAdvice
public class GlobalData {
@InitBinder("book")
public void initA(WebDataBinder binder){
binder.setFieldDefaultPrefix("book.");
}
@InitBinder("author")
public void initB(WebDataBinder binder){
binder.setFieldDefaultPrefix("author.");
}
}
3、controller接收类
package com.junya.areyouok.controller;
import com.junya.areyouok.entity.Author;
import com.junya.areyouok.entity.Book;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class BookController {
@PostMapping("/addBook")
public void addBook(@ModelAttribute("book") Book book, @ModelAttribute("author") Author author) {
System.out.println(book);
System.out.println(author);
}
}
4、从前端传入数据格式
处理前端传入的日期格式字符串到后台解析为Date格式
/**
* 将前台传递过来的日期格式的字符串,自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
//binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
{
@Override
public void setAsText(String text)
{
setValue(DateUtils.parseDate(text));
}
});
}
四、 小结
本文首先讲解了@ControllerAdvice注解的作用,然后结合@ControllerAdvice讲解了能够与其结合的三个注解的使用方式。关于这三种使用方式,需要说明的是,这三种注解如果应用于@ControllerAdvice注解所标注的类中,那么它们表示会对@ControllerAdvice所指定的范围内的接口都有效;如果单纯的将这三种注解应用于某个Controller中,那么它们将只会对该Controller中所有的接口有效,并且此时是不需要在该Controller上标注@ControllerAdvice的。
最后,本文借鉴了一些其他前辈的文章,如果有侵权,请联系我删除哦。
欢迎大家留言交流、共同进步!