7.3.2 编写异常处理的方法

在很多的场景下,将异常映射为状态码是很简单的方案,并且就功能来说也足够了。但是如果我们想在响应中不仅要包括状态码,还要包含所产生的错误,那该怎么办呢?此时的话,我们就不能将异常视为 HTTP 错误了,而是要按照处理请求的方式来处理异常了。

作为样例,假设用户试图创建的 Spittle 与已创建的 Spittle 文本完全相同,那么 SpittleRepository 的 save() 方法将会抛出 DuplicateSpittle Exception 异常。这意味着 SpittleController 的 saveSpittle() 方法可能需要处理这个异常。如下面的程序清单所示,saveSpittle() 方法可以直接处理这个异常。

程序清单 7.9 在处理请求的方法中直接处理异常
@RequestMapping(method=RequestMethod.POST)
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";
  }
}

程序清单 7.9 中并没有特别之处,它只是在 Java 中处理异常的基本样例,除此之外,也就没什么了。

它运行起来没什么问题,但是这个方法有些复杂。该方法可以有两个路径,每个路径会有不同的输出。如果能让 saveSpittle() 方法只关注正确的路径,而让其他方法处理异常的话,那么它就能简单一些。

首先,让我们首先将 saveSpittle() 方法中的异常处理方法剥离掉:

@RequestMapping(method=RequestMethod.POST)
public String saveSpittle(SpittleForm form, Model model) {
  spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), 
    form.getLongitude(), form.getLatitude()));
  return "redirect:/spittles";
}

可以看到,saveSpittle() 方法简单了许多。因为它只关注成功保存 Spittle 的情况,所以只有一个执行路径,很容易理解(和测试)。

现在,我们为 SpittleController 添加一个新的方法,它会处理抛出 Duplicate-SpittleException 的情况:

@ExceptionHandler(DuplicateSpittleException.class)
public String handleDuplicateSpittle() {
  return "error/duplicate";
}

handleDuplicateSpittle() 方法上添加了 @ExceptionHandler 注解,当抛出 DuplicateSpittleException 异常的时候,将会委托该方法来处理。它返回的是一个 String,这与处理请求的方法是一致的,指定了要渲染的逻辑视图名,它能够告诉用户他们正在试图创建一条重复的条目。

对于 @ExceptionHandler 注解标注的方法来说,比较有意思的一点在于它能处理同一个控制器中所有处理器方法所抛出的异常。所以,尽管我们从save-Spittle() 中抽取代码创建了 handleDuplicateSpittle() 方法,但是它能够处理 SpittleController 中所有方法所抛出的 DuplicateSpittleException 异常。我们不用在每一个可能抛出 DuplicateSpittleException 的方法中添加异常处理代码,这一个方法就涵盖了所有的功能。

既然 @ExceptionHandler 注解所标注的方法能够处理同一个控制器类中所有处理器方法的异常,那么你可能会问有没有一种方法能够处理所有控制器中处理器方法所抛出的异常呢。从 Spring 3.2 开始,这肯定是能够实现的,我们只需将其定义到控制器通知类中即可。

什么是控制器通知方法?很高兴你会问这样的问题,因为这就是我们下面要讲的内容。

Last updated