본문 바로가기

개발/Java

Spring의 HandlerInterceptorAdapter는 왜 deprecated되었을까?

찾아보게 된 계기


Spring의 Interceptor 사용 방법을 찾기 위해 Spring 공식 문서를 찾아보고 있었다.

공식 문서에는 아래와 같이 HandlerInterceptorAdapter를 상속받으면 된다고 적혀 있다.

public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {

    private int openingTime;
    private int closingTime;

    public void setOpeningTime(int openingTime) {
        this.openingTime = openingTime;
    }

    public void setClosingTime(int closingTime) {
        this.closingTime = closingTime;
    }

    public boolean preHandle(
            HttpServletRequest request,
            HttpServletResponse response,
            Object handler) throws Exception {

        Calendar cal = Calendar.getInstance();
        int hour = cal.get(HOUR_OF_DAY);
        if (openingTime <= hour && hour < closingTime) {
            return true;
        } else {
            response.sendRedirect("http://host.com/outsideOfficeHours.html");
            return false;
        }
    }
}

 

 

공식 문서를 따라 실제로 코드에 적용해 보니 강렬한 취소선과 함께 deprecated 경고가 발생한다.

'org.springframework.web.servlet.handler.HandlerInterceptorAdapter' is deprecated

 

 

이상하다 싶어서 읽었던 Spring 문서의 버전을 살펴보니, 앗차! 3.2.x버전의 문서(https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-handlermapping-interceptor)였다.

 

17. Web MVC framework

@RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, Model model, BindingResult result) { … } Note, that there is a Model parameter in between Pet and BindingResult. To get this working you have to reor

docs.spring.io

 

 

내가 사용중인 5.3.x버전에서는 HandlerInterceptorAdapter 대신 HandlerInterceptor를 바로 구현하면 된다고 한다.

as of 5.3 in favor of implementing HandlerInterceptor and/or AsyncHandlerInterceptor directly.

 

 

그런데, 여기서 궁금증이 들었다. HandlerInterceptorAdapter는 왜 deprecated 되었을까? 조금 더 살펴보기로 했다

 

HandlerInterceptorAdapter가 존재했던 이유


우선  HandlerInterceptorAdapter의 존재 이유가 궁금했다. 내가 구현하고 싶은 것은 Interceptor인데, 어째서 Adapter라는 정체불명(?)의 클래스를 상속받아야 하는 것일까?

 

 

이는 사실 Spring 3.2.x에 사용된 Java 버전과 관련이 있다. Spring 3.2.x는 Java 8 미만을 기준으로 구현되어 있으므로, Java 8에 새로 도입된 기능들을 사용할 수 없다. 즉 Interface의 default 메서드를 사용할 수 없다.

Beyond that, Spring 3.2.x simply doesn't include any dedicated Java 8 support: i.e. no JSR-310 date-time, no parameter discovery via 
-parameters
, etc. If you don't intend to rely on those features initially, that's by no means a showstopper. I would just strongly recommend an upgrade to Spring 4.x as soon as you intend to embrace Java 8 more fully. For the time being, using Spring 3.2.13 on Java 8 will get you quite far and is entirely safe for your purposes.

https://github.com/spring-projects/spring-framework/issues/16279

 

 

💡 Java 8에 도입된 기능들과 default 메서드에 대해서는 자세하게 설명된 글이 많으므로 구글링하면 금방 찾을 수 있다!

 

 

HandlerInterceptor는 Interface이다. Java 8 미만에서는 default 메서드를 지원하지 않으므로 Interface를 구현하기 위해서는 해당 Interface의 메서드를 모두 Override하여 구현해야 한다.

내가 사용하고 싶은 건 preHandle()뿐인데, 사용하지 않는 postHandle()까지 작성해야 하는 것이다.

default 메서드 없는 HandlerInterceptor를 구현하면, 얄짤없이 빨간줄이 그어진다

 

 

이러한 번거로움을 피하기 위해, Spring에서는 디자인패턴의 Adapter 패턴을 차용해 HandlerInterceptorAdapter를 제공해 주는데, 이를 상속받아 구현하면 내가 필요한 메서드만 Override하여 사용할 수 있다

Abstract adapter class for the AsyncHandlerInterceptor interface, for simplified implementation of pre-only/post-only interceptors.

HandlerInterceptorAdaptor의 JavaDoc에 적힌 설명

 

What is difference between HandlerInterceptor and HandlerInceptorAdaptor in Spring MVC?

Both HandlerInterceptor and HandlerInterceptorAdaptor has preHandle and postHandle methods. But I am not able to understand what is the difference between them with respect to implementation.

stackoverflow.com

StackOverflow의 설명

HandlerInterceptorAdapter의 상속구조

 

 

이러한 방식은 개발상의 편의를 제공해 주는 장점이 있지만, 불필요한 클래스가 중간에 끼게 되므로 Adapter 패턴에 대한 배경지식이 없다면 직관적으로 이해하기 힘들고 가독성을 해치는 단점이 있다.

 

Java 8 이상에서 HandlerInterceptor의 변경점


Java 8 이상에서 HandlerInterceptor의 코드를 살펴보면, 메서드들이 아래와 같이 default 메서드로 지정되어 있음을 확인할 수 있다.

package org.springframework.web.servlet;

public interface HandlerInterceptor {

   default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      return true;
   }

   default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable ModelAndView modelAndView) throws Exception {
   }

   default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable Exception ex) throws Exception {
   }

}

 

즉, 내가 Override하지 않은 메서드는 default로 정의된 동작을 하게 되므로, 필요없는 메서드를 재작성하지 않아도 잘 동작한다.

따라서 굳이 Adapter가 필요 없어진 것이고, 바로 HandlerInterceptor를 구현해서 사용하면 되는 것이다.

 

결론


별 것 아닌 내용인 것 같지만, 생각보다 많은 점을 느낄 수 있었다.

  1. HandlerInterceptorAdapter Java 8의 default method로 인해 deprecated 되었다
  2. 디자인 패턴의 이해 중요하다
  3. 공식문서를 읽을 때 버전을 꼭 확인하자