过滤器 vs 拦截器
本文最后更新于 927 天前,其中的信息可能已经有所发展或是发生改变。

过滤器 (Filter)

过滤器基于函数回调实现,属于Servlet规范,导致它只能在Web程序使用。

  1. 在Application上声明@ServletComponentScan注解
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    //@ServletComponentScan能够识别当前包及其子包下的@WebFilter注解
    @ServletComponentScan
    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }
  2. 自定义过滤器
    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.annotation.WebInitParam;
    import java.io.IOException;
    
    @Slf4j
    @WebFilter(urlPatterns = "/custom/*",//*表示通配符,此处即可拦截/custom/user,也可拦截/custom/user/me等多级URL
            filterName = "customFilter",
            initParams = {
                    @WebInitParam(name = "user", value = "admin")
            })
    //此处无需使用@Component
    public class CustomFilter implements Filter {
    
        //Web容器中的每一次请求都会调用该方法
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            log.info("过滤前执行");
            filterChain.doFilter(servletRequest, servletResponse);//交由后续过滤器调用,若当前过滤器是过滤器链中最后一个,则将请求交由Controller。也可给客户端直接返回响应信息。
            log.info("过滤后执行");
        }
    
        //Web容器初始化过滤器时调用,只会被调用一次
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            //通过FilterConfig可以获取web.xml文件中的初始化参数
            String filterName = filterConfig.getFilterName();//来自@WebFilter的filterName,此处为customFilter
            log.info(filterName);
            String user = filterConfig.getInitParameter("user");//来自@WebInitParam的name,此处为admin
            log.info(user);
            ServletContext servletContext = filterConfig.getServletContext();
        }
    
        //Web容器销毁过滤器实例时调用,只会被调用一次
        @Override
        public void destroy() {
            log.info("destroy");
        }
    }
  3. 更改过滤器执行顺序
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import javax.servlet.Filter;
    @Configuration
    public class GlobalMvcConfig {
        @Bean
        public FilterRegistrationBean filterRegistrationBean() {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
            registrationBean.setFilter(new CustomFilter());//此处的Filter不必使用@WebFilter
            registrationBean.addUrlPatterns("/material/*");//类似@WebFilter的urlPatterns
            registrationBean.setName("customFilter");//类似@WebFilter的filterName
            registrationBean.addInitParameter("user", "admin");//类似@WebInitParam
            registrationBean.setOrder(1);//Filter默认根据filterName顺序执行,setOrder方法可以更改doFilter执行顺序
            return registrationBean;
        }
        //多个Filter需要声明多个FilterRegistrationBean
        @Bean
        public FilterRegistrationBean filterRegistrationBean2() {
            //TODO
        }
    }

拦截器 (Interceptor)

拦截器基于动态代理实现,属于Spring容器管理,可以单独使用。

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CustomInterceptor implements HandlerInterceptor {
    //在Controller方法调用之前执行
    //注意:如果该方法的返回值为false,表示终止当前请求,不仅自身拦截失效,还会导致后续拦截器也不再执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return false;
    }
    //在Controller方法调用之后,DispatcherServlet返回渲染视图之前执行
    //注意:只有在preHandle方法返回true时才会生效
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }
    //在DispatcherServlet返回渲染视图之后执行
    //注意:只有在preHandle方法返回true时才会生效
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

注意:拦截器是在SpringContext之前加载的,所以不能在拦截器中直接使用Spring容器管理的Bean,解决办法如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class GlobalMvcConfig implements WebMvcConfigurer {
    @Bean
    public CustomInterceptor customInterceptor() {
        return new CustomInterceptor();
    }
    //定义好的拦截器可以通过addPathPatterns、excludePathPatterns等方法设置需要拦截或排除的URL
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(customInterceptor())
                .addPathPatterns("/oauth/token")
                .order(0)//Interceptor默认根据addInterceptor添加顺序执行,order方法可以更改执行顺序
                .excludePathPatterns("/oauth/check_token");
    }
}

@Order & Ordered接口

注意:@Order注解和Ordered接口并不能改变过滤器和拦截器的执行顺序,详情请参考《Spring Boot 常用注解》

触发时机

Tomcat → Filter → Servlet → Interceptor → Controller

如果觉得本文对您有帮助,记得收藏哦~
上一篇
下一篇