【问题标题】:How to handle exception in DeferredResult in Spring Boot?如何在 Spring Boot 中处理 DeferredResult 中的异常?
【发布时间】:2017-07-20 12:12:34
【问题描述】:

我有一个休息方法:

@RequestMapping(value = "wash/washHistory", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public DeferredResult<String> getWashHistory(@RequestParam(value = "sid", required = true, defaultValue = "") String sid,
            HttpServletResponse response, HttpServletRequest request,
            @RequestParam(value = "sort", defaultValue = "") String sortType,
            @RequestParam(value = "order", defaultValue = "") String order,
            @RequestParam(value = "limit", defaultValue = "") String limit,
            @RequestParam(value = "offset", defaultValue = "") String offset) {

        System.out.println("Thread: "+Thread.currentThread());
        final Integer managerId = checkSession(sid);      
        DeferredResult<String> defResult = new DeferredResult<>();
        new Thread(() -> {
                final String result = washController.getWashHistory( managerId, order, sortType, limit, offset);
                defResult.setResult(result);            
        }).start();
    return defResult;
    }

在“getWashHistory”中,我抛出了以下自定义异常:

throw new InvalidUserInputException("Wrong offset", this.getClass().getSimpleName(), "getWashHist", params);

为了处理这个异常,我使用了以下类:

@ControllerAdvice
@EnableWebMvc
public class GlobalExceptionHandler {
 @ExceptionHandler(value = InvalidUserInputException.class)
    public ResponseEntity<String> invalidUserInputExc(InvalidUserInputException e) {
          logger.log("GMoika", e.error().getClassName(), e.error().getMethodName(), e.error().getParams(), e.error().getCause());
        return ResponseEntity.
                status(HttpStatus.BAD_REQUEST).
                body(e.error().getErrorCode());  
    }
}

只要我不使用 DeferredResult,它就可以正常工作,但是当我想使用非阻塞方式时,会发生超时异常。 我找到了一种解决方法:

defResult.onTimeout(new Runnable() {
        @Override
        public void run() {
            defResult.setErrorResult("Explanation goes here.");
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); //or SC_NO_CONTENT
        }
    });

但这不是我要找的,因为我用特定的构造函数抛出了自己的异常,以便在抛出此异常的类中写入原因。 在我的 GlobalExceptionHandler 类中是否有其他可能的方法来处理 DeferredResult 中的异常?

【问题讨论】:

    标签: java spring spring-boot nonblocking


    【解决方案1】:

    DeferredResult 有方法setErrorResult 可以接受异常并根据documentation

    该值可能是 Exception 或 Throwable 在这种情况下它将是 如同处理程序引发异常一样处理。

    【讨论】:

    • 这不是我要找的,因为我的 GlobalExceptionHandler 中有一些业务逻辑,因此我需要处理来自此类的异常
    • 我相信这正是将要发生的事情。将在setErrorResult 中设置的异常将由您的GlobalExceptionHandler 处理。
    • 谢谢,但我找到了另一种方法 =)
    【解决方案2】:

    @jny 的决定在大多数情况下是正确的,但对我来说,我找到了另一种方法 在我的休息控制器中,我添加了以下代码:

    new Thread(() -> {          
                Thread.currentThread().setUncaughtExceptionHandler(new SeparateThreadsExceptionHandler(defResult));
                final String result = washController.getWashHistory(managerId, order, sortType, limit, offset);
                defResult.setResult(result);
            }).start();
    

    还有一个SeparateThreadsExceptionHandler 类:

    public class SeparateThreadsExceptionHandler implements Thread.UncaughtExceptionHandler{
        private DeferredResult<String> dr;
        public SeparateThreadsExceptionHandler(DeferredResult<String> dr){
            this.dr = dr;
        }
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            if(e instanceof InvalidUserInputException){
               InvalidUserInputException throwableException =  (InvalidUserInputException)e;
                dr.setResult(throwableException.error().getErrorCode());
            } else {
                dr.setResult(UnknownException.UNKNOWN_ERROR_CODE);
            }
        }
    
    }
    

    当我的自定义异常被抛出时,我可以将一些字符串错误消息设置为 DeferredResult。在我的示例中,它是前端的错误代码。

    【讨论】:

      【解决方案3】:

      在我的 GlobalExceptionHandler 类中是否有任何其他可能的方法来处理 DeferredResult 中的异常

      我使用@jny 指出的setErrorResult 方法

      这是我的控制器方法的主体:

      DeferredResult<Wrapper<Object>> deferredResult = new DeferredResult<>();
      
      ForkJoinPool.commonPool().submit(() -> {
          try {
              Wrapper<Object> response = ...
              deferredResult.setResult(response);
          } catch (Exception e) {
              deferredResult.setErrorResult(e);
          }
      });
      
      return deferredResult;
      

      在这种情况下,新线程中抛出的异常被传递给全局@ExceptionHandler

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-09-14
        • 2023-03-28
        • 2019-07-21
        • 1970-01-01
        • 2021-09-18
        • 2015-02-23
        • 2016-09-29
        相关资源
        最近更新 更多