在Spring应用程序中处理异常的位置

时间:2017-07-11 12:16:23

标签: spring spring-boot exception

我还在学习Spring。我已经设置了一个应用程序,并且我试图理解Spring中的异常处理。

我使用days = [x for i, x in enumerate(days) if weather[i] <= condition] 来处理异常。在我的应用程序中,有几个层,例如@ControllerAdviceServicesControllersModels。我应该在哪个层处理异常?或者我应该在适当的时候处理每一层中的异常吗?

6 个答案:

答案 0 :(得分:5)

这是在Spring中启动异常处理的好方法:

步骤1 - 创建一个特定的DefaultExceptionHandler类,并使用 @ControllerAdvice 注释对其进行注释。在此处理程序类中,您有不同的方法,同时捕获预期意外异常,这些异常使用 @ExceptionHandler 注释进行注释:

@ControllerAdvice("com.stackoverflow.example")
@SuppressWarnings("WeakerAccess")
public class DefaultExceptionHandler extends ResponseEntityExceptionHandler {

    private final Logger log = LoggerFactory.getLogger("DefaultExceptionHandler");

    private final MessageSourceAccessor messageSource;

    @Autowired
    public DefaultExceptionHandler(MessageSourceAccessor messageSource) {
        Assert.notNull(messageSource, "messageSource must not be null");
        this.messageSource = messageSource;
     }

      @ExceptionHandler(ApplicationSpecificException.class)
      public ResponseEntity<Object> handleApplicationSpecificException(ApplicationSpecificExceptionex) {
         final Error error = buildError(ex);
         return handleExceptionInternal(ex, ex.getHttpStatus(), error);
      }

       @ExceptionHandler(Exception.class)
       public ResponseEntity<Object> handleException(Exception ex) {
           final Error error = buildError(ex);
           return handleExceptionInternal(ex, HttpStatus.INTERNAL_SERVER_ERROR, error);
    }
}

第2步 - 创建一个特定于应用程序的异常(ApplicationSpecificException类),用于预期的异常,并在任何级别抛出此异常,它将被Spring接收:

public class ApplicationSpecificException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private final ExceptionType exceptionType;

    public ApplicationSpecificException(ExceptionType exceptionType, Object... messageArguments) {
        super(MessageFormat.format(exceptionType.getMessage(), messageArguments));
        this.exceptionType = exceptionType;
    }

    public ApplicationSpecificException(ExceptionType exceptionType, final Throwable cause, Object... messageArguments) {
        super(MessageFormat.format(exceptionType.getMessage(), messageArguments), cause);
        this.exceptionType = exceptionType;
    }

    public HttpStatus getHttpStatus() {
        return exceptionType.getStatus();
    }

    public ExceptionType getExceptionType() {
        return exceptionType;
    }
}

ExceptionType为枚举:

public enum ExceptionType {

    HTTP_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "An internal server error occurred.");
    //you can specify your own exception types...

    private HttpStatus status;
    private String message;

    ExceptionType(HttpStatus status, String message) {
        this.status = status;
        this.message = message;
    }

    public HttpStatus getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }
}

第3步 - 最后,创建了一个ExceptionFactory类。这允许您在应用程序日志中自动记录异常:

public class ExceptionFactory {

    private static final Logger LOG = LoggerFactory.getLogger(ExceptionFactory.class);

    public static ApplicationSpecificException create(final Throwable cause, final ExceptionType exceptionType, final Object... messageArguments) {
        LOG.error(MessageFormat.format(exceptionType.getMessage(), messageArguments), cause);
        return new ApplicationSpecificException (exceptionType, cause, messageArguments);
    }

    public static ApplicationSpecificException create(final ExceptionType exceptionType, final Object... messageArguments) {
        LOG.error(MessageFormat.format(exceptionType.getMessage(), messageArguments));
        return new TerminologyServerException(exceptionType, messageArguments);
    }
}

第4步 - 在应用程序的任何位置,您现在都可以抛出异常,这将在应用程序日志中记录异常。由于Spring @ControllerAdvice注释,DefaultExceptionHandler抛出并拾取此异常:

throw ExceptionFactory.create(ExceptionType.INTERNAL_SERVER_ERROR);

像这样,您可以将异常处理流程作为一个跨领域的问题来应对。不会将内部服务器错误传播给最终用户,并且DefaultExceptionHandler会处理预期和意外异常。异常被分配了一个HTTP错误代码和错误消息,它将返回给客户端。

答案 1 :(得分:3)

最好使用@ControllerAdvice注释专用的类来处理所有意外问题。通过这样做,您可以防止将应用程序的内部暴露给客户端。

@ControllerAdvice
public class UncaughtExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(UncaughtExceptionHandler.class);

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public void handleAll(Exception e) {
        log.error("Unhandled exception occurred", e);
    }

}

对于预期异常(不要与已检查的异常混淆),您可能会解决发生问题的原因。某些异常可以传播或包装并重新抛出到与@ControllerAdvice一样实现的同一个全局处理程序,以保持整个逻辑专用于单个位置的异常。

答案 2 :(得分:2)

您应该尝试@ExceptionHandler注释。

您可以在此处详细了解: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

答案 3 :(得分:2)

您可以在spring应用程序中使用@ControllerAdvice作为全局异常处理程序。 Checkout this很好地解释了教程。使用@ControllerAdvice可以减少您的业务代码的混乱,然后您可以在一个单独的位置处理所有异常。 @ControllerAdvice使您遵守设计原则seperation of concerns

答案 4 :(得分:1)

使用Spring Framework可以通过三种方式处理异常,

  1. @ExceptionHandler - 基于控制器

    这个处理程序是基于控制器的,我们需要一个用@ExceptionHandler注释的方法,它接受Exception Class [你要处理的任何异常]作为参数, 如果在控制器中引发任何这些异常,则此处理程序方法将处理。

    如果我们在同一个控制器中有两个处理程序方法,比如说一个Exception处理程序和另一个RuntimeException处理程序,则会触发更接近Exception Class hirarchy的处理程序方法。 在这种情况下,抛出NullpointerException然后触发IOException处理程序方法,该方法最接近Exception类。

  2. @ControllerAdvice - 全局异常处理程序

    这用于Spring应用程序中的全局错误处理。您需要拥有的是使用@ControllerAdvice注释的类。如果在定义的控制器中引发任何异常[您可以定义此控制器建议应该监听基础包中的异常的包] 然后它由ControllerAdvice处理。

    您将在ControllerAdvice中拥有多个@ExceptionHandler,如下面的代码段所示。这个类可以是单个位置,您可以在其中处理整个应用程序的异常。

    @ControllerAdvice(basePackages = "{com.exampe.controller}")
    public class RestApiExceptionHandlerAdvice {
    
        /** Handling Business exception */  
    
        @ExceptionHandler(value = BadRequestException.class)
        public ErrorMessage handleBadRequest(BadRequestException exception) {
            //code...
            return errMsg;
        }
    
        @ExceptionHandler(value = GatewayTimeoutException.class)
        public ErrorMessage handleGatewayTimeout(GatewayTimeoutException exception) {
            //code...
            return errMsg;
        }
    
    }
    
  3. HandlerExceptionResolver

    通过以上两种方法,我们大多数时候都使用静态页面。在这种情况下,我们可以将不同的视图返回到不同的异常。 我们可以在spring.xml中使用MySimpleMappingExceptionResolver进行配置,您可以指定需要为哪个异常呈现哪个视图。

答案 5 :(得分:0)

我认为接受的答案很好。它使用Spring @ControllerAdvice进行全局异常处理,也使用枚举来组合错误代码和HTTP状态。

但是,我不同意Exception工厂部分。工厂记录该异常并将其抛出。在thisthis之类的许多文章中不建议这样做。当然,如果您没有类似的任何异常翻译,那应该没问题,因为该异常只会记录一次。但是您只是无法确定将来是否不需要翻译。

我建议使用@ControllerAdvice进行日志记录,因为这是应用程序异常的最终目的地。这样可以确保每个异常都将被记录一次,并且可以很好地呈现给客户端。