스프링에 대해서 여러가지 공부를 하던중, 정리 해놓으면 좋을 것 같은 주제를 정리하려고 한다.
이번에 다를 주제는 OncePerRequestFilter vs GenericFilterBean이다.
해당 주제를 다루게 된 이유는 Filter에 대해서 공부하던 중에
GenericFilter와 OncePerRequestFilter를 발견하게 되었고, 딱히 구현에 있어서 큰 차이점은 없어보이지만 왜 다른이름으로 사용되는지 궁금하여 공부해보았고 해당 내용을 정리하여 기록해보려고 한다.
우리가 자세히 봐야하는건 해당 사진에서 Filter에 대한 부분이다. Filter에 대해 간단히 설명하자면 아래와 같다.
- javax.servlet-api나 tomcat-embed-core를 사용하면 제공되는 Servlet Filter Interface이다.
- DispatcherServlet가 요청을 받기 전 앞단에 Filter에서 먼저 request를 받는다.
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
default void destroy() {
}
}
Filter 인터페이스를 구현하고 등록하면 서블릿 컨테이너가 필터를 싱글톤 객체로 생성하고 관리한다.
- init() : 필터 초기화 메소드, 서블릿 컨테이너가 생성될 때 호출된다.
- doFilter() : 클라이언트 요청이 올 때마다 해당 메소드를 호출한다. 필터의 로직을 구현하면 된다.
- destroy() : 필터 종료 메소드, 서블릿 컨테이너가 종료될 때 호출된다.
public abstract class GenericFilterBean implements Filter, BeanNameAware, EnvironmentAware,
EnvironmentCapable, ServletContextAware, InitializingBean, DisposableBean {
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private String beanName;
@Nullable
private Environment environment;
@Nullable
private ServletContext servletContext;
@Nullable
private FilterConfig filterConfig;
private final Set<String> requiredProperties = new HashSet<>(4);
...
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
GenericFilterBean은 Filter를 implements 받아 구현한 클래스이다.
- Filter를 확장하여 Spring에서 제공하는 filter
- 기존 Filter에서 얻어올 수 없는 정보였던 Spring의 설정 정보를 가져올 수 있게 확장된 추상 클래스
- 해당 Filter들은 서블릿마다 호출이 된다.
- 서블릿은 사용자의 요청을 받으면 서블릿을 생성해 메모리에 저장해두고, 같은 클라이언트의 요청을 받으면 생성해둔 서블릿 객체를 재활용하여 요청을 처리한다.
앞에서 GenericFilterBean만을 공부하였을때는 완벽해 보인다. 하지만 완벽하다면 OncePerRequestFilter가 탄생할 이유가 없었겠지만말이다.
public class FirstFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request , response);
}
}
- 해당 필터는 사용자의 요청당 한번만 실행되는 필터를 만들도록 해준다.
- GenericFilterBean을 상속하여 구현한 경우 doFilter메서드를 구현하면 되고, OncePerRequestFilter를 상속하여 구현한 경우 doFilterInternal 메서드를 구현하면 된다.
JwtAuthenticationFilter 구현시에는 Filter가 두번씩 적용되는 경우가 있을 수 있기 때문에 OncePerRequestFilter을 사용하는 것이 적절하다.