안녕하세요.
정리 강의를 들으면서 Filter를 스프링 빈으로 등록하는 방법을 따라하다 버전 차이인지, 스프링 부트의 설정 차이인지, 에러가 발생하더군요.
The bean 'logFilter', defined in class path resource [hello/springcoremvc26/config/FilterConfig.class], could not be registered.
A bean with that name has already been defined in file [/project/java/spring/spring-core-mvc2-6/out/production/classes/hello/springcoremvc26/web/filter/LogFilter.class] and overriding is disabled.
이유를 읽어보니, logFilter가 이미 빈으로 등록되어서 중복 등록이 안된다고 써져있었습니다.
그래서 이에 대해 찾아본 결과를 공유하고자 글을 작성합니다.
아래 글은 제 블로그에도 정리되어 있습니다. (홍보...ㅎㅎ..)
@Configuration
LogFilter - 로그 필터
@Slf4j
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
LoginCheckFilter - 로그인 필터
@Slf4j
public class LoginCheckFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LoginFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LoginFilter doFilter End", requestURI);
}
}
}
FilterConfig - Filter 등록
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Filter> logFilter() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LogFilter());
bean.setOrder(1);
bean.addUrlPatterns("/*");
return bean;
}
@Bean
public FilterRegistrationBean<Filter> loginCheckFilter() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LoginCheckFilter());
bean.setOrder(2);
bean.addUrlPatterns("/*");
return bean;
}
}
실행 결과
[/] LogFilter doFilter Start
[/] LoginFilter doFilter Start
[/] LoginFilter doFilter End
[/] LogFilter doFilter End
특징
강의에서 나온 방법입니다.
설정을 위한 별개의 파일(
@Configuration
이 붙은 객체)이 필요합니다.setOrder()
를 통해 순서를 정할 수 있습니다.addUrlPatterns()
을 통해 베이스 URL 및 Whitelist를 설정할 수 있습니다.
@Component
LogFilter
@Slf4j
@Component
@Order(1)
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
LoginCheckFilter
@Slf4j
@Component
@Order(2)
public class LoginCheckFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LoginFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LoginFilter doFilter End", requestURI);
}
}
}
실행 결과
[/] LogFilter doFilter Start
[/] LoginFilter doFilter Start
[/] LoginFilter doFilter End
[/] LogFilter doFilter End
특징
컴포넌트 스캔을 이용하기 때문에 설정을 위한 별개의 파일이 필요하지 않습니다.
@Order
애노테이션을 이용해 순서를 설정할 수 있습니다.기본 URL Pattern이
/*
이며 설정할 수 없습니다.
@WebFilter + @ServletComponentScan
LogFilter
@Slf4j
@WebFilter
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
LoginCheckFilter
@Slf4j
@WebFilter
public class LoginCheckFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LoginFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LoginFilter doFilter End", requestURI);
}
}
}
MainApplication
@ServletComponentScan
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCoreMvc26Application.class, args);
}
}
실행 결과
[/] LogFilter doFilter Start
[/] LoginFilter doFilter Start
[/] LoginFilter doFilter End
[/] LogFilter doFilter End
특징
설정을 위한 별개의 파일이 필요하지 않습니다.
대신, 애플리케이션 실행되는 메인 객체위에
@ServletComponentScan
을 사용해야 합니다.
@Order
를 이용한 순서 등록을 사용할 수 없습니다.각 필터에 대한 순서를 보장할 수 없습니다.
@WebFilter
의value
나urlPatterns
파라미터를 이용해 whitelist 방식으로 베이스 URL을 설정할 수 있습니다.@WebFilter("/filter/*")
@WebFilter({"/login", "/items"})
@WebFilter(urlPatterns = "/filter/*")
@WebFilter(urlPatterns = {"/login", "/items"})
@WebFilter + @Component
LogFilter
@Slf4j
@WebFilter
@Component
@Order(1)
public class LogFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LogFilter init()");
}
@Override
public void destroy() {
log.info("LogFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LogFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LogFilter doFilter End", requestURI);
}
}
}
LoginCheckFilter
@Slf4j
@WebFilter
@Component
@Order(1)
public class LoginCheckFilter implements Filter {
@Override
public void init(
FilterConfig filterConfig
) throws ServletException {
log.info("LoginCheckFilter init()");
}
@Override
public void destroy() {
log.info("LoginCheckFilter destroy()");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String requestURI = req.getRequestURI();
log.info("[{}] LoginFilter doFilter Start", requestURI);
try {
chain.doFilter(request, response);
} finally {
log.info("[{}] LoginFilter doFilter End", requestURI);
}
}
}
실행 결과
[/] LogFilter doFilter Start
[/] LoginFilter doFilter Start
[/] LoginFilter doFilter End
[/] LogFilter doFilter End
특징
두 번째 방법과 세 번째 방법을 모두 사용하는 방법입니다.
설정을 위한 별개의 파일이 필요하지 않습니다.
컴포넌트 스캔 방식을 사용하기 때문에
@ServletComponentScan
도 필요없습니다.
@Order
애노테이션을 이용해 순서를 설정할 수 있습니다.@WebFilter
의value
나urlPatterns
파라미터를 이용해 베이스 URL이나 Whitelist 방식으로 설정할 수 있습니다.@WebFilter("/filter/*")
@WebFilter({"/login", "/items"})
@WebFilter(urlPatterns = "/filter/*")
@WebFilter(urlPatterns = {"/login", "/items"})
애노테이션이 기본으로 3개가 필요합니다.
정리
결론
필터를 사용하기 위한 4가지 방식을 알아보았습니다.
첫 번째 방법(@Configuration
+ FilterRegistrationBean
)과 두 번째, 세 번째 방법을 합친 네 번째 방법(@WebFilter
+ @Component
) 중에서 고려하면됩니다.
저의 경우에는 @Configuration
+ FilterRegistrationBean
을 더 맘에 드는데, 이유는 Filter Class 위에 애노테이션을 여러개 붙이는 것 보다, 별도의 생성 파일을 하나 만들어서 관리하는게 더 편할 것 같아서였습니다.
하지만, @WebFilter + @Component
은 스프링 빈으로 등록하는 방법이기에, 다른 스프링 빈을 의존성 주입 받을 수 있습니다. 그런 경우에는 첫 번째 방법으론 할 수 없으니, 네 번째 방법을 사용하면 될 것 같습니다.