zoukankan      html  css  js  c++  java
  • SpringMVC入门学习(十二)----SpringMVC的拦截器

    1、拦截器介绍

    SpringMVC 中的拦截器类似于 Servlet 开发中的过滤器 Filter ,只不过拦截器的功能更为强大。SpringMVC 中的 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

    拦截器,本质类似于AOP,它主要的应用场景

    • 日志记录:记录请求信息的日志,以便进行信息监控、信息统计等。
    • 权限检查:如登录检测,进入处理器检测是否登录,没有登录返回登录页面。
    • 性能监控:记录拦截器进入处理器和离开处理器的时间。
    • 通用行为:读取cookie中的用户信息放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器的需要都可以使用拦截器实现。

    拦截器的三要素:

    1. 拦截
    2. 过滤
    3. 放行

    2、拦截器的两种实现方式

    • 实现拦截器处理器接口(推荐):org.springframework.web.servlet.HandlerInterceptor
      • preHandle(HttpServletRequest request,HttpServletResponse response, Object handler):预处理回调方法。在Controller前执行,返回true继续执行下一个流程(interceptor或handler)。返回false中断执行,不会再调用拦截器或处理器。可以用于身份认证、身份授权。比如如果认证没有通过表示用户没有登陆,需要此方法拦截不再往下执行(return false),否则就放行(return true)。
      • postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,ModelAndView modelAndView):后处理回调方法。在进入Controller后,返回ModelAndView之前执行,可以通过对ModeAndView进行处理或对视图进行处理,ModeAndView可能为null。应用场景:从modelAndView出发:将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里统一指定视图。
      • afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex):整个请求完毕的回调方法。在视图渲染完毕时回调。应用场景:统一异常处理,统一日志处理等。
    • 继承拦截器适配器类:org.springframework.web.servlet.handler.HandlerInterceptorAdapter
      • 实现拦截器需要重写三个接口,拦截器适配器为这三个方法做了空实现,可以继承这个类,根据需要重写拦截器的1~3个方法。

    3、拦截器的定义与配置

    在 SpringMVC 中,定义拦截器推荐使用实现HandlerInterceptor接口的方式。也就是自定义拦截器必须实现HandlerInterceptor接口,并实现该接口中提供的三个方法,代码如下:

    /**
     * 定义一个拦截器
     */
    public class HandlerInterceptor1 implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("拦截器1的preHandle方法执行了...");
            //返回值true表示放行,false表示不放行
            return true;
        }
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("拦截器1的postHandle方法执 行了");
        }
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("拦截器1的afterCompletion方法执行了");
        }
    }
    

    在spring-mvc.xml配置文件中配置拦截器:

    • 在 <mvc:interceptors>:使用 <mvc:interceptor> 标签对拦截器进行作用范围的设置。
    • 使用<mvc:mapping path="" />:设置处理的请求,可以使用通配符,可以配置多个。
    • 使用<mvc:exclude-mapping path="" />:设置不需要拦截的请求,可以使用通配符,可以配置多个。但是使用的前提是需要先配置需要处理的请求范围,即需要先配置了<mvc:mapping path="" />才行,否则会有错误。
    <!-- 配置拦截器,拦截器可以有0或多个 -->
    <mvc:interceptors>
        <!-- 配置一个拦截器 -->
        <mvc:interceptor>
            <!-- mvc:mapping用于指定当前所注册的拦截器可以拦截的请求路径,path路径/**表示拦截所有请求,两个*表示匹配多级目录URL地址,例如/aaa/bbb/ccc -->
            <mvc:mapping path="/**"/>
            <bean id="handlerInterceptor1" class="com.thr.interceptor.HandlerInterceptor1"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

    启动Tomcat测试,随便访问一个请求,查看控制台的打印结果:

    image

    4、多个拦截器的执行顺序

    在 SpringMVC 中是可以配置多个拦截器,它们按照定义的先后顺序执行,但是拦截器中的方法执行顺序却是不一样的,下面来验证一下。分别创建三个拦截器,它们的配置如下:

    <!-- 配置拦截器,拦截器可以有0或多个 -->
    <mvc:interceptors>
        <!-- 配置一个拦截器 -->
        <mvc:interceptor>
            <!-- mvc:mapping/用于指定当前所注册的拦截器可以拦截的请求路径,url路径/**表示拦截所有请求 -->
            <mvc:mapping path="/**"/>
            <bean id="handlerInterceptor1" class="com.thr.interceptor.HandlerInterceptor1"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="handlerInterceptor2" class="com.thr.interceptor.HandlerInterceptor2"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="handlerInterceptor3" class="com.thr.interceptor.HandlerInterceptor3"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

    启动Tomcat测试,随便访问一个请求,查看控制台的打印结果:

    image

    可以发现,先是执行了拦截器1、2、3 的preHandle()方法,然后再逆序执行了3个拦截器的postHandle()方法,最后逆序执行拦截器的afterCoompletion()方法。

    通过观察分析总结多个拦截器的执行流程如下所示:

    image

    在SpringMVC中,拦截器是一个链式的,只有前面的拦截器放行时后面的拦截器才能够执行,例如把第一个拦截器的predHandle()方法设置为false。

    image

    再次访问请求时,由于第一个拦截器不放行,导致后面的都执行不了,所以拦截器何时放行时非常重要的!!!

    image

    5、拦截器登的录实例

    [1]、创建实现登陆的Controller方法

    /**
     * 登录Controller
     */
    @Controller
    public class LoginController {
     
        @RequestMapping(value = "login")
        public String login(String username, String password, HttpServletRequest request){
            //获取session对象
            HttpSession session = request.getSession();
            //模拟登录,实际从数据库获取
            if ("admin".equals(username)&&"123456".equals(password)){
                session.setAttribute("username",username);
                session.setAttribute("password",password);
                return "success";
            }else {
                //登录失败,返回登录界面,重新登录
                session.setAttribute("errorMsg","账号或密码错误!");
                return "redirect:/login.jsp";
            }
        }
    }
    

    [2]、登陆验证拦截器的实现

    • 如果用户 session 不存在则跳转到登陆页面
    • 如果用户 session 存在放行,则放行继续操作。
    /**
     * 拦截器登录案例
     */
    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println(request.getContextPath());
            ////获取请求的url
            String uri = request.getRequestURI();
            //判断当前请求地址是否是登录地址
            if (uri.indexOf("login")>0){
                return true;
            }
            //获取session对象
            HttpSession session = request.getSession();
            //判断session中是否有用户身份信息,如果不为空,说明用户已经登录过,放行
            String username = (String) session.getAttribute("username");
            if(username != null) {
                return true;
            }
            //执行到这里表示用户身份需要验证,说明用户之前没有登录过,跳转到登录页面
            //转发
            //request.getRequestDispatcher("login.jsp").forward(request, response);
            //重定向
            response.sendRedirect("login.jsp");
            //默认拦截
            return false;
        }
     
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        }
     
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        }
    }
    

    [3]、配置该拦截器:

    <!--  配置登录拦截器  -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="loginInterceptor" class="com.thr.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

    [4]、登录页面以及回显的页面

    • 登录页面:login.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>登录</title>
    </head>
    <body>
    <form action="${pageContext.request.contextPath}/login" method="post">
        <table style=" 300px;height: 100px;">
            <tr>
                <td style="text-align: center">用户名:</td>
                <td><input type="text" name="username" id="username"/></td>
            </tr>
            <tr>
                <td style="text-align: center">密  码:</td>
                <td><input type="password" name="password" id="password"/></td>
            </tr>
            <tr>
                <td></td>
                <td><span style="color: red">${errorMsg}</span></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" id="login_button" value="用户登录"/></td>
            </tr>
        </table>
    </form>
    </body>
    </html>
    
    • 登录成功页面:success.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>成功</title>
    </head>
    <body>
        登录用户:${username}<br>
        登录密码:${password}
    </body>
    </html>
    
    作者: 唐浩荣
    本文版权归作者和博客园共有,欢迎转载,但是转载需在博客的合适位置给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    初学java-基础
    初学java-基础
    HYPER-V 实现管理器远程管理虚拟机
    zeromq rpc原型
    haproxy+keepalived
    [转]序列化悍将Protobuf-Net,入门动手实录
    RabbitMQ的几种应用场景
    redis数据类型及使用场景
    [转]DDD领域驱动设计基本理论知识总结
    Centos 下编译安装Redis
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/14534570.html
Copyright © 2011-2022 走看看