过滤器Filter和拦截器Interceptor

文章详细介绍了过滤器Filter和拦截器Interceptor的创建、执行过程、实现原理以及应用场景。过滤器基于函数回调实现,应用于web应用,在servlet前后执行。拦截器则是基于Java反射机制的动态代理实现,可以深入到Controller方法前后,有更广泛的作用范围。两者触发时机和作用范围有所不同,过滤器在请求进入容器后、servlet之前触发,拦截器则在Controller方法之前和之后执行。

目录

一、过滤器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即是它的一个实现类
      2. 因此执行自定义过滤器中的doFilter()方法中的filterChain.doFilter(servletRequest,servletResponse),即是执行ApplicationFilterChain中的doFilter(servletRequest,servletResponse)方法,该方法会获取容器中的所有过滤器,并依次执行各自的doFilter()方法,从而实现所有过滤器的执行
  4. 应用场景

    1. 过滤器只能在web应用中使用
      1. 过滤器实现的是javax.servlet.Filter接口,即依赖于类似tomcat等servlet容器

二、拦截器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
      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!";
          }
      }
    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容器而单独使用

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

  1. 触发时机的不同

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

  2. 作用范围不同

    1. 基于各自实现原理和触发时机的不同,导致过滤器Filter只在servlet前后起作用,它对几乎所有请求可起作用;而拦截器Interceptor可深入到方法前后、异常抛出前后,对请求起作用,也可访问上下文、栈里的对象等;因此在spring框架中,可优先使用拦截器。
在Spring中,过滤器Filter)、拦截器InterceptorAOP(面向切面编程)既有区别又有联系。 ### 区别 - **实现机制**:拦截器基于Java的反射机制,使用代理模式;而过滤器基于函数回调。拦截器不依赖servlet容器,过滤器依赖于servlet容器[^2]。 - **作用范围**:拦截器只能对action起作用,而过滤器可以对几乎所有的请求起作用,能够保护资源[^2]。 - **访问权限**:拦截器可以访问action上下文堆栈里面的对象,过滤器则不能[^2]。 - **执行顺序**:执行顺序为过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后[^2]。 ### 联系 它们都可以对程序的流程进行拦截处理,起到增强功能控制访问的作用。例如在Spring Boot中,都可以对请求进行一定的前置或后置处理。而且它们都可以用于实现一些通用的功能,如日志记录、权限验证等。 ### 代码示例 以下是简单的过滤器拦截器AOP的代码示例: #### 过滤器示例 ```java import javax.servlet.*; import java.io.IOException; public class MyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 过滤前处理 System.out.println("Filter: Before processing"); chain.doFilter(request, response); // 过滤后处理 System.out.println("Filter: After processing"); } } ``` #### 拦截器示例 ```java import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 拦截前处理 System.out.println("Interceptor: Before handler execution"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 拦截后处理 System.out.println("Interceptor: After handler execution"); } } ``` #### AOP示例 ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class MyAspect { @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() {} @Before("serviceMethods()") public void beforeServiceMethod(JoinPoint joinPoint) { // 增强处理(前置通知) System.out.println("AOP: Before service method execution"); } @After("serviceMethods()") public void afterServiceMethod(JoinPoint joinPoint) { // 增强处理(后置通知) System.out.println("AOP: After service method execution"); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值