filter和Interceptor

342 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

一、前言

filter和Interceptor都可以拦截请求,对参数进行过滤,安全校验、记录日志等工作,但是两者还是有本质的区别。

二、过滤器

过滤器( Filter),是 JavaEE 的标准,依赖于 Servlet 容器,配置在web.xml 文件中,可以配置多个,执行的顺序是根据配置顺序从上到下。

过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是设置字符集、控制权限、控制转向、做一些业务逻辑判断等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一配置请求编码以及过滤一些非法参数,垃圾信息,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。

Filter可以认为是Servlet的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

(一)过滤器的作用

  • 在请求到达serverlt之前拦截客户的HttpServletRequest,获取、修改请求数据
  • 在响应返回客户端之前拦截HttpServletResponse,检查、修改响应数据。

(二)使用场景

  • 负责检查用户请求,根据请求过滤用户非法请求,springsecurity就是通过filter实现
  • 日志Filter:详细记录某些特殊的用户请求
  • 负责解码的Filter:包括对非标准编码的请求解码。

(三)创建方式

  • 实现javax.servlet.Filter接口
  • 继承spring中OncePerRequestFilter类

(四)使用方式

1.在xml中配置

<filter>
  <filter-name>CharacterEncodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CharacterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

2.使用FilterRegistrationBean注册

@Configuration
public class FilterConfig {
    
    @Bean
    public FilterRegistrationBean testFilter3RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter3());
        registration.addUrlPatterns("/hello");
        registration.setOrder(1); // 值越小越优先执行,此处配置有效
        return registration;
    }
    @Bean
    public FilterRegistrationBean testFilter4RegistrationBean() {
        FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter4());
        registration.addUrlPatterns("/hello");
        registration.setOrder(2);
        return registration;
    }
}

3.使用@WebFilter 注解配置

在filter上增加@WebFilter注解,在启动类上增加@ServletComponentScan注解

此方式无法通过使用@Order注解来指定filter执行顺序,更详细的原因需要翻看源码。

@WebFilter(urlPatterns = "/hello")
public class TestFilter1 implements Filter {
    @Override
    public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
        System.out.println("##############Filter1 init##############");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前执行
        System.out.println("##############doFilter1 before##############");
        filterChain.doFilter(servletRequest, servletResponse);
        // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
        System.out.println("##############doFilter1 after##############");
    }

    @Override
    public void destroy() {
        System.out.println("##############Filter1 destroy##############");
    }
}

三、拦截器

拦截器是SpringMVC中实现的一种基于Java反射(动态代理)机制的方法增强工具,拦截器的实现是继承HandlerInterceptor 接口,并实现接口的preHandle、postHandle和afterCompletion方法。

1、preHandle:请求方法前置拦截,该方法会在Controller处理之前进行调用,Spring中可以有多个Interceptor,这些拦截器会按照设定的Order顺序调用,当有一个拦截器在preHandle中返回false的时候,请求就会终止。

2、postHandle:preHandle返回结果为true时,在Controller方法执行之后,视图渲染之前被调用

3、afterCompletion:在preHandle返回ture,并且整个请求结束之后,执行该方法。

(一)创建方式

通过实现HandlerInterceptor接口来创建拦截器

@Component
public class TestInterceptor1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("##############TestInterceptor1 preHandle##############");
        return true;
    }

    //在Controller之后的DispatcherServlet之后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("##############TestInterceptor1 postHandle##############");
    }

    // 在页面渲染完成之后返回给客户端执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("##############TestInterceptor1 afterCompletion##############");
    }
}

(二)使用方式

通过实现WebMvcConfigurer接口来注册拦截器

@Configurer
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private TestInterceptor1 testInterceptor1;

    @Autowired
    private TestInterceptor2 testInterceptor2;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(testInterceptor1)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/hello");

        registry.addInterceptor(testInterceptor2).addPathPatterns("/hello");
    }

}

四、总结:

  • Filter需要在web.xml中配置,依赖于Servlet;
  • Interceptor需要在SpringMVC中配置,依赖于框架;
  • 两者的本质区别:拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。
  • 拦截器可以获取IOC容器中的各个bean,而过滤器不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。