我还在学习Spring。我已经设置了一个应用程序,并且我试图了解Spring中的异常处理。
我@ControllerAdvice用来处理异常。在我的应用程序有几层如Services,Controllers,Models和Repositories。我应该在哪一层处理异常?还是应该在每一层中适当地处理异常?
@ControllerAdvice
Services
Controllers
Models
Repositories
这是在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错误代码和错误消息,这些错误代码和错误消息将返回给客户端。