一、@ControllerAdvice
1、作用:配合一个或者多个自定义异常类(如下文:BusinessException),实现全局或者某个控制器(如下文:TestController)的异常处理功能。
2、原理:顾名思义,它其实是利用AOP(面向切面),实现在调用controller中的方法后织入后置异常通知。(因AOP主要还是利用动态代理模式,想深入了解可以看这篇https://www.jianshu.com/p/95970b089360,大概意思就是:控制器其实是一个代理对象,在调用里面的方法时进入代理方法中,在method.invoke(target,arg)执行时捕捉异常,利用反射生成@ControllerAdvice注解声明的类的对象,再调用对应的@ExceptionHandler方法进行处理)----本人才疏学浅,这只是实战类的博客就不多说了。
示例:
a、自定义异常类
public class BusinessException extends RuntimeException {
private String message;
//最好定义带参构造器
public BusinessException(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
b、异常处理器
//这里指定切入的controller ----不指定情况下,默认所有控制器抛出的异常都会在这个类进行处理
@ControllerAdvice(assignableTypes={TestController.class})
public class ExceptionAdvice {
//指定异常处理器所处理的异常(可自定义也可使用java自带的)
@ExceptionHandler(BusinessException.class)
public ModelAndView businessExceptionAdvice(Exception e){
//参数e其实是BusinessException的对象,可以获取其中的属性。
//跳转到异常页面
ModelAndView modelAndView = new ModelAndView("testException");
//返回到界面的数据
modelAndView.addObject("errMsg", message);
return modelAndView;
//也可以使用@ResponseBody 返回json格式数据
}
}
c、控制器
@Controller
@RequestMapping("/")
public class TestController {
/**
* 全局异常捕捉测试
* @param map
* @param request
* @param response
*/
@RequestMapping(value="exception",method=RequestMethod.GET)
@ResponseBody
public void exceptionTest(ModelMap map,HttpServletRequest request,HttpServletResponse response) throws Exception{
throw new BusinessException("发生运行时异常!");
}
}
d、异常jsp界面主要部分
<h4>异常:${errMsg}</h4>
------------------------到此简易的全局异常处理demo完成----------------------------
下面再说一下,在@ControllerAdvice注解下ExceptionAdvice 类中,使用@ModelAtrribute实现全局控制器数据绑定
好处:可以在路由到对应方法前,进行数据预先处理(添加的情况下,就是在controller方法前,调用一下这些方法使之注入到modelMap中)
缺点:因为每次路由都会先进入,@ModelAtrribute注解的方法,因此会牺牲一部分性能
(另外:@ModelAttribute标注的方法的执行是在所有拦截器的preHandle()方法执行之后才会执行。)
/**
* 全局数据绑定---1、指定名称
* @return
*/
@ModelAttribute(value="testModel")
public String mydata() {
return "这是一个全局绑定数据的测试";
}
/**
* 全局数据绑定 2、对象绑定 ---默认的key就是user
* @return
*/
@ModelAttribute
public User addAccount() {
User user = new User();
user.setId("333");
user.setName("啊啊");
return user;
}
/**
* 全局数据绑定 3、直接在ModelMap中设置 ---默认的key就是user
* @return
*/
@ModelAttribute
public void addMap(ModelMap map) {
map.put("id", 99);
map.put("name", "老王");
}
/**
* 全局异常捕捉测试
* @param map
* @param request
* @param response
*/
@RequestMapping(value="exception",method=RequestMethod.GET)
@ResponseBody
public void exceptionTest(@ModelAttribute("user") User user,ModelMap map,@ModelAttribute("testModel") String msg,HttpServletRequest request,HttpServletResponse response) throws Exception{
//获取方式1、指定名称
System.out.println("msg1:"+msg);
//获取方式2、指定对象
System.out.println("user:"+user.getName()+" "+user.getId());
//获取方式3、ModelMap中获取数据
Integer id = (Integer)map.get("id");
String name = (String)map.get("name");
System.out.println("id:"+id+" name:"+name);
//向前台返回异常
throw new BusinessException("发生运行时异常!");
}
运行结果:
msg1:这是一个全局绑定数据的测试
user:啊啊 333
id:99 name:老王
然后跳转界面。