SpringMVC_中的拦截器

7. SpringMVC中的拦截器

Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对请求进行预处理和后处理。

用户可以自己定义一些拦截器来实现特定的功能。

谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺

序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
在这里插入图片描述
说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,都是基于AOP编程思想的体现,都能实现权限检查、日志记录等,但是也有区别,例如:

  • 使用范围不一样
    • 过滤器是 servlet规范中的一部分,只能用于web工程中。
    • 拦截器是 SpringMVC框架自己的,只有使用了 SpringMVC框架的工程才能用。
  • 规范不同:
    • Filter实在servlet规范中定义的,是servlet容器支持的。
    • 拦截器(interceptor)是在Spring容器内的,是Spring框架支持的。
  • 使用资源不同
    • 拦截器是Spring的组件,因此能够使用Spring里的任何资源、对象,而Filter不能
  • 深度不同
    • Filter只在servlet前后起作用,
    • 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用更具有弹性,因此在Spring架构中优先使用拦截器

截的。

它也是 AOP思想的具体应用。

我们要想自定义拦截器, 要求必须实现:HandlerInterceptor接口。

7.1. 自定义拦截器

  1. 编写一个普通类实现 HandlerInterceptor 接口
  2. 配置拦截器
  3. 测试运行结果:
7.1.1. 编写一个普通类实现 HandlerInterceptor 接口
public class HelloInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler) throws Exception {
        
        System.out.println("preHandle...controller之前执行");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, 
                           	HttpServletResponse response, 
                           	Object handler, 
                           	ModelAndView modelAndView) throws Exception {
        
        System.out.println("postHandle...jsp之前执行");
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request,
                                    HttpServletResponse response,
                                    Object handler,
                                    Exception ex) throws Exception {

        System.out.println("afterCompletion...jsp 之后执行");
    }

}
7.1.2. 配置拦截器

springmvc.xml中配置拦截器

<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="helloInterceptor" 
              	class="com.czxy.mvc.interceptor.HelloInterceptor">
        </bean>
    </mvc:interceptor>
</mvc:interceptors>
7.1.3. 测试
  • controller
@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(){
        System.out.println("hello controller...");
        return "forward:/success.jsp";
    }

    @RequestMapping("/user/list")
    public String userList(){
        System.out.println("userList controller...");
        return "forward:/success.jsp";
    }
}
  • success.jsp
<body>
    <h1>success</h1>

    <% System.out.println("success.jsp 执行了...");%>

</body>
  • 访问路径:

    扫描二维码关注公众号,回复: 11496979 查看本文章
    • http://localhost:8080/hello
    • http://localhost:8080/user/list
  • 控制台输出:

    preHandle…controller之前执行
    userList controller…
    postHandle…
    success.jsp 执行了…
    afterCompletion…controller之后执行

    preHandle…controller之前执行
    userList controller…
    postHandle…
    success.jsp 执行了…
    afterCompletion…controller之后执行

  • 案例结论:

    • 每次请求都要经过拦截器
    • 拦截器不同的方法执行时机不一样

7.2. 拦截器细节

7.2.1. 拦截器的放行

preHandle()方法返回值为true表示放行

放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器

中的方法。

@Override
public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler) throws Exception {
    System.out.println("preHandle...controller之前执行");
    //返回true表示放行
    return true;
}
7.2.2. 拦截器方法说明
  • 拦截器方法执行顺序图
    在这里插入图片描述

preHandle

@Override
public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler) throws Exception {
    
    System.out.println("preHandle...controller之前执行");
    //返回true表示放行
    //return true;

    request.getRequestDispatcher("/error.jsp").forward(request,response);
    return false;
}
  • 执行时机
  • 该方法将在请求处理之前进行调用。
  • 返回值特点
    • 该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,可以返回到其他页面,后续的Interceptor 和Controller 都不会再执行;
    • 当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
  • 作用
    • 可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。典型的应用场景:登录拦截、权限校验

postHandle

@Override
public void postHandle(HttpServletRequest request,
                       HttpServletResponse response,
                       Object handler,
                       ModelAndView modelAndView) throws Exception {

    modelAndView.addObject("token","CZXY-2020-07-03");

    System.out.println("postHandle...");
}
  • 执行时机
    • preHandle 方法的返回值为true 时才能被调用,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,
    • postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行
  • 作用
    • 可以在这个方法中对Controller 处理之后的ModelAndView 对象和request、response进行操作。

afterCompletion

@Override
public void afterCompletion(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler,
                            Exception ex) throws Exception {

    System.out.println("afterCompletion...controller之后执行");
}
  • 执行时机
    • 当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。
    • 该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。
  • 作用
    • 这个方法的主要作用是用于进行资源清理、记录日志信息等工作。
7.2.3. 拦截器的配置
<!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--<mvc:mapping>用于配置拦截器作用的路径,该路径在其属性path 中定义。-->
            <!--path 的属性值“/**” 表示拦截所有路径,-->
            <!--“/hello” 表示拦截所有以 “/hello” 结尾的路径-->
            <mvc:mapping path="/**"/>
            <!-- 用于指定排除的 url-->
            <mvc:exclude-mapping path="/login"/>
            <bean id="helloInterceptor1" 			class="com.czxy.mvc.interceptor.HelloInterceptor"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/user/**"/>
            <bean id="helloInterceptor2" class="com.czxy.mvc.interceptor.HelloInterceptor2"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/user/list"/>
            <bean id="helloInterceptor3" class="com.czxy.mvc.interceptor.HelloInterceptor3"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
  • 拦截器配置资源解析器放行静态资源
<mvc:resources location="/js/" mapping="/**/*.js"/>
<mvc:resources location="/css/" mapping="/**/*.css"/>
<mvc:resources location="/assets/" mapping="/assets/**"/>
<mvc:resources location="/images/" mapping="/images/**" cache-period="360000"/>

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**/*"/>
      <!--设置不拦截的资源  设置哪些请求不拦截-->
        <mvc:exclude-mapping path="/**/fonts/*"/>
        <mvc:exclude-mapping path="/**/*.css"/>
        <mvc:exclude-mapping path="/**/*.js"/>
        <mvc:exclude-mapping path="/**/*.png"/>
        <mvc:exclude-mapping path="/**/*.gif"/>
        <mvc:exclude-mapping path="/**/*.jpg"/>
        <mvc:exclude-mapping path="/**/*.jpeg"/>
        
        <bean class="com.czxy.mvc.interceptor.HelloInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>
7.2.4. 拦截器链的方法执行顺序

在这里插入图片描述

  1. 符合path拦截规则的拦截器都会执行
  2. 拦截器执行顺序有配置决定,配置在前面的先执行
  3. 拦截器preHandle 方法返回false,后续拦截器不会执行,也不会执行controller,前面如果有拦截器,前面拦截器的afterCompletion还是会执行。
  4. 前面的拦截器preHandle 方法先执行,但是postHandle、afterCompletion方法后执行。

7.3. 拦截器小案例

验证用户是否登录:用户登录后跳转到main.jsp,可以在main.jsp查询数据,如果没登录不能查询,需要重定向到登录页

  1. 添加登录页和主页

  2. 编写登录controller

    1. 验证是否能登录
  3. 编写查询controller,模拟用户查询操作

    1. 测试是否能查询
  4. 编写拦截器,对除了登录以外的请求,进行进行拦截判断是否已登录

  5. 测试

  6. 添加登录页和主页

  • login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/user/login" method="post">
        账号:<input type="text" name="account">  <br/>
        密码:<input type="password" name="password"><br/>
        <input type="submit" value="登录">
    </form>
</body>
</html>
  • main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
<h1>首页</h1>

    <a href="/user/list">查询员工列表</a>

</body>
</html>
  1. 编写登录controller
    /**
     * 模拟管理员root:root登录
     * 登录之后用户信息存入session中
     * 跳转到主页/WEB-INF/pages/main.jsp
     * @param account
     * @param password
     * @return
     */
    @RequestMapping("/user/login")
    public String login(String account, String password, HttpSession session){
        if(StringUtils.isEmpty(account) || StringUtils.isEmpty(password))
            return "redirect:/login.jsp";
        if(account.equals("root") && password.equals("root")){
            //登录成功,将用户信息存入session中
            session.setAttribute("user",account);
            return "forward:/WEB-INF/pages/main.jsp";
        }
        //账号密码不对,返回登录页面
        return "redirect:/login.jsp";
    }
  1. 验证是否能正常登陆访问main.jsp
  2. 添加查询方法
@RequestMapping(value = "/user/lisg",method = RequestMethod.GET)
public String userList(){
    System.out.println("模拟冲数据库查询数据...");
    return "forward:/WEB-INF/pages/main.jsp";
}
  1. 拦截器实现登录拦截
/**
     * 登录的请求需要放行:
     *      可以用配置的方式,也可以用代码的方式实现
     * 验证用户是否登录:
     *      登录:放行
     *      未登录:重定向到登录页面
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
@Override
public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler) throws Exception {

    //StringBuffer url = request.getRequestURL();  // http://localhost:8080/user/login
    //获取请求路径
    String servletPath = request.getServletPath(); // /user/login
    //登录请求放行
    if (servletPath.equals("/user/login")){
        return true;
    }
    //判断是否登录
    String user = (String) request.getSession().getAttribute("user");
    if (user != null){
        return true;
    }
    //未登录:重定向到登录页
    response.sendRedirect("/login.jsp");
    return false;
}
  1. 配置拦截器
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean id="loginInterceptor" class="com.czxy.mvc.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

使用配置的方式,放行登录请求

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <!-- 哪些请求不拦截 -->
        <mvc:exclude-mapping path="/user/login"></mvc:exclude-mapping>
        <mvc:exclude-mapping path="/login"></mvc:exclude-mapping>
        <bean id="loginInterceptor" class="com.czxy.mvc.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

猜你喜欢

转载自blog.csdn.net/qq_44509920/article/details/107700841