Đặt mức độ ưu tiên của nhiều @ControllerAdvice @ExceptionHandlers


82

Tôi có các lớp đa lớp được chú thích @ControllerAdvice, mỗi lớp có một @ExceptionHandlerphương thức trong.

Một xử lý Exceptionvới ý định rằng nếu không tìm thấy trình xử lý cụ thể hơn, điều này sẽ được sử dụng.

Đáng buồn thay, Spring MVC dường như luôn sử dụng trường hợp chung chung nhất ( Exception) thay vì các trường hợp cụ thể hơn ( IOExceptionví dụ).

Đây có phải là cách người ta mong đợi Spring MVC sẽ hoạt động không? Tôi đang cố gắng mô phỏng một mẫu từ Jersey, đánh giá từng ExceptionMapper(thành phần tương đương) để xác định loại đã khai báo mà nó xử lý so với ngoại lệ đã được ném ra bao xa và luôn sử dụng tổ tiên gần nhất.

Câu trả lời:


124

Đây có phải là cách người ta mong đợi Spring MVC sẽ hoạt động không?

Kể từ Spring 4.3.7, đây là cách Spring MVC hoạt động: nó sử dụng các HandlerExceptionResolverphiên bản để xử lý các ngoại lệ do các phương thức xử lý ném ra.

Theo mặc định, cấu hình MVC web đăng ký một HandlerExceptionResolverbean duy nhất , a HandlerExceptionResolverComposite,

đại biểu cho một danh sách khác HandlerExceptionResolvers.

Những trình giải quyết khác là

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

đã đăng ký theo thứ tự đó. Đối với mục đích của câu hỏi này, chúng tôi chỉ quan tâm đến ExceptionHandlerExceptionResolver.

An AbstractHandlerMethodExceptionResolvergiải quyết các ngoại lệ thông qua @ExceptionHandlercác phương thức.

Tại khởi bối cảnh, mùa xuân sẽ tạo ra một ControllerAdviceBeancho mỗi @ControllerAdvicelớp chú thích nó phát hiện. Các ExceptionHandlerExceptionResolversẽ lấy những từ ngữ cảnh, và sắp xếp chúng bằng cách sử dụng AnnotationAwareOrderComparator

là một phần mở rộng OrderComparatorhỗ trợ Ordered giao diện của Spring cũng như @Order@Prioritycác chú thích, với giá trị thứ tự được cung cấp bởi một phiên bản Đã đặt hàng ghi đè giá trị chú thích được xác định tĩnh (nếu có).

Sau đó, nó sẽ đăng ký một ExceptionHandlerMethodResolvercho mỗi ControllerAdviceBeantrường hợp này (ánh xạ @ExceptionHandlercác phương thức có sẵn với các loại ngoại lệ mà chúng có ý định xử lý). Chúng cuối cùng được thêm theo cùng một thứ tự vào a LinkedHashMap(bảo toàn thứ tự lặp lại).

Khi một ngoại lệ xảy ra, hàm ExceptionHandlerExceptionResolversẽ lặp qua những cái này ExceptionHandlerMethodResolvervà sử dụng cái đầu tiên có thể xử lý ngoại lệ.

Vì vậy, vấn đề ở đây là: nếu bạn có một @ControllerAdvicevới @ExceptionHandlerfor Exceptionđược đăng ký trước một @ControllerAdvicelớp khác với một @ExceptionHandlerngoại lệ cụ thể hơn, chẳng hạn như IOException, cái đầu tiên đó sẽ được gọi. Như đã đề cập trước đó, bạn có thể kiểm soát thứ tự đăng ký đó bằng cách @ControllerAdvicetriển khai lớp chú thích của bạn Orderedhoặc chú thích nó bằng @Orderhoặc @Priorityvà đặt cho nó một giá trị thích hợp.


5
Hơn nữa, trong trường hợp có nhiều @ExceptionHandlerphương thức trong a @ControllerAdvice, phương thức xử lý lớp cha cụ thể nhất của ngoại lệ đã ném được chọn.
Vijay Aggarwal

Trong khởi động mùa xuân 2.3.3, nó không yêu cầu chú thích @Order trên một lớp con ghi đè một lời khuyên bộ điều khiển Phương thức ExceptionHandler từ một lớp tư vấn bộ điều khiển mẹ
Vadiraj Purohit

92

Sotirios Delimanolis đã rất hữu ích trong câu trả lời của mình, khi điều tra thêm, chúng tôi thấy rằng, dù sao vào mùa xuân 3.2.4, mã tìm kiếm chú thích @ControllerAdvice cũng kiểm tra sự hiện diện của chú thích @Order và sắp xếp danh sách ControllerAdviceBeans.

Thứ tự mặc định kết quả cho tất cả các bộ điều khiển không có chú thích @Order là Thứ tự # LOWEST_PRECEDENCE có nghĩa là nếu bạn có một bộ điều khiển cần có mức ưu tiên thấp nhất thì TẤT CẢ các bộ điều khiển của bạn cần có thứ tự cao hơn.

Dưới đây là một ví dụ cho thấy cách có hai lớp xử lý ngoại lệ với chú thích ControllerAdvice và Order có thể cung cấp các phản hồi thích hợp khi một UserProfileException hoặc RuntimeException xảy ra.

class UserProfileException extends RuntimeException {
}

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
class UserProfileExceptionHandler {
    @ExceptionHandler(UserProfileException)
    @ResponseBody
    ResponseEntity<ErrorResponse> handleUserProfileException() {
        ....
    }
}

@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
class DefaultExceptionHandler {

    @ExceptionHandler(RuntimeException)
    @ResponseBody
    ResponseEntity<ErrorResponse> handleRuntimeException() {
        ....
    }
}
  • Xem ControllerAdviceBean # initOrderFromBeanType ()
  • Xem ControllerAdviceBean # findAnnotatedBeans ()
  • Xem ExceptionHandlerExceptionResolver # initExceptionHandlerAdviceCache ()

Thưởng thức!


21

Thứ tự của các trình xử lý ngoại lệ có thể được thay đổi bằng cách sử dụng @Orderchú thích.

Ví dụ:

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomExceptionHandler {

    //...

}

@OrderGiá trị của có thể là bất kỳ số nguyên nào.


5

Tôi cũng tìm thấy trong tài liệu rằng:

https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html#getExceptionHandlerMethod-org.springframework. web.method.HandlerMethod-java.lang.Exception-

ExceptionHandlerMethod

được bảo vệ ServletInvocableHandlerMethod getExceptionHandlerMethod (HandlerMethod handlerMethod, ngoại lệ Exception)

Tìm phương thức @ExceptionHandler cho ngoại lệ đã cho. Việc triển khai mặc định sẽ tìm kiếm các phương thức trong phân cấp lớp của bộ điều khiển trước tiên và nếu không tìm thấy, nó sẽ tiếp tục tìm kiếm các phương thức @ExceptionHandler bổ sung giả sử một số bean do @ControllerAdvice Spring quản lý được phát hiện . Tham số: handlerMethod - phương thức mà ngoại lệ được đưa ra (có thể là null) ngoại lệ - ngoại lệ được nâng lên Trả về: một phương thức để xử lý ngoại lệ hoặc null

Vì vậy, điều này có nghĩa là nếu bạn muốn giải quyết vấn đề này, bạn sẽ cần thêm trình xử lý ngoại lệ cụ thể của mình trong bộ điều khiển ném các ngoại lệ đó. ANd để xác định một và chỉ ControllerAdvice xử lý trình xử lý ngoại lệ mặc định chung.

Quá trình này đơn giản hóa quá trình và chúng tôi không cần chú thích Đơn hàng để xử lý sự cố.


2

Có một tình huống tương tự được đề cập trong bài đăng xuất sắc " Xử lý ngoại lệ trong Spring MVC " trên blog Spring, trong phần có tên Xử lý ngoại lệ toàn cầu . Kịch bản của chúng liên quan đến việc kiểm tra các chú thích ResponseStatus đã đăng ký trên lớp ngoại lệ và nếu có, hãy ném lại ngoại lệ để khung xử lý chúng. Bạn có thể sử dụng chiến thuật chung này - cố gắng xác định xem có thể có một trình xử lý thích hợp hơn ở đó hay không và ném lại.

Ngoài ra, có một số chiến lược xử lý ngoại lệ khác được đề cập mà bạn có thể xem xét thay thế.


1

Lớp quan trọng cần xử lý:

**@Order(Ordered.HIGHEST_PRECEDENCE)**
public class FunctionalResponseEntityExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(FunctionalResponseEntityExceptionHandler.class);

    @ExceptionHandler(EntityNotFoundException.class)
    public final ResponseEntity<Object> handleFunctionalExceptions(EntityNotFoundException ex, WebRequest request)
    {
        logger.error(ex.getMessage() + " " + ex);
        ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.getMessage(),
                request.getDescription(false),HttpStatus.NOT_FOUND.toString());
        return new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
    }
}

Các trường hợp ngoại lệ khác có mức độ ưu tiên thấp

@ControllerAdvice
    public class GlobalResponseEntityExceptionHandler extends ResponseEntityExceptionHandler
    {
    private final Logger logger = LoggerFactory.getLogger(GlobalResponseEntityExceptionHandler.class);
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request)
    {
        logger.error(ex.getMessage()+ " " + ex);
        ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.toString(),
                request.getDescription(false),HttpStatus.INTERNAL_SERVER_ERROR.toString());
    }
    }

0

bạn cũng có thể sử dụng một giá trị số, như bên dưới

@Order(value = 100)

Giá trị thấp hơn có mức độ ưu tiên cao hơn. Giá trị mặc định là * {@code Ordered.LOWEST_PRECEDENCE}, cho biết mức độ ưu tiên thấp nhất (thua bất kỳ giá trị đặt hàng được chỉ định nào khác *)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.