Bootstrap

SpringMVC拦截器详解

目录

1.拦截器概述

2.拦截器的配置

3.拦截器的执行流程

   3.1单个拦截器的执行流程

   3.2测试代码及结果展示

   3.3多个拦截器的执行流程

   3.4测试代码及结果展示


1.拦截器概述

对于优秀的MVC框架,都会提供一些通用的操作,如请求数据的封装、类型转换、数据校验、解析上传的文件、防止表单的多次提交等。早期的MVC框架将这些操作都写死在核心控制器中,而这些常用的操作又不是所有的请求都需要实现的,这就导致了框架的灵活性不足,可扩展性降低,对此SpringMVC提供了Interceptor拦截器机制,类似于Servlet中的Filter过滤器,用于拦截用户的请求并做出相应的处理。比如通过拦截器来进行用户权限验证,或者用来判断用户是否已经登录。Spring MVC拦截器是可插拔式的设计,需要某一功能拦截器,只需在配置文件中应用该拦截器即可;如果不需要这个功能拦截器,只需在配置文件中取消应用该拦截器。

要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义。

1.实现HandlerInterceptor接口

2.实现WebRequestInterceptor接口

此处对于第一种实现HandlerInterceptor接口方法来详细介绍:

来看看HandlerInterceor接口的源码:

1.preHandle()方法

调用时间:Controller方法处理之前

返回值为Boolean类型,如果返回false,表示拦截请求,不再向下执行,如果返回true,表示放行,程序继续向下执行(如果后面没有其他Interceptor,就会执行controller方法)。所以此方法可对请求进行判断,决定程序是否继续执行,或者进行一些初始化操作及对请求进行预处理。

若返回false,则中断执行,注意:不会进入afterCompletion

2.postHandle()方法

调用前提:preHandle返回true

调用时间:Controller方法处理完之后由于该方法会在DispatcherServlet进行返回视图渲染之前被调用,所以此方法多被用于处理返回的视图,可通过此方法对请求域中的模型和视图做进一步的修改。

备注:postHandle虽然post打头,但post、get方法都能处理

3.afterCompletion()方法

调用前提:preHandle返回true

调用时间:DispatcherServlet进行视图的渲染之后,由于是在Controller方法执行完毕后执行该方法,所以该方法适合进行一些资源清理,记录日志信息等处理操作。

2.拦截器的配置

Spring MVC 中配置拦截器(Interceptor),可以通过Java配置类的方式进行配置,也可以通过xml文件的方式进行配置。

先从Java配置类的方式来看:

  • @EnableWebMvc : 表示启用Spring MVC的功能
  • @Configuration: 表示该类是Spring 中的配置类
  • addPathPatterns来表示拦截哪些请求
  • excludePathPatterns不拦截哪些请求
  • :匹配一个字符
  • * : 任意一级路径
  • **:任意多级路径
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppConfig implements WebMvcConfigurer {
    /**
     * SpringMVC会遍历每个Controller,来设置一些信息
     * 为所有Controller添加路径前缀
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("api",c->{
            System.out.println(c.getClass());
            return true;
        });
    }

    /**
     * 添加拦截器
     * @param registry 拦截器注册器(注册拦截器:路径规则+拦截器对象)
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DemoInterceptor())
                //拦截所有的后端路径
                .addPathPatterns("/api/**")
                //排除登录和注册接口
                .excludePathPatterns("/api/user/login")
                .excludePathPatterns("/api/user/register");
    }
}

在xml中进行配置:

<mvc:interceptors>
	<bean class="com.example.web.interceptor.DemoInterceptor"/>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/admin/**"/>
        <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/secure/*"/>
        <bean class="org.example.SecurityInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

3.拦截器的执行流程

3.1单个拦截器的执行流程

在运行程序时,拦截器的执行是有一定顺序的,该顺序与配置文件中所定义的拦截器的顺序相关。
单个拦截器,在程序中的执行流程如下图所示:

1.程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行。

2.在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应。

3.在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。

3.2测试代码及结果展示

测试案例:通过一个测试程序来验证它的执行流程。

新建一个测试controller,代码如下:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DemoController {
    @RequestMapping("/hello")
    public String hello(){
        System.out.println("hello world");
        return "successed";
    }
}

然后,新建一个拦截器,实现HandlerInterceptor接口,并实现其中的方法。

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DemoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("DemoInterceptor......preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("DemoInterceptor......postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("DemoInterceptor......afterCompletion");
    }
}

我们使用postman来测试接口:

 

运行项目查看结果:

 3.3多个拦截器的执行流程

多个拦截器(假设有两个拦截器Interceptor1和Interceptor2,并且在配置文件中, Interceptor1拦截器配置在前),在程序中的执行流程如下图所示:

从图可以看出,当有多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。

3.4测试代码及结果展示

测试案例:新建两个拦截器: 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DemoInterceptor implements HandlerInterceptor {
    private static final Logger log= LoggerFactory.getLogger(DemoInterceptor.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("DemoInterceptor......preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("DemoInterceptor......postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("DemoInterceptor......afterCompletion");
    }
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SecondInterceptor implements HandlerInterceptor {
    private static final Logger log= LoggerFactory.getLogger(DemoInterceptor.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("SecondInterceptor......preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("SecondInterceptor......postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("SecondInterceptor......afterCompletion");
    }
}

测试运行:

从结果可以看出,执行的顺序和图片中是一样的。 

如果第一个拦截器return true; 而第二个拦截器 return false;结果如下:

 

;