过滤器Filter和拦截器Interceptor

目录

一、过滤器Filter

创建过滤器

过滤器执行

实现原理

应用场景

二、拦截器Interceptor

创建过滤器

过滤器执行

实现原理

应用场景

三、过滤器和拦截器的其他区别

触发时机的不同

作用范围不同

一、过滤器Filter

  1. 创建过滤器

    1. @Component注解,实现Filter接口,重写init()、doFilter()和destroy()方法
      @Component
      public class MyFilter1 implements Filter {
      
          /**
           * 容器初始化时执行过滤器初始化方法
           * @param filterConfig
           * @throws ServletException
           */
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
              System.out.println("执行MyFilter1初始化方法init()。。。");
          }
      
          /**
           * 每次发起请求后,在Controller方法前执行
           * @param servletRequest
           * @param servletResponse
           * @param filterChain
           * @throws IOException
           * @throws ServletException
           */
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              System.out.println("执行MyFilter1过滤器方法doFilter()...");
              filterChain.doFilter(servletRequest,servletResponse);
          }
      
          /**
           * 容器销毁时执行销毁过滤器方法
           */
          @Override
          public void destroy() {
              System.out.println("执行MyFilter1销毁方法destroy()。。。");
          }
      }
  2. 过滤器执行

    1. 容器初始化时,会且仅有一次执行init()方法
    2. 每次发起被拦截的请求时,都会调用doFilter()方法,然后通过调用其中的FilterChain回调接口的doFilter()回调方法,去依次调用其他过滤器的处理方法(doFilter())
    3. 容器销毁时,会且仅有一次执行过滤器销毁方法(destroy())
  3. 实现原理

    1. 过滤器的底层时基于函数回调实现的
      1. 我们自定义的过滤器,都会实现其处理方法doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain),其中的FilterChain类型参数,它实际上是一个回调接口,而ApplicationFilterChain即是它的一个实现类过滤器Filter和拦截器Interceptor_第1张图片
      2. 因此执行自定义过滤器中的doFilter()方法中的filterChain.doFilter(servletRequest,servletResponse),即是执行ApplicationFilterChain中的doFilter(servletRequest,servletResponse)方法,该方法会获取容器中的所有过滤器,并依次执行各自的doFilter()方法,从而实现所有过滤器的执行过滤器Filter和拦截器Interceptor_第2张图片过滤器Filter和拦截器Interceptor_第3张图片
  4. 应用场景

    1. 过滤器只能在web应用中使用
      1. 过滤器实现的是javax.servlet.Filter接口,即依赖于类似tomcat等servlet容器过滤器Filter和拦截器Interceptor_第4张图片

二、拦截器Interceptor

  1. 创建过滤器

    1. @Component注解,实现HandlerInterceptor接口,重写preHandle()、postHandle()和afterCompletion()方法
      @Component
      public class MyInterceptor1 implements HandlerInterceptor {
      
          /**
           * 每次发起请求后,在Controller方法前执行过滤器方法
           * @param request
           * @param response
           * @param handler
           * @return
           * @throws Exception
           */
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("执行拦截器MyInterceptor1方法preHandle()...");
              response.setHeader("sss","preHandle");
              return true;
          }
      
          /**
           * preHandle()方法返回true时,在Controller方法调用后,DispatcherServlet返回渲染视图后执行
           * @param request
           * @param response
           * @param handler
           * @param modelAndView
           * @throws Exception
           */
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("执行拦截器MyInterceptor1方法postHandle()...");
              response.setHeader("ddd","postHandle");
              if (null != modelAndView){
                  System.out.println("view name------"+modelAndView.getViewName());
                  modelAndView.addObject("kkk","postHandle");
              }
          }
      
          /**
           * preHandle()方法返回true时,在Controller方法调用后,DispatcherServlet返回渲染视图后执行
           * @param request
           * @param response
           * @param handler
           * @param ex
           * @throws Exception
           */
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("执行拦截器MyInterceptor1的处理后方法afterCompletion()...");
              response.setHeader("hhh","afterCompletion");
          }
      }
    2. 并需要在新建一个mvc配置文件(@Configuration注解,实现WebMvcConfigurer接口),在配置文件中注册拦截器(重写addInterceptors()方法)
      @Configuration
      public class MyMvcConfig implements WebMvcConfigurer {
      
          /**
           * 注册拦截器,并添加待拦截的请求路径
           * @param registry
           */
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              System.out.println("开始注册拦截器。。。");
              registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
              registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
              System.out.println("结束注册拦截器。。。");
          }
      }
      1. 此时要注意,在注册拦截器时,如果是使用上面方法,直接new的拦截器实例(如new MyInterceptor1()),即意味着该实例不会被spring管理,则类MyInterceptor1中注入的其他bean或service组件都会是null过滤器Filter和拦截器Interceptor_第5张图片
      2. 要解决这个问题,就应该在注册拦截器时,通过依赖注入的方式,将拦截器实例交给spring管理,进而完成service等组件的注入;而非自己new
        @Configuration
        public class MyMvcConfig implements WebMvcConfigurer {
        
            /**
             * 向容器中注入自定义拦截器
             * @return
             */
        //    @Bean
        //    public MyInterceptor1 myInterceptor1(){
        //        System.out.println("向容器中注入拦截器myInterceptor1实例...");
        //        return new MyInterceptor1();
        //    }
            @Autowired
            MyInterceptor1 myInterceptor1;
            @Autowired
            MyInterceptor2 myInterceptor2;
        
            /**
             * 注册拦截器,并添加待拦截的请求路径
             * @param registry
             */
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                System.out.println("开始注册拦截器。。。");
                registry.addInterceptor(myInterceptor1).addPathPatterns("/**");
        //        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
                registry.addInterceptor(myInterceptor2).addPathPatterns("/**");
        //        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
                System.out.println("结束注册拦截器。。。");
            }
        }
  2. 过滤器执行

    1. 容器初始化时,会通过配置文件,注册拦截器
    2. 每次发起被拦截的请求时,首先执行过滤器处理方法(doFilter()),然后按注册顺序执行拦截器处理方法(preHandler()),再执行controller方法,再按注册倒序执行拦截器方法postHandler(),最后按注册倒序执行拦截器销毁方法(afterCompletion())
      @Controller
      @RequestMapping("/")
      @ResponseBody
      public class HelloWorld {
      
          /**
           * Controller方法
           * @return
           */
          @RequestMapping("/hello")
          public String hello(){
              System.out.println("我是controller!");
              return "Hello World!";
          }
      }
      过滤器Filter和拦截器Interceptor_第6张图片过滤器Filter和拦截器Interceptor_第7张图片
    3. preHandle()方法调用后,如果返回值是false,则视为当前请求结束,不仅自身会失效,后面的拦截器也会失效
    4. postHandle()方法只会在preHandle()方法返回true后执行,具体是在Controller方法调用后,DispatcherServlet返回渲染视图之前调用
    5. afterCompetion()方法只会在preHandle()方法返回true后执行,具体是在整个请求结束之后,DispatcherServlet返回渲染视图之后调用
  3. 实现原理

    1. 拦截器是基于Java的反射机制(动态代理)实现的
  4. 应用场景

    1. 拦截器不仅能应用在web程序中,也可用于Application、Swing等程序中
      1. 拦截器实现的是org.springframework.web.servlet.HandlerInterceptor接口,它是一个spring组件,故可不依赖于tomcat等servlet容器而单独使用过滤器Filter和拦截器Interceptor_第8张图片

三、过滤器和拦截器的其他区别

  1. 触发时机的不同

    1. 过滤器Filter是在请求进入容器后,servlet之前就可触发;在servlet处理完,容器销毁前结束后处理destroy();而拦截器Interceptor是在请求进入servlet之后,Controller方法之前触发;在DispatcherServlet返回渲染视图之后结束后处理afterCompetion()过滤器Filter和拦截器Interceptor_第9张图片

    过滤器Filter和拦截器Interceptor_第10张图片

  2. 作用范围不同

    1. 基于各自实现原理和触发时机的不同,导致过滤器Filter只在servlet前后起作用,它对几乎所有请求可起作用;而拦截器Interceptor可深入到方法前后、异常抛出前后,对请求起作用,也可访问上下文、栈里的对象等;因此在spring框架中,可优先使用拦截器。

你可能感兴趣的:(servlet,java,前端)