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 中。

简单原理分析

  1. ServletContextInitializerBeans 实例化时,从 Bean 容器中查找类型为 ServletContextInitializer 的所有 bean,包含 Servlet,Filter,Listener。我们定义的 FilterRegistrationBean 就是实现 ServletContextInitializer 这个接口的。
  2. TomcatServletWebServerFactory 得到 ServletContextInitializer 列表,在 getWebServer() 方法时传给 TomcatStarter 对象。
  3. TomcatStarter 是一个 ServletContainerInitializer,这是 servlet3.0 提供的一种扩展方式,会在 StandardContext 启动时调用 ServletContainerInitializer 的 onStartup() 方法。
  4. TomcatStarter 的 onStartup() 方法会依次调用 ServletContextInitializer 的 onStartup() 方法,ServletContextInitializer 可以看作是 Spring 提供的一种扩展方式。
  5. 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 注解配合使用。

  1. @ServletComponentScan 注解会自动配置 ServletComponentScanRegistrar,它又会配置 ServletComponentRegisteringPostProcessor。
  2. ServletComponentRegisteringPostProcessor 是一个 BeanFactory 后置处理器,它会扫描所有包含 WebServlet,WebFilter,WebListener注解的类。
  3. 以 WebFilterHandler 为例,它会将 包含 WebFilter 注解的类包装为 FilterRegistrationBean,注册到 Spring 容器中。
  4. 后续原理就和第一种方式一样了。

第三种方式

@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,固定为 /*。

  1. ServletContextInitializerBeans 在实例化时,不仅会查找类型为 ServletContextInitializer 的所有 bean,还会查找 类型为 Servlet,Filter 的所有 bean,将其转换为 ServletRegistrationBean,FilterRegistrationBean。
  2. 后续原理和第一种方式一样。

总结

3种方式本质上都是包装为 ServletRegistrationBean 或 FilterRegistrationBean,然后注册到 Tomcat 中,第二,第三种方式可以看作第一种方式的变种。

posted @ 2024-04-08 21:34  strongmore  阅读(476)  评论(0编辑  收藏  举报