Эта статья задержалась очень долго. В предыдущей статье «Как правильно использовать перечисления в Java, это не так просто!» я упомянул, что собираюсь поделиться. Только после того, как один из читателей напомнил мне об этом, я обнаружил, что забыл опубликовать её на канале. В этом месте я обнаружил, что у меня есть большая проблема: иногда я говорю, что обновлю что-то, но потом забываю об этом. Часто это не потому, что я не написал, а потому что забываю из-за множества других дел. В будущем я постараюсь исправить эту проблему!
В предыдущей статье «Несколько способов обработки исключений в Spring Boot» я представил:
@ControllerAdvice
и @ExceptionHandler
для обработки глобальных исключений@ExceptionHandler
для обработки исключений на уровне контроллераResponseStatusException
Через эту статью можно понять, как обрабатывать исключения в Spring Boot. Однако, просто знать, как это сделать, недостаточно. Нам также нужно подумать, как сделать код обработки исключений более элегантным. Ниже я расскажу о способах обработки исключений, которые я узнал из реальных проектов.Ниже приведено только мое личное мнение. Если у вас есть лучшие решения или предложения по улучшению предложенного мной подхода, пожалуйста, оставьте свои комментарии в разделе для обсуждения.
Сначала я покажу конечный результат. Когда определенное исключение поймано системой и возвращено клиенту, информация выглядит следующим образом:
Возвращенная информация включает в себя 5 частей:
Такой способ возврата информации об исключениях позволяет фронтенду лучше отображать ошибки.
ErrorCode.java
(этот перечисление содержит уникальные идентификаторы исключения, HTTP-статусные коды и ошибки)Основная роль этого класса — унифицированное управление исключениями, которые могут возникнуть. Однако, когда система становится слишком сложной, а количество исключений увеличивается, этот класс может стать слишком большим. Одним из решений может быть объединение похожих исключений в один, например, объединение исключения "пользователь не найден" и исключения "информация о заказе не найдена" в одно исключение "ресурс не найден", а затем фронтенд может обрабатывать его более детально в зависимости от конкретной ситуации (это моя личная методика обработки, и я не гарантирую, что это оптимальное решение).
import org.springframework.http.HttpStatus;
public enum ErrorCode {
RESOURCE_NOT_FOUND(1001, HttpStatus.NOT_FOUND, "Ресурс не найден"),
REQUEST_VALIDATION_FAILED(1002, HttpStatus.BAD_REQUEST, "Ошибка валидации запроса");
}
```## Класс `ErrorReponse`
```java
public ErrorReponse(int code, int status, String message, String path, Map<String, Object> data) {
this.code = code;
this.status = status;
this.message = message;
this.path = path;
this.timestamp = Instant.now();
if (!ObjectUtils.isEmpty(data)) {
this.data.putAll(data);
}
}
@Override
public String toString() {
return "ErrorReponse{" +
"code=" + code +
", status=" + status +
", message='" + message + '\'' +
", path='" + path + '\'' +
", timestamp=" + timestamp +
", data=" + data +
'}';
}
BaseException
Абстрактный класс, который является родительским для всех исключений в системе.```java public abstract class BaseException extends RuntimeException { private final ErrorCode error; private final HashMap<String, Object> data = new HashMap<>();
public BaseException(ErrorCode error, Map<String, Object> data) {
super(error.getMessage());
this.error = error;
if (!ObjectUtils.isEmpty(data)) {
this.data.putAll(data);
}
}
protected BaseException(ErrorCode error, Map<String, Object> data, Throwable cause) {
super(error.getMessage(), cause);
this.error = error;
if (!ObjectUtils.isEmpty(data)) {
this.data.putAll(data);
}
}
public ErrorCode getError() {
return error;
}
public Map<String, Object> getData() {
return data;
}
}
### Класс `ResourceNotFoundException`
Пользовательский класс исключения, который наследуется от `BaseException`.
```java
public class ResourceNotFoundException extends BaseException {
public ResourceNotFoundException(Map<String, Object> data) {
super(ErrorCode.RESOURCE_NOT_FOUND, data);
}
}
GlobalExceptionHandler
Глобальный обработчик исключений, который определяет два метода обработки исключений.
// Определение метода обработки исключений
public class GlobalExceptionHandler {
// Обработка исключения
public ResponseEntity<ErrorResponse> handleAppException(AppException ex, WebRequest request) {
// Логика обработки исключения
}
// Обработка исключения
public ResponseEntity<ErrorResponse> handleException(Exception ex, WebRequest request) {
// Логика обработки исключения
}
}
```В этом классе метод `handleAppException()` достаточно для обработки всех исключений, так как все исключения в системе наследуются от `BaseException`.
```java
import com.twuc.webApp.web.ExceptionController;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
```@ControllerAdvice(assignableTypes = {ExceptionController.class})
@ResponseBody
public class GlobalExceptionHandler {
// Также можно заменить BaseException на RuntimeException
// Поскольку RuntimeException является родительским классом для BaseException
@ExceptionHandler(BaseException.class)
public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
ErrorReponse representation = new ErrorReponse(ex, request.getRequestURI());
return new ResponseEntity<>(representation, new HttpHeaders(), ex.getError().getStatus());
}
@ExceptionHandler(value = ResourceNotFoundException.class)
public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
ErrorReponse errorReponse = new ErrorReponse(ex, request.getRequestURI());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorReponse);
}
}
(Важно) дополнительный вопрос:
Ха-ха! На самом деле я добавил дополнительный метод обработки исключений handleResourceNotFoundException()
, чтобы проверить, какой из методов будет обрабатывать исключение ResourceNotFoundException
.
Ответ:
Исключение ResourceNotFoundException
будет обработано методом handleResourceNotFoundException()
. В процессе обработки исключений с помощью @ExceptionHandler
, Spring ищет наиболее подходящий метод обработки исключения.
Вот краткий анализ исходного кода:
В данном коде представлен класс GlobalExceptionHandler
, который используется для глобального обработчика исключений. Он содержит два метода обработки исключений: handleAppException()
и handleResourceNotFoundException()
. Метод handleAppException()
обрабатывает исключения типа BaseException
, а метод handleResourceNotFoundException()
обрабатывает исключения типа ResourceNotFoundException
.``````markdown
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
// Находит все异常类型,mappedMethods 中存放了异常和处理异常的方法的对应关系
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
// 不为空说明有方法处理异常
if (!matches.isEmpty()) {
// 按照匹配程度从小到大排序
matches.sort(new ExceptionDepthComparator(exceptionType));
// 返回处理异常的方法
return this.mappedMethods.get(matches.get(0));
}
else {
return null;
}
}
```Из исходного кода следует: **`getMappedMethod()` сначала находит все методы, которые могут соответствовать обработке исключения, затем сортирует их по возрастанию, и в конце выбирает метод с наименьшим значением (то есть наиболее подходящий).**``
Из исходного кода следует: **`getMappedMethod()` сначала находит все методы, которые могут соответствовать обработке исключения, затем сортирует их по возрастанию, и в конце выбирает метод с наименьшим значением (то есть наиболее подходящий).**## Напишите класс для тестирования выбрасывания исключения
**`Person.java`**
```java
public class Person {
private Long id;
private String name;
// Пропущены методы getter и setter
}
ExceptionController.java
(класс, выбрасывающий исключение)
@RestController
@RequestMapping("/api")
public class ExceptionController {
@GetMapping("/resourceNotFound")
public void throwException() {
Person p = new Person(1L, "SnailClimb");
throw new ResourceNotFoundException(ImmutableMap.of("person id:", p.getId()));
}
}
Исходный код доступен по адресу: https://github.com/Snailclimb/springboot-guide/tree/master/source-code/basis/springboot-handle-exception-improved
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )