在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。参考:@ControllerAdvice 文档
创建 MyControllerAdvice,并添加 @ControllerAdvice注解。
第一种,返回map
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;
/**
* controller 增强器
* @author
* @since
*/
@ControllerAdvice
public class MyControllerAdvice {
/**
* 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
* @param binder
*/
@InitBinder
public void initBinder(WebDataBinder binder) {}
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
* @param model
*/
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("author", "Magical Sam");
}
/**
* 全局异常捕捉处理
* @param ex
* @return
*/
@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;
}
}
第二种,返回ModelAndView
import com.cngrain.gateway.util.ErrorResult;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* @类名 GlobalExceptionHandler
* @描述 全局异常捕获
* @作者
* @日期
**/
@ControllerAdvice
public class GlobalExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
/**
* @名称 defaultErrorHandler
* @描述
* 我们只需要在Controller中抛出Exception,当然我们可能会有多种不同的Exception。
* 然后在@ControllerAdvice类中,根据抛出的具体Exception类型匹配@ExceptionHandler
* 中配置的异常类型来匹配错误映射和处理。
* @参数 [request, e]
* @返回值 org.springframework.web.servlet.ModelAndView
* @作者
* @时间
*/
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", request.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
/**
* @名称 jsonErrorHandler
* @描述
* 通过@ControllerAdvice统一定义不同Exception映射到不同错误处理页面。
* 而当我们要实现RESTful API时,返回的错误是JSON格式的数据,而不是HTML页面,这时候我们也能轻松支持。
* 本质上,只需在@ExceptionHandler之后加入@ResponseBody,就能让处理函数return的内容转换为JSON格式。
* @参数 [request, e]
* @返回值 com.cngrain.gateway.exception.ErrorInfo
* @作者
* @时间
*/
@ExceptionHandler(value = MyException.class)
@ResponseBody
public ErrorInfo<String> jsonErrorHandler(HttpServletRequest request, MyException e) throws Exception {
ErrorInfo<String> r = new ErrorInfo<>();
r.setMessage(e.getMessage()==null?"空":e.getMessage());
r.setCode(ErrorInfo.ERROR);
r.setData("Some Data");
r.setUrl(request.getRequestURL().toString());
return r;
}
private ErrorResult buildError(Exception exception) {
ErrorResult error = new ErrorResult();
error.setType(exception.getClass().getName());
error.setMessage(ExceptionUtils.getMessage(exception)==null?"空":ExceptionUtils.getMessage(exception));
error.setStackTrace(ExceptionUtils.getStackTrace(exception));
error.setDate(new Date());
return error;
}
}
启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 被 @RequestMapping 注解的方法上。
@ModelAttribute:在Model上设置的值,对于所有被 @RequestMapping 注解的方法中,都可以通过 ModelMap 获取,如下:
@RequestMapping("/home")
public String home(ModelMap modelMap) {
System.out.println(modelMap.get("author"));
}
//或者 通过@ModelAttribute获取
@RequestMapping("/home")
public String home(@ModelAttribute("author") String author) {
System.out.println(author);
}
@ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,上面拦截了 Exception.class 这种异常。
spring boot 默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好,下面自定义异常处理,提供友好展示。
1、编写自定义异常类:
public class MyException extends RuntimeException {
public MyException(String code, String msg) {
this.code = code;
this.msg = msg;
}
private String code;
private String msg;
// getter & setter
}
注:spring 对于 RuntimeException 异常才会进行事务回滚。
2、编写全局异常处理类
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 {
/**
* 全局异常捕捉处理
* @param ex
* @return
*/
@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;
}
/**
* 拦截捕捉自定义异常 MyException.class
* @param ex
* @return
*/
@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;
}
}
3、controller中抛出异常进行测试。
@RequestMapping("/home")
public String home() throws Exception {
// throw new Exception("Sam 错误");
throw new MyException("101", "Sam 错误");
}
启动应用,访问:http://localhost:8080/home ,正常显示以下json内容,证明自定义异常已经成功被拦截。
{"msg":"Sam 错误","code":"101"}
*** 如果不需要返回json数据,而要渲染某个页面模板返回给浏览器,那么MyControllerAdvice中可以这么实现:**
@ExceptionHandler(value = MyException.class)
public ModelAndView myErrorHandler(MyException ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("code", ex.getCode());
modelAndView.addObject("msg", ex.getMsg());
return modelAndView;
}
在 templates 目录下,添加 error.ftl(这里使用freemarker) 进行渲染:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>错误页面</title>
</head>
<body>
<h1>${code}</h1>
<h1>${msg}</h1>
</body>
</html>
重启应用,http://localhost:8080/home 显示自定的错误页面内容。
补充:如果全部异常处理返回json,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody。