1.拦截器与过滤器的区别

:::tips 过滤器就是过滤掉一些不需要的东西(eg:你家大米里面有石头,你需要把石头筛掉,这就是过滤)。拦截器是你希望干预他的进程(eg:不明人员想进校园,被保安拦下,要有证件才能入内。这就是拦截器)。 :::

  • 拦截器是基于java的反射机制的,而过滤器是基于函数的回调。
  • 拦截器不依赖于servlet容器,而过滤器依赖于servlet容器。
  • 拦截器只对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  • 拦截器可以访问action上下文、值、栈里面的对象,而过滤器不可以。
  • 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
  • 拦截器可以获取IOC容器中的各个bean,而过滤器不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

Springboot中如何使用拦截器与过滤器 - 图1

2.拦截器与过滤器的触发时机

:::tips 过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。
过滤器包裹servlet,servlet包裹住拦截器。 :::

3.Springboot中使用Filter过滤器

:::danger 使用场景:在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。
缺点:一个过滤器实例只能在容器初始化时调用一次 ::: 方式一 :::tips @Order里边的数字越小代表越先被该Filter过滤,@WebFilter代表这是个Filter类并把这个类注入到容器中 ::: 【步骤1 】 :::info 自定义 MyFilter implements Filter 重写doFilter()方法 :::

  1. //
  2. @Slf4j
  3. @Order(3)
  4. @WebFilter(filterName ="myFilter3" ,urlPatterns = "/*")
  5. public class MyFilter implements Filter {
  6. @Override
  7. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  8. log.info("MyFilter3---开始");
  9. chain.doFilter(request,response);
  10. }
  11. }

【步骤二】 :::info 启动类添加注解:@ServletComponentScan :::

  1. @ServletComponentScan
  2. @SpringBootApplication
  3. public class SpringbootFilterInterceptorApp {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SpringbootFilterInterceptorApp.class, args);
  6. }
  7. }

方式二
【步骤1 】 :::info 自定义 MyFilter implements Filter 重写doFilter()方法 :::

  1. @Slf4j
  2. public class MyFilter implements Filter {
  3. @Override
  4. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  5. log.info("执行过滤器MyFilter---开始");
  6. chain.doFilter(request,response);
  7. }
  8. }

【步骤二:】 :::info 自定义MyConfig配置类,并注册自定义类到FilterRegistrationBean :::

  1. @Configuration
  2. public class MyConfig {
  3. @Bean
  4. MyFilter myFilter() {
  5. return new MyFilter();
  6. }
  7. @Bean
  8. public FilterRegistrationBean<MyFilter> filterRegistrationBean(MyFilter myFilter) {
  9. FilterRegistrationBean<MyFilter> filterRegistrationBean = new FilterRegistrationBean<>();
  10. filterRegistrationBean.setFilter(myFilter);
  11. filterRegistrationBean.setOrder(1);
  12. filterRegistrationBean.addUrlPatterns("/*");
  13. return filterRegistrationBean;
  14. }
  15. }

执行流程
Springboot中如何使用拦截器与过滤器 - 图2

4.springboot中使用Interceptor拦截器

:::danger 使用场景:比如动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。
优点:同时一个拦截器实例在一个controller生命周期之内可以多次调用。
缺点:只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。 ::: 使用拦截器步骤: :::info 创建拦截器实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter类:

  • 继承关系:HandlerInterceptorAdapter—->AsyncHandlerInterceptor—>HandlerInterceptor
  • 通过继承关系可知,我们可以直接通过继承拦截器适配器类来创建自定义的拦截器,这样需要哪个方法就直接重写哪个即可

自定义配置类继承 WebMvcConfigurerAdapter 类或者 WebMvcConfigurationSupport类 :

  • 同样道理,使用适配器类 ::: 【步骤一】

    1. @Slf4j
    2. public class MyInterceptor implements HandlerInterceptor {
    3. @Override
    4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    5. log.info("进入拦截器之前>>>>>>>>>>>>>");
    6. String name = request.getParameter("name");
    7. if (name.equals("mck")){
    8. return true;
    9. }
    10. return false;
    11. }
    12. @Override
    13. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    14. log.info("进入拦截器中>>>>>>>>>>>>>");
    15. }
    16. @Override
    17. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    18. log.info("拦截器结束>>>>>>>>>>>>>");
    19. }
    20. }

    【步骤二】 ```java @Configuration public class MyMvcConfig implements WebMvcConfigurer {

  1. @Bean
  2. public MyInterceptor getMyInterceptor() {
  3. return new MyInterceptor();
  4. }
  5. @Override
  6. public void addInterceptors(InterceptorRegistry registry) {
  7. registry.addInterceptor(getMyInterceptor())
  8. .addPathPatterns("/**")//// 拦截所有路径
  9. .excludePathPatterns("/system/max/login");// 不拦截的路径
  10. }

}

  1. **工作原理**<br />一个拦截器,只有preHandle方法返回truepostHandleafterCompletion才有可能被执行;如果preHandle方法返回false,则该拦截器的postHandleafterCompletion必然不会被执行。拦截器不是Filter,却实现了Filter的功能,其原理在于:
  2. 1. 所有的拦截器(Interceptor)和处理器(Handler)都注册在HandlerMapping中。
  3. 1. Spring MVC中所有的请求都是由DispatcherServlet分发的。
  4. 1. 当请求进入DispatcherServlet.doDispatch()时候,首先会得到处理该请求的Handler(即Controller中对应的方法)以及所有拦截该请求的拦截器。拦截器就是在这里被调用开始工作的。
  5. **拦截器工作流程:**
  6. ```java
  7. 1.Interceptor2.preHandle
  8. 2.Interceptor1.preHandle
  9. 3.Controller处理请求
  10. 4.Interceptor1.postHandle
  11. 5.Interceptor2.postHandle
  12. 6.渲染视图view
  13. 2.Interceptor1.afterCompletion
  14. 2.Interceptor2.afterCompletion
  1. 如果在Interceptor1.preHandle中报错或返回false ,那么接下来的流程就会被中断,但注意被执行过的拦截器的afterCompletion仍然会执行。

和Filter共存时的执行顺序 :::danger 拦截器是在DispatcherServlet这个servlet中执行的,因此所有的请求最先进入Filter,最后离开Filter。其顺序如下。
Filter->Interceptor.preHandle->Handler->Interceptor.postHandle->Interceptor.afterCompletion->Filter :::

5.拦截器和过滤器如何取舍

:::danger 在使用场景上有些时候都能使用,但是我们怎么去区分到底使用哪个更好呢?

  1. 当需要过滤掉其中的部分信息,只留一部分时,就用过滤器。
  2. 当需要对其流程进行更改,做相关的记录时用拦截器。 :::