处理异常
在请求的过程中,错误往往是不可避免的,那么发生异常时,该给客户端什么响应呢?Servlet 请求的输出和输入均是一个 Servlet 响应。因此,异常必须要以某种方式转换为响应。Spring 提供多种方式将异常转换为响应:
a、特定的 Spring 异常将会自动映射为指定的 HTTP 状态码。 b、异常可以添加 @ResponseStatus 注解,从而将其映射为某一个 HTTP 状态码; c、在方法上可以添加 @ExceptionHandler 注解,使其用来处理异常。将异常映射为 HTTP 状态码
Spring 提供了一种机制,能够通过 @ResponseStatus 注解将异常映射为 HTTP 状态码。实例如下;
在 SpittleController 中通常会有一个根据 ID 查询用户的处理器方法,如下:
- @RequestMapping(value = "/{spittleId}", method = GET) public String spittle(@PathVariable("spittleId") long spittleId, Model model) {
- Spittle spittle = spittleRepository.findOne(spittleId);
- if (spittle == null) {
- throw new SpittleNotFoundException();
- }
- model.addAttribute(spittle);
- return "spittle";
- }
需要注意的是当 spittle 为空的时候,会抛出一个 SpittleNotFoundException() 异常,这个异常是我们自定义的异常:
- package spittr.exception;
- public class SpittleNotFoundException extends RuntimeException {}
如果调用 spittle() 方法来处理请求,并且给定 ID 获取到的结果为空,那么 SpittleNotFoundException(默认)将会产生 500 状态码(Internal Server Error)的响应。实际上,如果出现任何没有映射的异常,响应都会带有 500 状态码,但是,我们可以通过映射 SpittleNotFoundException 对这种默认行为进行变更。
当抛出 SpittleNotFoundException 异常时,这是一种请求资源没有找到的场景。如果资源没有找到的话,HTTP 状态码 404 是最为精确的响应状态码。所以,我们要使用 @ResponseStatus 注解将 SpittleNotFoundException 映射为 HTTP 状态码 404。
- package spittr.exception;
- import org.springframework.http.HttpStatus;
- import org.springframework.web.bind.annotation.ResponseStatus;@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Spittle Not Found") public class SpittleNotFoundException extends RuntimeException {}
在引入 @ResponseStatus 注解之后,如果控制器方法抛出 SpittleNotFoundException 异常的话,响应将会具有 404 状态码,这是因为 SpittleNotFound。
编写异常处理的方法
假设有这样一个场景,用户视图创建的 Spittle 与已创建的 Spittle 文本完全相同,那么 SpittleRepository 的 save() 方法将会抛出 DuplicateSpittle Exception 异常。这意味着 SpittleController 的 saveSpittle() 方法可能需要处理这个异常。如下的程序清单处理了这个异常:
- public String saveSpittle(SpittleForm form, Model model) {
- try {
- spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude()));
- return "redirect:/spittles";
- } catch(DuplicateSpittleException e) {
- return "error/duplicate";
- }
- }
这是 java 中处理异常的样例,但是这样让代码看起来很冗杂,并且让方法多了一个路径。如果能让 saveSpittle() 方法之关注正确的路径,而让其他方法处理异常的话,那么它就能简单一些。
现在我们想将 saveSpittle() 方法中的异常处理方法剥离掉:
- public String saveSpittle(SpittleForm form, Model model) {
- spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude()));
- return "redirect:/spittles";
- }
这样,该方法就可以只关注正确的逻辑。现在,我们为 SpittleController 添加一个新的方法,它会处理抛出 DuplicateSpittleException 的情况:
- @ExceptionHandler(DuplicateSpittleException.class) public String handleDuplicateSpittle() {
- return "error/duplicate";
- }
当该控制器中的任意一个方法抛出了 DuplicateSpittleException 后,都会委托该方法来处理。我们不用在每一个可能抛出该异常的地方添加异常处理方法,这样无疑能省去不少的冗余代码,我们的代码看起来也会更加整洁。
那么,更进一步,既然已经有覆盖一个控制器的公用异常处理方法了,那有没有覆盖所有控制器的异常处理方法呢?答案是肯定的,我们只需要将其定义到控制器通知类中即可。
为控制器添加通知
如果控制器的特定切面能够运用到整个应用程序的所有控制器中,那么这将会便利很多。控制器通知(Controller advice)是任意带有 @ControllerAdvice 注解的类,这个类会包含一个或多个如下类型的方法:
a、@ExceptionHandler 注解标注的方法;
b、@InitBinder 注解标注的方法;
c、@ModelAttribute 注解标注的方法。
在带有 @ControllerAdvice 注解的类中,以上所述的这些方法会运用到整个应用程序所有控制器中带有 @RequestMapping 注解的方法上。@ControllerAdvice 本身已经使用了 @Component,一次可以被自动扫描获取到。
@ControllerAdvice 最为实用的一个场景就是将所有的 @ExceptionHandler 方法收集到一个类中,这样所有控制器的异常就能在一个地方进行一致的处理。如下程序 1 清单所示:
- package spitter.web;
- import rog.springframework.web.bind.annotation.ControllerAdvice;@ControllerAdvicepublic class webWideExceptionHandler {@ExceptionHandler(DuplicateSpittleException, class) public String duplicateSpittleHandler() {
- return "error/duplicate";
- }
- }
现在,如果任意的控制器方法抛出了 DuplicateSpittleException,不管这个方法位于哪个控制器中,都会调用这个 duplicateSpittleHandler() 方法来处理异常。
就爱阅读 www.92to.com 网友整理上传, 为您提供最全的知识大全, 期待您的分享,转载请注明出处。
来源: http://www.92to.com/bangong/2017/02-27/17743641.html