Spring MVC의suspend Controller를 실행하면 Interceptor와 Controler Advice가 여러 번 실행됩니다

버전 정보
  • Spring Boot: 2.6.3
  • Spring MVC는 Kotlin의 코르크를 지원합니다.
    Controller의handler 함수에suspend를 더하면 Spring은handler 함수를 실행하기 위해 코르크를 시작합니다.
    @RestController
    class DemoController {
        @GetMapping("suspend")
        suspend fun indexSuspend(): String {
            return "suspend handler is executed."
        }
    }
    
    이렇게 handler 함수에suspend를 더하면 Controller 앞에 있는 인터셉터와 Controller Advice가 여러 번 호출됩니다.
    순서는 다음과 같다.
    현재 단계에서는 여러 번 호출되는 것 자체를 피할 수 없기 때문에 여러 번 호출돼도 문제가 없도록 인터셉터와 Controller Advice를 사전에 제작할 수밖에 없다. Interceptor의 설치 예입니다. @Component class DemoHandlerInterceptor( private val asyncHandlerUtil: AsyncHandlerUtil ) : HandlerInterceptor { override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { // 비동기 처리(Controller의 suspend fun)가 완료되면 // 후속 처리를 복원하기 위해 DispatcherServlet에 다시 할당합니다. // 이때 인터셉터(즉 두 번째로 불린 경우)로 불려가면 아무것도 하지 않는다. if (asyncHandlerUtil.isDispatchedToResume(request)) { // 가짜를 되돌리면 후속 처리 재개도 멈추기 때문에 진실에 답할 필요가 있다. return true } // 비동기 프로세싱을 시작하기 위해 (Controller의 suspend fun) //DispatcherServlet이 할당되었을 때만(즉, 처음 호출된 경우만) // 본래의 처리를 진행한다. println return true } } @Component class AsyncHandlerUtil { fun isDispatchedToResume(request: HttpServletRequest): Boolean { val manager = WebAsyncUtils.getAsyncManager(request) // WebAsyncManager.hasConclurentResult를 사용하는 경우() // 비동기 처리 완료 후 재할당 여부를 확인할 수 있습니다. // see: https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/request/async/WebAsyncManager.html return manager.hasConcurrentResult() } } Controller Advice의 구현 예입니다. @RestControllerAdvice class DemoControllerAdvice() { @ModelAttribute("myModel") fun addSomeAttributeToModel(request: HttpServletRequest): String? { // 두 번째 할당 시 마지막으로 사용한 결과. // 두 번째 설정null도 가능할 수 있음 // 지난번 결과를 사용하는 방식이 더 견고하다(여러 번 불러도 문제가 발생하지 않는다). val cached = request.getAttribute("myModelAttr") as String? if (cached != null) { return cached } // 속성 값 생성 val attributeValue = "model!!" // 두 번째로 파견될 때 결과를 Request에 저장합니다. request.setAttribute("myModelAttr", attributeValue) return attributeValue } @ExceptionHandler @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) fun handleException(e: Throwable): String { // 프로세서 (Controller의 supendfun) 에서 예외로 던질 때 //여기 패치하러 두 번째 왔어요. return "error occurred." } } 코드는 아래에 놓으세요.

    좋은 웹페이지 즐겨찾기