【问题标题】:Throw Exception after Try Catch blockTry Catch 块后抛出异常
【发布时间】:2020-06-10 10:41:39
【问题描述】:

我在我的 Spring 应用程序中使用了一个外部 SOAP 服务。 SOAP 服务在连接时会抛出异常,所以我在 try catch 块中捕获它:

try {
    response = soapService.callService(request);
} catch (Exception e){
    throw SoapServiceException("explaining",e);
}

if (BAD_STATUS_CODE.equals(response.getStatusCode()) {
    throw CustomException("explaining", e);
}

return response;

如您所见,我还想在响应不是调用者所期望的结果时抛出异常,例如,可能是因为请求格式有效但在 soap 服务中找不到值。

这是一个好习惯吗?我试图防止将响应对象包装在另一个保留错误代码的对象中。我认为如果服务调用者处理肥皂异常或响应异常等异常情况会很好。谢谢你的建议。

【问题讨论】:

    标签: java exception


    【解决方案1】:

    异常是 java 编程语言的一个特性,但不是必须使用的。好吧,您可能必须抓住现有的,但您不必提出新的。

    这是您的应用程序/框架/库的设计决策。如果处理错误的方式在整个代码库中通常是一致的,那就更好了。没有更好的通用方法。这是一个集体选择,也取决于您的程序的一般预期行为。

    简单的设计

    “幼稚”或至少简单的实现通常会在许多不受支持的情况下引发异常,因为这是中断执行、获取错误日志和调查的简单方法。

    以这种方式看待事物,不一定值得检查异常。您的异常可以从 RuntimeException 派生,您无需声明它们并在出现问题时简单地提出它们。

    这种设计的缺点是开发人员可能会在许多情况下引发异常,并且大多数这些“异常”情况实际上是您的应用程序中的常见情况。找不到文件?嗯,这很常见。网络不可用?这是可以预料的。该服务没有返回您所期望的?这种情况发生的频率可能比您想象的要多...

    至少在我们拥有数千台机器和十万 TPS 的生产中,一切都会发生。

    实现更强大的实现

    因此,通常您会将您认为会发生且必须处理的事情(例如网络请求超时或数据库暂时不可用)或永远不会发生的事情(文件部分工件分发丢失)或提供给函数的参数无效(代码错误)。

    您将保留真正异常的异常,并且通常将它们设为 RuntimeException/unchecked,因为没有什么可做的。您只想记录和报告此类错误。

    对于所有其余部分,中间可能存在异常,但不是全局异常。您想处理它们并将它们视为正常行为。

    在这种情况下,选择取决于您的设计。

    但通常情况下,如果我必须在发生这种情况时正常采取行动并继续进行,我也希望没有例外。通常对于网络请求,我会认为 Time Out 实际上是一个有效的响应,并作为我在业务域中建模的值的一部分返回。

    让我的数据域中的错误和错误代码部分允许我累积/聚合错误并微调我对它们的反应方式。相反的例外更多的是全有或全无。

    这让我在选择如何反应时更加灵活...就像我执行了 10 个请求,一次超时,返回错误时,我不想要异常。我想要一个取决于我的应用程序功能方面的结果和“合并”策略。

    在您的 BadStatus 示例中,如果这是我得到的错误代码,我会将其视为异常,因为我提供了无效的输入(我的代码中的错误)。但如果那是因为没有网络或因为有外部故障,那对我来说是预期的行为,所以我不会抛出。

    这是我的设计选择,这是我一直与设计合作的团队的选择。这不一定是普遍的选择。只要确保你们都同意如何处理这种情况,并且它与你的整体软件设计相匹配

    已检查的异常

    他们强迫你处理它,这可能是祝福或诅咒取决于上下文。

    这是您在设计中的选择。

    您是否只在不应该发生的异常情况下抛出异常并将其用作错误检测机制?然后我认为它们没用,当我使用这种策略时,我将它们包装在 RuntimeException 的派生中以简化我的代码,并且在我的代码的几个关键区域中,我有通用的 catch 所有可以被视为框架 lvl 的机制,所以我确保我总是返回一个正确的响应(即使那个有错误),然后我可以集中记录。

    但是,如果您认为客户端总是要处理它,请使用已检查的异常。这样做的问题是,大多数情况下,调用者不能直接做任何事情,他可能需要包装到许多中间来转发它或拥有一个已检查异常的无句柄列表。

    所以我不是那么喜欢他们。但对于您和团队来说,这又是一个设计决策。

    【讨论】:

    • 非常有帮助的答案!最后,它帮助我更多地了解如何管理特殊情况。我忘了提到这个将被消费的服务很关键,如果这个服务没有按预期工作,消费者就不能正常继续。现在,我知道在这种情况下,可以抛出已检查的异常,以便客户端管理异常情况。
    【解决方案2】:

    当行为出乎意料时在代码中抛出异常绝对没问题。请务必标记您的方法

    void testMethod() throws CustomException, CustomException2, ..
    

    以便方法的调用者知道有可能抛出异常。 未捕获的异常将导致您的程序终止,因此请务必捕获并正确处理。

    【讨论】:

      【解决方案3】:
      [HttpDelete]
              [Route("api/employee/delete")]
              public void Delete(int Id)
              {
                  using (EmployeeDBEntities db = new EmployeeDBEntities())
                  {
                      try
                      {
                          Employee emp = db.Employees.FirstOrDefault(e => e.EmpId == Id);
                          db.Employees.Remove(emp);
                          db.SaveChanges();
                      }
                      catch (Exception ex)
                      {
                          throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest)
                          {
                              Content = new StringContent(ex.Message)
                          });
                      }
                  }
              }
      

      【讨论】:

        【解决方案4】:

        在我一直在工作的所有项目中,我们都像您一样使用,我们抛出异常而不是将其包装在任何响应对象中。看起来很适合我。

        【讨论】:

          猜你喜欢
          • 2011-03-18
          • 1970-01-01
          • 2012-02-20
          • 2020-12-16
          • 2011-03-25
          • 1970-01-01
          • 1970-01-01
          • 2021-11-17
          • 1970-01-01
          相关资源
          最近更新 更多