过滤器、拦截器入门,看这一篇就够了

536 阅读3分钟

实际项目开发过程中,肯定有这样的需求:

  1. 记录每个rest请求耗费的时间,入参,出参
  2. 某些请求需要判断是否有权限,某些请求是不需要权限也可以运行的

这个需求太普遍了。今天我们就一起来学习这个需求的两种实现方式:

  1. 拦截器
  2. 过滤器

拦截器

话不多说,咱们上代码,新建日志拦截器:

@Component
public class LogInterceptor implements HandlerInterceptor {
    //在请求rest接口之前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("请求url:"+request.getRequestURL());
        Map<String, String[]> parameterMap = request.getParameterMap();
        Enumeration<String> parameterNames = request.getParameterNames();

        System.out.println("请求参数:");
        while (parameterNames.hasMoreElements()) {
            String name = (String) parameterNames.nextElement();
            String value = request.getParameter(name);
            System.out.println(name+"==>"+value);
        }

        //假设参数a传递的如果是1的话,是不合法的请求,不继续往下执行
        if("1".equals(request.getParameter("a"))){
            return false;
        }
        return true;//如果返回false则不会继续往下执行,不会真正的进入到controller
    }

    //请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle is called...");
    }

    //在整个请求结束之后被调用
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion is called...");
    }
}

WebMvcConfigurer中注册该拦截器,并告诉spring哪些路径需要拦截,哪些路径不需要拦截

@Configuration
public class InterceperConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(new LogInterceptor() {
        });
        //如下的路径不拦截,多个路径用逗号分隔
        interceptorRegistration.excludePathPatterns("/demo/test2");
        //拦截如下路径
        interceptorRegistration.addPathPatterns("/**");
    }
}

好了,拦截器就做完了,so easy。下面我们写个controller来测试下吧!

@RestController
@RequestMapping("/demo")
public class DemoController {

    @GetMapping("test1")
    public String test1(){
        System.out.println("rest.test1 is called");
        return "test1";
    }

    @GetMapping("test2")
    public String test2(){
        System.out.println("rest.test2 is called");
        return "test2";
    }

    @GetMapping("test3")
    public String test3(@RequestParam("a")String a){
        System.out.println("rest.test3 is called");
        return "test3";
    }
}

测试的结果基本可以预期到

  1. demo/test1 和 demo/test3会被拦截,进拦截器
  2. demo/test2 不会进拦截器
  3. demo/test3?a=1 这个请求会进拦截器,但是进不了controller。

过滤器

@WebFilter(urlPatterns = "/*", filterName = "LogFilter")
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("logfilter.init is called...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter中打印的请求参数:");
        Enumeration<String> parameterNames = servletRequest.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String key = parameterNames.nextElement();
            String value = servletRequest.getParameter(key);
            System.out.println(key+"==>"+value);
        }

        long bgn = System.currentTimeMillis();
        //一定要调用链式调用传递,否则也进不了controller
        filterChain.doFilter(servletRequest,servletResponse);
        long end = System.currentTimeMillis();
        System.out.println("filter中记录耗时:"+(end-bgn)+"ms");
    }

    @Override
    public void destroy() {
        System.out.println("logfilter.destroy is called...");
    }
}

注意:@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包。

总结

拦截器、过滤器执行顺序,默认先执行过滤器,再执行拦截器。

如果过滤器有多个会根据过滤器的名词按照A-Z的排序先后执行。这是因为@WebFilter这个注解不支持执行顺序。当然你也可以通过写一个配置文件解决。

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean reqResFilter1() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        LogFilter logFilter = new LogFilter();
        filterRegistrationBean.setFilter(logFilter);
        filterRegistrationBean.addUrlPatterns("/demo/test1","/demo/test3");//配置过滤规则
        filterRegistrationBean.addInitParameter("name","spingboot");//设置init参数
        filterRegistrationBean.setName("logFilter");//设置过滤器名称
        filterRegistrationBean.setOrder(2);//执行次序

        return filterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean reqResFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        Log1Filter log1Filter = new Log1Filter();
        filterRegistrationBean.setFilter(log1Filter);
        filterRegistrationBean.addUrlPatterns("*");//配置过滤规则
        filterRegistrationBean.setName("Log1Filter");//设置过滤器名称
        filterRegistrationBean.setOrder(1);//执行次序
        
        return filterRegistrationBean;
    }
}

更多java原创阅读:javawu.com/