过滤器(Filter)

  • 1)它依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。
  • 2)使用过滤器的目的,是用来做一些过滤操作,获取我们想要获取的数据, 比如:在Javaweb中,对传入的request、response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者Controller进行业务逻辑操作。
  • 3)通常用的场景是:在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。

代码实现

springboot我们可以这样添加自己的拦截器

/**
* 注解方式
**/
@WebFilter(urlPatterns = "/*",filterName = "myfilter")
public class FilterAnnotationTest implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器2开始初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器2开始工作");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("过滤器2销毁");
    }
}




/**
* bean 注入方式
*/

public class FilterDemo implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        System.out.println("过滤器开始工作。。"+httpServletRequest.getRequestURL());
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("过滤器开始销毁");
    }
}

@Configuration
public class FilterDemo {
    @Bean
    @Order(2)
    //spring boot会按照order值的大小,从小到大的顺序来依次过滤
    public FilterRegistrationBean configFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new FilterDemo());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setName("sessionFilter");
        //filterRegistrationBean.setOrder(2);
        return filterRegistrationBean;
    }
    
}

拦截器(Interceptor)

  • 1)它依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法, 比如:动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。
  • 2)由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。

代码实现


/**
*  1.实现拦截器可以通过继承HandlerInterceptorAdapter类。
*/
public class InterceptorDemo extends HandlerInterceptorAdapter {


    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        StringBuffer requestURL = httpServletRequest.getRequestURL();
        System.out.println("前置拦截器1 preHandle: 请求的uri为:"+requestURL.toString());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器1 postHandle: ");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("拦截器1 afterCompletion: ");
    }
}



/**
*  2.将自定义拦截器加入到Mvc配置中
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer{

/**
     * 注册自定义拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       	registry.addInterceptor(new InterceptorDemo2()).addPathPatterns("/**");
        registry.addInterceptor(new InterceptorDemo()).addPathPatterns("/**");
    }
 }

正常流程:

  	Interceptor2.preHandle
  	Interceptor1.preHandle
  	Controller处理请求
  	Interceptor1.postHandle
  	Interceptor2.postHandle
  	渲染视图view
  	Interceptor1.afterCompletion
  	Interceptor2.afterCompletion

中断流程:

如果在Interceptor1.preHandle中报错或返回false ,那么接下来的流程就会被中断,但注意被执行过的拦截器的afterCompletion仍然会执行。下图为Interceptor1.preHandle返回false的情况:

前置拦截器2 preHandle: 用户名:null
前置拦截器1 preHandle: 请求的uri为:http://localhost:8010/user/353434
拦截器2 afterCompletion:

和Filter共存时的执行顺序:

拦截器是在DispatcherServlet这个servlet中执行的,因此所有的请求最先进入Filter,最后离开Filter。其顺序如下。

Filter->Interceptor.preHandle->Handler->Interceptor.postHandle->Interceptor.afterCompletion->Filter

应用场景

拦截器本质上是面向切面编程(AOP),符合横切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:

1.登录验证:判断用户是否登录。 2.权限验证:判断用户是否有权限访问资源,如校验token 3.日志记录:记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。 4.通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。 5.性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录); 6.OpenSessionInView:如hibernate,在进入处理器打开Session,在完成后关闭Session。

过滤器filter和拦截器Interceptor的区别

spring的拦截器和servlet的过滤器有相似之处,都是AOP思想的体现,都可以实现权限检查,日志记录,不同的是

||filter|Interceptor| |-|-|-| |多个的执行顺序|根据filter mapping配置的先后顺序|按照配置的顺序,但是可以通过order控制顺序| |规范|在Servlet规范中定义的,是Servlet容器支持的|spring容器内的,是Spring框架支持的。| |使用范围|只能用于Web程序中|既可以用于Web程序,也可以用于Application、Swing程序中。| |深度|Filter在只在Servlet前后起作用|拦截器能够深入到方法前后、异常抛出前后等|

引发更多相关知识的思考

过滤器(Filter):可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息。

拦截器(Interceptor):可以拿到你请求的控制器和方法,却拿不到请求方法的参数。

切片 (Aspect): 可以拿到方法的参数,但是却拿不到http请求和响应的对象 监听 (Listener) : 监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。

参考文章: springboot下使用拦截器和过滤器 关于拦截器与过滤器使用场景、拦截器与过滤器的区别整理