Filter和Interceptor

  • 1、过滤器(Filter)
  • 1.1、Filter的三种实现方式
  • 1.1.1、无路径无顺序@Component
  • 1.1.2、有路径无顺序@WebFilter+@ServletComponentScan
  • 1.1.3、有路径有顺序@Configuration
  • 1.2、Filter各方法的作用
  • 2、过滤器(Interceptor)
  • 2.1、code
  • 2.2、方法及作用
  • 3、Filter和Interceptor区别


1、过滤器(Filter)

Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制过滤敏感词汇压缩响应信息等一些高级功能。
作用:
它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
应用场景:
使用Filter验证用户登录安全控制;
防止中文乱码过滤器;

1.1、Filter的三种实现方式

注意,这里只介绍SpringBoot实现过滤器,常见有三种方式,越复杂功能越强大。

1.1.1、无路径无顺序@Component

这种方式最简单,直接实现Filter接口,并使用@Component注解标注为组件自动注入bean。但是缺点是没办法设置过滤的路径,默认是 /* 过滤所有。Filter接口有 init、doFilter、destroy 三个方法,但 init、destroy 是有默认方法实现,可以不重写。
指定@Order()起作用,值越小级别越高越先执行。

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class DemoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        System.out.println("filter1----------" + httpServletResponse.getStatus());
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

1.1.2、有路径无顺序@WebFilter+@ServletComponentScan

这种方式要稍微复杂一点,但更全面。使用 @WebFilter替代 @Component,可以通过该注解设置过滤器的匹配路径。不过需要在启动类中使用@ServletComponentScan。
@ServletComponentScan扫描带@WebFilter、@WebServlet、@WebListener并将帮我们注入bean。
注:@Order()指定 int 值没有起作用,是无效的。修饰的过滤器在加载时,没有使用 @Order 注解,而是使用的类名来实现自定义Filter顺序。

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(filterName = "filter1", urlPatterns = {"/hello/*"})
public class DemoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        System.out.println("filter1----------" + httpServletResponse.getStatus());
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

1.1.3、有路径有顺序@Configuration

这种方式完全通过配置类来实现,在只实现过滤器的接口,并不需要通过任何注解注入IOC容器,都通过配置类来注入。

public class AFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("进入AFilter...");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("退出AFilter...");
    }

    @Override
    public void destroy() {

    }
}
public class BFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("进入BFilter ...");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("退出BFilter ...");
    }
}

配置类:

@Configuration
public class FilterDemoConfig {
    @Bean
    public FilterRegistrationBean aFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        AFilter aFilter = new AFilter();
        filterRegistrationBean.setFilter(aFilter);
        filterRegistrationBean.addUrlPatterns("*");
        filterRegistrationBean.setName("AFilter");
        filterRegistrationBean.setOrder(1); //设置顺序
        return filterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean bFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        SecondBFilter bFilter = new SecondBFilter();
        filterRegistrationBean.setFilter(bFilter);
        filterRegistrationBean.addUrlPatterns("*");
        filterRegistrationBean.setName("BFilter");
        filterRegistrationBean.setOrder(2);
        return filterRegistrationBean;
    }
}

1.2、Filter各方法的作用

init(): 当容器初始化 Filter 时调用,该方法在 Filter 的生命周期只会被调用一次,一般在该方法中初始化一些资源,FilterConfig 是容器提供给 Filter 的初始化参数,在该方法中可以抛出 ServletException 。init 方法必须执行成功,否则 Filter 可能不起作用,
出现以下两种情况时,web 容器中 Filter 可能无效:
a、抛出 ServletException ;b、超过 web 容器定义的执行时间。
doFilter(): 在doFilter()方法中,chain.doFilter()前的一般是对request执行的过滤操作;chain.doFilter后面的代码一般是对response执行的操作;chain.doFilter()执行下一个过滤器或者业务处理器。
destroy(): 当容器销毁 Filter 实例时调用该方法,在Filter的整个生命周期中仅会被调用一次。一般用来释放被该Filter对象占用的资源,例如:关闭数据库连接和IO流。

2、过滤器(Interceptor)

Interceptor拦截器和Filter有本质上的不同,Filter是依赖于Servlet容器,而Interceptor则是依赖于Spring框架,是aop的一种表现,基于Java的动态代理实现的。在SpringBoot中实现拦截器的方式,有点类似于实现过滤器的第三种方式,所以要通过下面两个步骤:

  1. 声明拦截器的类:通过实现 HandlerInterceptor接口,实现preHandlepostHandleafterCompletion方法。
  2. 通过配置类配置拦截器:通过实现WebMvcConfigurer接口,实现addInterceptors方法。

2.1、code

import com.sun.istack.internal.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

public class DemoInterceptor implements HandlerInterceptor {
    /**
     * 预处理回调方法 
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        String methodName = method.getName();
        System.out.println("====拦截到了方法:" + methodName + ",preHandle====");
        return true;
    }

    /**
     * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("====postHandle====");
    }

    /**
     * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("====afterCompletion====");
    }
}
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * spring 2.x 以前,通过继承 WebMvcConfigurerAdapter 类  
 * spring 2.x 之后,实现 WebMvcConfigurer 接口
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");
    }
}

2.2、方法及作用

preHandle(): 预处理回调方法,Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法。实现处理器的预处理(如检查登录),第三个参数为响应的处理器 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
postHandle(): 后处理回调方法,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
afterCompletion(): 该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。

3、Filter和Interceptor区别

1、实现原理不同

过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器则是基于Java的反射机制(动态代理)实现的。

2、使用范围不同

我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。

3、触发时机不同

过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。

拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。

4、拦截的请求范围不同

过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。

5、注入Bean情况不同

拦截器加载的时间点在springcontext之前。

6、控制执行顺序不同

实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。过滤器用@Order注解控制执行顺序,通过@Order控制过滤器的级别,值越小级别越高越先执行。拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。

java servlet filter 能拿到resoponse java filter interceptor_spring