SpringBoot中定义Filter的三种方式
第一种方式
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LogFilter> paramsFilter() {
FilterRegistrationBean<LogFilter> registration = new FilterRegistrationBean();
registration.setFilter(new LogFilter());
registration.addUrlPatterns("/*");
registration.setName("logFilter");
return registration;
}
public static class LogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
System.out.println("LogFilter requestURI: " + requestURI);
filterChain.doFilter(request, response);
}
}
}
将 Filter 包装为 FilterRegistrationBean,并注册到 Spring 中。
简单原理分析
- ServletContextInitializerBeans 实例化时,从 Bean 容器中查找类型为 ServletContextInitializer 的所有 bean,包含 Servlet,Filter,Listener。我们定义的 FilterRegistrationBean 就是实现 ServletContextInitializer 这个接口的。
- TomcatServletWebServerFactory 得到 ServletContextInitializer 列表,在 getWebServer() 方法时传给 TomcatStarter 对象。
- TomcatStarter 是一个 ServletContainerInitializer,这是 servlet3.0 提供的一种扩展方式,会在 StandardContext 启动时调用 ServletContainerInitializer 的 onStartup() 方法。
- TomcatStarter 的 onStartup() 方法会依次调用 ServletContextInitializer 的 onStartup() 方法,ServletContextInitializer 可以看作是 Spring 提供的一种扩展方式。
- ServletContextInitializer 的 onStartup() 方法就会创建Servlet,Filter,Listener。底层调用 ServletContext 的 addServlet(),addFilter(),addListener() 方法,这样我们的 Servlet 或 Filter 就添加到 Tomcat 中了。
第二种方式
@ServletComponentScan
@Configuration
public class FilterConfig2 {
@WebFilter(urlPatterns = "/*")
public static class LogFilter2 extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
System.out.println("LogFilter2 requestURI: " + requestURI);
filterChain.doFilter(request, response);
}
}
}
@WebFilter 注解和 @ServletComponentScan 注解配合使用。
- @ServletComponentScan 注解会自动配置 ServletComponentScanRegistrar,它又会配置 ServletComponentRegisteringPostProcessor。
- ServletComponentRegisteringPostProcessor 是一个 BeanFactory 后置处理器,它会扫描所有包含 WebServlet,WebFilter,WebListener注解的类。
- 以 WebFilterHandler 为例,它会将 包含 WebFilter 注解的类包装为 FilterRegistrationBean,注册到 Spring 容器中。
- 后续原理就和第一种方式一样了。
第三种方式
@Configuration
public class FilterConfig3 {
@Component
public static class LogFilter3 extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestURI = request.getRequestURI();
System.out.println("LogFilter3 requestURI: " + requestURI);
filterChain.doFilter(request, response);
}
}
}
直接将 Filter 注册为 Bean。这种方式不能指定 Filter 的 urlPattern,固定为 /*。
- ServletContextInitializerBeans 在实例化时,不仅会查找类型为 ServletContextInitializer 的所有 bean,还会查找 类型为 Servlet,Filter 的所有 bean,将其转换为 ServletRegistrationBean,FilterRegistrationBean。
- 后续原理和第一种方式一样。
总结
3种方式本质上都是包装为 ServletRegistrationBean 或 FilterRegistrationBean,然后注册到 Tomcat 中,第二,第三种方式可以看作第一种方式的变种。