为控制器添加通知
与SpringAOP一样,SpringMVC也可以为控制器加入通知,它主要涉及4个注解:
- @ControllerAdvice:作用于类,用以标识全局性的控制的拦截器,将应用于对应的控制器
- @InitBinder:允许在构造控制器参数的时候,加入一定的自定义控制
- @ExceptionHandler:当控制器发生异常时,就会跳转到该方法上
- @ModelAttribute:先于控制器方法执行,当标注方法返回对象时,会保存到数据模型中,并传递给拦截的控制器
一个控制器通知的实例。
//标识控制器通知,并且指定对应的包
@ControllerAdvice(basePackages = {"com.wise.tiger.controller.advice"})
public class CommonControllerAdvice {
/**
* 定义http对应参数处理规则
*/
@InitBinder
public void initBinder(WebDataBinder binder){
//针对日期类型的格式化,其中CustomDateEditor是客户自定义编辑器
//boolean值表示是否允许为空
binder.registerCustomEditor(Date.class,
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),
false));
}
@ModelAttribute //处理数据模型
public void populateModel(Model model){
model.addAttribute("projectName","peppa");
}
//被拦截控制器出现Exception异常时,会调用该方法,该方法返回会返回exception视图
@ExceptionHandler(Exception.class)
public String exception(){
return "exception";
}
}
- @ControllerAdvice已经标记了@Component,它的属性packages则是指定拦截的控制器,然后通过注解@InitBinder可以获得一个参数----WebDataBinder,它是一个可以指定pojo参数属性转换的数据绑定。这里被拦截到的控制器关于日期对象的参数都会被它处理不需要我们自己制定Formatter了。
- @ModelAttribute是关于数据模型的,它会在进入控制器方法前运行,加入一个数据模型键值对"projectName"->"peppa".
- @ExceptionHandler作用是制定被拦截到的控制器发生异常后,如果异常匹配,就会使用该方法处理,返回视图exception去响应,这样可以避免异常页面的不友好
也可以在控制器当中使用@InitBinder、@ExceptionHandler、@ModelAttribute,此时标注的方法只对当前控制器有效,控制器形参可以通过@ModelAttribute("参数名")获得提前保存在数据模型中的数据,拿上面的例子来说,如果一个控制器方法想获得projectName参数的值:
@RequestMapping(value="getProjectName")
public String getProjectName(@ModelAttribute("projectName") String name){
System.out.println(name);//name的最终值为peppa
}
处理异常
控制器的通知注解@ExceptionHandler可以处理异常,此外,spring mvc还提供了其他的异常处理机制,使用它们可以获取更为精确的信息,从而为定位问题带来方便。在默认情况下,spring会将自身产生的异常转换为合适的状态码,通过这些状态码可以进一步确定异常发生的原因,以便找到对应的问题。
spring中部分异常默认映射码
BindException | 400 - Bad Request |
ConversionNotSupportedException | 500 - Internal Server Error |
HttpMediaTypeNotAcceptableException | 406 - Not Acceptable |
HttpMediaTypeNotSupportedException | 415 - Unsupported Media Type |
HttpMessageNotReadableException | 400 - Bad Request |
HttpMessageNotWritableException | 500 - Internal Server Error |
HttpRequestMethodNotSupportedException | 405 - Method Not Allowed |
MethodArgumentNotValidException | 400 - Bad Request |
MissingServletRequestParameterException | 400 - Bad Request |
MissingServletRequesPartException | 400 - Bad Request |
NoSuchRequestHandlingMethodException | 404 - Not Found |
TypeMismatchException | 400 - Bad Request |
有时候可以自定义一些异常,比如定义一个找不到角色异常
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "找不到角色信息异常")
public class RoleException extends RuntimeException {
// ...
}
通过注解@ResponseStatus将异常映射为某一种状态码,通过配置reason可以了解配置产生异常的原因。
- 处理方式一:通过配置专门的错误页(可以在web.xml中使用<error-page>标签进行配置)进行处理
<web-app>
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
</web-app>
- 处理方式二: 在Controller中添加一个或多个用于处理异常的方法并在方法上用@ExceptionHandler加以注解,并指明该方法可以处理的异常类型,该方法可以返回错误视图的视图名或者返回ModelAndView对象
@Controller public class RoleController { //当前控制器发生RoleException异常时,进入该方法 @ExceptionHandler(RoleException.class) public String handleRoleException(Model model, RoleException ex) { model.addAttribute("hint", ex.getMessage()); //返回指定的页面,避免不友好 return "error"; } }
- 处理方式三:多个控制器有公共的异常需要统一处理,那么可以通过@ControllerAdvice为控制器写一个处理异常的Advice
@ControllerAdvice public class ControllerExceptionAdvice { @ExceptionHandler(RoleException.class) public ModelAndView handleRE(Model model, RoleException ex) { ModelAndView mav = new ModelAndView("error"); mav.addObject("hint", ex.getMessage()); return mav; } }