SpringMVC入门之十:异常的优化处理

处理异常

在请求的过程中,错误往往是不可避免的,那么发生异常时,该给客户端什么响应呢?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;
@ControllerAdvice
public class webWideExceptionHandler{
    @ExceptionHandler(DuplicateSpittleException,class)
    public String duplicateSpittleHandler(){
      return "error/duplicate";
    }
}

现在,如果任意的控制器方法抛出了DuplicateSpittleException,不管这个方法位于哪个控制器中,都会调用这个duplicateSpittleHandler()方法来处理异常。



上一篇: SpringMVC入门之九:multipart文件上传
下一篇: SpringMVC入门之十一:跨重定向请求传递数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值