Servlet Filter

Better late than never·2023년 3월 5일
0

Servlet Filter Bean으로 등록?

예전 책들을 참고하면 Filter는 서블릿 기술이라 Spring 빈으로 등록할 수 없다고 나오는데

J2EE표준 스펙 기능이지만 인터셉터는 스프링 프레임워크가 제공하는 기술이므로 필터와 달리 인터셉터는 스프링 빈으로 등록 가능

서블릿 스펙의 기술인 필터는 스프링 범위 밖인 서블릿 범위에서 관리되는데(실제 interceptor가 Controller로 요청을 위임하지는 않는다)

  • 스프링 컨테이너보다 큰 범위인 서블릿 컨테이너의 필터가 스프링 컨테이너에 의해 관리되지 않는다

하지만 현재 테스트 시 필터 역시 스프링 빈으로 등록이 가능하며 빈을 주입 받을 수 있는데

과거에는 실제로 필터(Filter)가 스프링 컨테이너에 의해 관리되지 않았기 때문에 빈으로 등록할 수 없고 다른 빈을 주입받을 수 없다

→ DelegatingFilterProxy가 등장하면서 사용 가능

DelegatingFilterProxy 등장

  • DelegatingFilterProxy 이전

필터는 서블릿이 제공하는 기술로 서블릿 컨테이너에 의해 생성되며 서블릿 컨테이너에 등록되는데 스프링 빈으로 등록도 불가하고 빈을 주입 받을 수 없다

필터에서도 DI와 같은 스프링 기술을 필요로 하는 상황이 발생하면서 개발자는 필터도 스프링 빈을 주입 받을 수 있는 대안을 마련 → DelegatingFilterProxy

등장 이후

Spring 1.2이후 DelegatingFilterProxy가 등장 하면서 서블릿 필터(Servlet Filter)이 스플링에서 관리 가능, DelegatingFilterProxy는 서블릿 컨테이너에서 관리되는 프록시용 필터로써 우리가 만든 필터를 가지고 있는데 우리가 만든 필터는 스프링 컨테이너의 빈으로 등록되는데 요청이 오면 DelegatingFilterProxy가 요청을 받아 우리가 만든 핕터(스프링 빈)에게 요청을 위임

  1. Filter 구현체가 스프링 빈으로 등록
  2. ServletContext가 Filter 구현체를 갖는 DelegatingFilterProxy 생성
  3. ServletContext가 DelegatingFilterProxy를 서블릿 컨테이너에 필터로 등록
  4. 요청이 오면 DelegatingFilterProxy가 필터 구현체에게 요청을 위임하여 필터 처리 진행

예시

@Component
public class MyFilter implements Filter {

   @Override
   public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException {
      chain.doFilter(request, response);
   }

   @Override
   public void init(final FilterConfig filterConfig) throws ServletException {
      Filter.super.init(filterConfig);
   }

   @Override
   public void destroy() {
      Filter.super.destroy();
   }

}

// ...

public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

   public void onStartup(ServletContext servletContext) throws ServletException {
      super.onStartup(servletContext);
      servletContext.addFilter("myFilter", DelegatingFilterProxy.class);
   }
}

SpringBoot가 아닌 Spring에서는 다음과 같이 서블릿 컨텍스트에 addFilter로 추가

"myFilter"라는 빈을 찾아서 DelegatingFilterProxy에 넣어주고, 서블릿 컨테이너에 myFilter 대신 DelegatingFilterProxy를 등록하여 DelegatingFilterProxy를 통해 myFilter에 요청을 위임하라는 것이다.

DelegatingFilterProxy가 add되는 곳은 ServletContext이고, 우리가 만든 필터는 스프링 컨테이너에 빈으로 먼저 등록된 후에 DelegatingFilterProxy에 감싸져 서블릿 컨테이너로 등록이 되는 것이다. 이러한 이유로 우리가 개발한 Filter도 스프링 빈으로 등록되며, 스프링 컨테이너에서 관리되기 때문에 빈 등록뿐만 아니라 빈의 주입까지 가능

Spring Boot 등장

DelegatingFilterProxy를 등록하는 과정은 Spring boot면 필요없는데 Spring Boot는 내장 웹 서버를 지원하면서 톰캣과 같은 서블릿 컨테이너까지 Spring Boot가 제어 가능하기 때문

Spring Boot에서는 ServletContext에 필터 빈을 DelegatingFilterProxy로 감싸서 등록하지 않아도 된다. Spring Boot가 서블릿 필터의 구현체 빈을 찾으면 DelegatingFilterProxy없이 바로 필터 체인(Filter Chain)에 필터를 등록

실제로 필터에서 doFilter 호출 전에 런타임 예외를 발생시켜 로그를 출력해보면 Spring 프레임워크에 DelegatingFilterProxy를 이용해 필터를 등록한 경우

java.lang.RuntimeException
    at com.mangkyu.MyFilter.doFilter(MyFilter.java:17)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

DelegatingFilterProxy가 doFilter로 요청을 받고, 구현체인 MyFilter로 위임(invokeDelegate), 반면에 Spring Boot에서 동일한 상황에서 로그 호출 시 DelegatingFilterProxy 관련 내용이 없다

at com.mangkyu.MyFilter.doFilter(MyFilter.java:17) [main/:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) [tomcat-embed-core-9.0.56.jar:9.0.56]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) [tomcat-embed-core-9.0.56.jar:9.0.56]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) [spring-web-5.3.15.jar:5.3.15]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) [spring-web-5.3.15.jar:5.3.15]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) [tomcat-embed-core-9.0.56.jar:9.0.56]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) [tomcat-embed-core-9.0.56.jar:9.0.56]

Conclusion

  • 등장 배경: 필터에서 다른 스프링 빈의 주입이 필요해짐
  • DelegatingFilterProxy의 등장 이전: 스프링 빈으로 등록 및 다른 빈 주입이 불가능했음
  • DelegatingFilterProxy의 등장 이후: DelegatingFilterProxy를 통해 스프링 빈으로 등록 및 다른 빈 주입이 가능해짐
  • SpringBoot의 등장 이후: 웹서버를 직접 관리하면서 DelegatingFilterProxy조차 필요없게됨

0개의 댓글