07.springMVC过滤器和拦截器

157 阅读5分钟

概述

总的来说不管是过滤器还是拦截器,他们都是用来拦截请求,并且做一些自己想要特殊处理。其本质都是AOP的提现。但是他们的实现方式、作用范围和处理的时机都不太一样。来看一个比较粗略的图:

image-20230403140736173

在请求到达容器前,首先会进入Filter过滤器链,执行完过滤器链上每个Filter.doFilter()方法后,进入Servlet.service()方法,然后由dispatcher分发器将请求方法给对应映射成功的处理器controller,在进入controller具体方法之前,会被先进入Interceptor.preHandler()方法,然后再进入controller的具体返回,执行之后进入Interceptor.postHandler()这里主要是拦截了controller方法执行之后到返回的数据模型到达视图解析器之前,接着进入Interceptor.afterCompletion()方法,主要可以操作返回客户端之前的逻辑,最后返回到过滤链中各个Filter的调用点,可以处理返回到客户端的跳转等逻辑。下面是一个更为详细的执行流程图:

image-20230403143523962

过滤器(Filter)

1. 过滤器定义

一个实现了特殊接口(Filter)的Java类,实现对请求资源(jsp、servlet、html)的过滤功能。过滤器是一个运行在服务器的程序,优先于请求资源(jsp、servlet、html)之前执行, 过滤器是javaweb技术中最为实用的技术之一。

2. 过滤器的应用场景

由于过滤器是在目标资源(Servlet,jsp)之前执行,所以比较适合的应用场景有:登录权限检查、解决乱码、敏感字符过滤等场景。

3. Filter接口方法

  • init()

    该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次,这个方法必须执行成功,否则过滤器会不起作用。

  • doFilter()

    容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。

  • destroy()

    当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次。

4. 过滤器示例

  • Filter类

    public class MyFilter1 implements Filter {
    ​
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("MyFilter1初始化方法");
        }
    ​
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("MyFilter1,客户端请求被拦截了");
            chain.doFilter(request, response);
            System.out.println("MyFilter1,返回客户端的信息被拦截了");
        }
    ​
        @Override
        public void destroy() {
            System.out.println("MyFilter1销毁方法");
        }
    }
    

    这里可以再有一个MyFilter2类,可以用于观察多个过滤的前后执行顺序

  • 配置web.xml

    <!-- 配置两个过滤器,执行循序与配置的前后顺序有关 -->
        <filter>
            <filter-name>myFilter1</filter-name>
            <filter-class>com.mayuanfei.filter.MyFilter1</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>myFilter1</filter-name>
            <!-- /*是对所有的文件进行拦截 -->
            <url-pattern>/*</url-pattern>
        </filter-mapping>
        <filter>
            <filter-name>myFilter2</filter-name>
            <filter-class>com.mayuanfei.filter.MyFilter2</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>myFilter2</filter-name>
            <!-- /*是对所有的文件进行拦截 -->
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    

    这里可以把myFilter2和myFilter1的配置顺序调整下,然后观察

  • 测试Controller

    @RestController
    public class MyController {
    ​
        @RequestMapping("/test")
        public UserInfo test() {
            UserInfo userInfo = new UserInfo();
            userInfo.setUserid("1111");
            userInfo.setUsername("张三");
            userInfo.setAge(28);
            System.out.println(userInfo);
            return userInfo;
        }
    }
    
  • 观察请求

    image-20230403172026610

拦截器(Interceptor)

1. 拦截器定义

Spring MVC拦截器是一种用于拦截HTTP请求和响应的机制。它可以在请求到达控制器之前或响应返回客户端之前执行一些操作,是一种应用于业务处理的AOP技术。

需要实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter类

2. 拦截器的应用场景

基本Filter的场景它也都适用,比如:记录日志、权限验证、设置字符编码等操作

3. 拦截器的核心API

SpringMVC拦截器提供三个方法分别是preHandle、postHandle、afterCompletion,我们就是通过这几个方法来对用户的请求进行拦截处理的。

  • preHandle() :这个方法将在请求处理之前进行调用。「注意」:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
  • postHandle():只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。「有意思的是」:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。
  • afterCompletion():只有在 preHandle() 方法返回值为true 时才会执行,在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。

4. 拦截器示例

  • Interceptor类

    public class MyInterceptor1 implements HandlerInterceptor {
        @Resource
        private LogMapper logMapper;
    ​
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("MyInterceptor1的preHandle执行");
            // 返回true代表放行,可以继续到handler;否则无法执行我们的handler方法.
            return true;
        }
    ​
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("MyInterceptor1的postHandle执行");
        }
    ​
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("MyInterceptor1的afterCompletion执行");
            // 插入日志
            this.logMapper.insertLog();
        }
    }
    

    这里可以再有一个MyInterceptor2类,可以用于观察多个过滤的前后执行顺序

  • 配置 springmvc.xml

    <!--注册拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="myInterceptor1" class="com.mayuanfei.interceptor.MyInterceptor1"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="myInterceptor2" class="com.mayuanfei.interceptor.MyInterceptor2"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
    
  • 观察请求

    image-20230403173918666

过滤器和拦截器的区别

  • Filter是servlet的规范;而Interceptor是由spring框架支持的
  • 正是由于Interceptor由spring框架支持,所以能在拦截器中很容易的注入各种对象;但在Filter中不行。
  • Interceptor在其生命周期中,几个方法可以多次被调用;Filter除了doFilter,只能在容器初始化调用一次。
  • Filter只能在servlet的前后起作用,而拦截器能在方法前后异常前后执行,更加灵活,粒度更小

代码地址

gitee.com/mayuanfei/s…下的springmvc03

记忆印记

  • 过滤器时Servlet规范中的;拦截器是spring框架中的
  • 过滤器中获得Spring框架容器中的对象很费劲;而拦截器很方便
  • 过滤器是在拦截器前执行的,这意味着过滤器处理过的对象,在拦截器中可以直接使用。
  • 过滤器和拦截器都以配置的前后,顺序执行的。