天天看点

拦截器和过滤器详解

作者:小彭思考

过滤器Filter

定义

对Servlet容器调用Servlet的过程进行拦截,基于函数回调实现

常见使用场景

  • 统一设置编码
  • 过滤敏感字符
  • 登录校验
  • URL级别的访问权限控制
  • 数据压缩

使用方式

这里展示的是SpringBoot整合过滤器的方式

使用配置类的方式(bean注入)

  • 编写filter类并实现Filter接口
  • 实现Filter接口中的init,doFilter方法,与destory方法
  • doFilter方法中使用chain.doFilter(request, response);来放行servlet
  • 编写Filter配置类,注解加上@ConFiguration注解
  • 编写方法,返回值类型为FilterRegistrationBean
  • 方法内创建FilterRegistrationBean对象,构造器内传入过滤器类的对象,直接new一个就可以,或者让Spring容器自动注入后再使用
  • 调用FilterRegistrationBean 对象的addUrlPatterns方法类设定过滤的请求路径

过滤器类

@Slf4j//方便使用下面的log.info()方法输出日志
public class LoginCheckFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        log.info("过滤器{}正在生效",LoginCheckFilter.class.getName());
        log.info("过滤器{}拦截到了请求{}",LoginCheckFilter.class.getName(),request.getRequestURI());
        filterChain.doFilter(request,response);
        log.info("请求{}已经被放行",request.getRequestURI());
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("过滤器{}被初始化",LoginCheckFilter.class.getName());
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        log.info("过滤器{}被销毁",LoginCheckFilter.class.getName());
        Filter.super.destroy();
    }
}           

配置类

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<LoginCheckFilter> filterRegistrationBean() {
        FilterRegistrationBean<LoginCheckFilter> loginCheckFilter = new FilterRegistrationBean<>();
        loginCheckFilter.setFilter(new LoginCheckFilter());//注册自定义过滤器
        loginCheckFilter.setName("LoginCheckFilter");//过滤器名称
        loginCheckFilter.addUrlPatterns("/*");//过滤所有路径
        loginCheckFilter.setOrder(1);//优先级,最顶级按照数据从小到大来执行过滤器
        return loginCheckFilter;
    }
}           

使用注解

  • 创建过滤器类,方法与上面创建过滤器类的步骤一样
  • 过滤器类上面添加注解 @WebFilter(urlPatterns = {“拦截路径”}, filterName = “过滤器名称”)
  • 在启动类上添加@ServeltComponentScan注解来扫描servlet组件(包括:@WebFilter和@WebListener)

注意:使用注解方式无法控制多个过滤器的顺序,()默认执行顺序是按照类名字母的排序,

使用@Order注解控制Bean的执行顺序时,必须配合注解一起使用,才会起作用

但是@Component与@WebFilter注解都会把过滤器类加载一遍,并且@component会把@WebFilter加载的Bean覆盖掉,从而使得@WebFilter配置的拦截路径与过滤器名称失效,

所以如果指定了优先级则配置的拦截路径失效,默认为任意路径,如果不使用@Order注解则无法控制多个过滤器的执行顺序,

综上笔者这里推荐使用Bean注入的方式来使用过滤器

总结

  • Filter是依赖于Servlet的,需要导入Servlet的依赖
  • 过滤器会在容器启动时被初始化,并且只会初始化一次
  • doFilter()方法在目标请求被拦截前执行,放行调用filterChain.doFilter(servletRequest,servletResponse);具体变量名根据方法体而变
  • destroy()方法在容器销毁时执行,只执行一次
  • Filter可以拦截所有请求,包括静态资源
  • 过滤器基于函数回调实现

扩展

  • 过滤器,监听器和Servlet是JavaWeb三大组件之一,它们的在SpringBoot中的使用方式都差不多区别如下
  • 使用注解方式使用时都需要在启动类加上@ServletComponentScan注解
  • 三个注解为@WebSrvlet,@WebFilter和@Weblistener
  • Servlet类继承的是HttpServlet,Filter实现的是Filter类,Listener实现ServletContextListener类
  • 使用Bean注入方式使用时,Filter组件方法返回类型是FilterRegistrationBean,Listener组件方法返回类型为ServletListenerRefistrationBean,Servlet组件返回类型为ServletRefistrationBean

拦截器Interceptor

定义

类似于Servlet中的过滤器,主要用于拦截用户请求并做相应的处理,基于java反射机制(动态代理)实现

常见使用场景

  • 日志记录
  • 权限校验
  • 登录校验
  • 性能检测【检测方法的执行时间】

其实拦截器和过滤器很像,有些使用场景。无论选用谁都能实现。需要注意的使他们彼此的使用范围,触发机制。

使用方式

  • 编写拦截器类并实现HandlerInterceptor 接口
  • 实现HandlerInterceptor 接口的preHandle,postHandle和afterHandle方法
  • 编写配置类,注意加上@configuration注解,并继承WebMvcConfigurer接口

总结

  • 拦截器依赖于SpringMvc的,需要导入Mvc的依赖
  • preHandle() 在目标请求完成之前执行。有返回值Boolean类型,true:表示放行
  • postHandle() 在目标请求之完成后执行。
  • afterCompletion() 在整个请求完成之后【modelAndView已被渲染执行】。
  • 拦截器只能拦截action请求,不包括静态资源(有待验证)
  • 基于java反射机制实现

拦截器不生效原因分析

  • 配置类没有加@configuration注解
  • 拦截路径出错,/*拦截当前目录,/**拦截所有目录
  • WebMvcConfigurationSupport,WebMvcConfigurer,WebMvcConfigurerAdapter项目中不能有两个以上的类继承或实现自这三个类 因为这三个类都是WebMVC的配置类,如果同时出现只会有一个类生效,一般实现WebMvcConfilgurer就可以了

过滤器与拦截器的区别

  • 触发顺序不一样:
  1. 过滤器的触发顺序在拦截器前,也就是先执行过滤器,在执行拦截器, 过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。
  2. 过滤器之间的执行顺序嵌套的。
  3. 但是拦截器执行顺序也是嵌套执行的但是,因为有三个方法,会先执行pre方法,再一起执行post方法,最后一起执行after方法。
  4. 实现方式不同,过滤器基于函数回调实现,拦截器基于动态代理(反射)实现。
  • 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
  • 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  • 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  • 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  • 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
  • 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

aop与过滤器,拦截器的区别

  • 过滤器,拦截器拦截的是URL。AOP拦截的是类的元数据(包、类、方法名、参数等)。
  • 过滤器并没有定义业务用于执行逻辑前、后等,仅仅是请求到达就执行。
  • 拦截器有三个方法,相对于过滤器更加细致,有被拦截逻辑执行前、后等。
  • AOP针对具体的代码,能够实现更加复杂的业务逻辑。
  • 三者功能类似,但各有优势,从过滤器 -> 拦截器 -> 切面,拦截规则越来越细致。
  • 执行顺序依次是过滤器、拦截器、切面。