springMVC拦截器Interceptor

Interceptor拦截器的基本使用方法

springMVC提供了Interceptor拦截器机制,类似于Servlet中的Filter过滤器,用于拦截用户请求并作出相应的处理。
通过拦截器机制可以进行用户权限鉴定,或者用来判断用户是否已经登录。
springMVC拦截器是可插拔式的设计,需要拦截器的某一个功能时,只需要在配置文件中应用该拦截器即可;如果不需要这个拦截器功能,只需要在配置文件中取消这个功能即可。
在SpringMVC中定义拦截器有两种办法:

  1. 实现HandlerInterceptor接口,或者继承实现HandlerInterceptor接口的实现类(例如HandlerInterceptorAdapter);
  2. 实现WebRequestInterceptor接口 ,或者继承WebRequestInterceptor接口的实现类。

1、实现HandlerInterceptor接口

handlerInterceptor接口位于org.springframework.web.servlet包下:

public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;

    void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}

这三个方法的具体作用是:

  • preHandle方法:该方法在执行Controller控制器方法之前执行。返回值为布尔值类型,如果返回为false,表示拦截该请求,不向下执行;如果返回值为true,则不拦截该请求,放行该请求,程序继续向下执行(如果后面没有其他的拦截器,那么就执行Controller控制器中的方法)。因为该方法可以对请求进行判断,决定是否继续执行,所以该方法常用来进行一些初始化操作或者对请求进行预处理;
  • postHandle方法:该方法将在执行Controller控制器方法之后调用,但是在返回ModelAndView之前调用。在DispatcherServlet进行渲染之前调用,所以此方法常用于处理返回的视图,在此方法中对请求域中的视图和模型做进一步处理和修改;
  • afterCompletion方法:该方法在Controller控制器方法执行完成后执行,由于该方法实在执行完成Controller方法之后执行,所以常用于一些资源清理、记录执行的日志等操作。

注意:由于preHandle方法决定了程序是否继续执行,因此postHandle方法和afterCompletion方法只有在preHandle方法返回值为true时才执行。
实现了handlerInterceptor接口后,只有在SpringMVC的配置文件中添加mvc:interceptors标签对才能起到全局拦截器的作用:

    <!--    配置拦截器-->
    <mvc:interceptors>
        <!--        在mvc:interceptor标签中直接使用Bean标签会拦截所有请求-->
        <bean class="com.springmvc.interceptor.MyInterceptor"/>
        
        <!--定义多个拦截器,将会按顺序执行-->
        <mvc:interceptor>
            <!-- mvc:mapping path配置为/**,表示拦截所有请求-->
            <mvc:mapping path="/**"/>
            <!--  mvc:exclude-mapping path配置为/hello表示不会拦截/hello请求     -->
            <mvc:exclude-mapping path="/hello"/>
            <bean class="com.springmvc.interceptor.MyInterceptor1"/>
        </mvc:interceptor>
        
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/hello"/>
            <bean class="com.springmvc.interceptor.MyInterceptor2"/>
        </mvc:interceptor>
        ...<!-- 可以接着配置更多的拦截器-->
    </mvc:interceptors>

2、实现WebRequestInterceptor接口

WebRequestInterceptor接口中定义了三个方法,这三个方法都传递了同一个参数WebRequest.
WebRequest是spring定义的一个接口,在WebRequestInterceptor中对WebRequest的所有操作都将同步到HttpServletRequest中,然后在当前请求中一直传递。

public interface WebRequestInterceptor {
    void preHandle(WebRequest var1) throws Exception;
    void postHandle(WebRequest var1, ModelMap var2) throws Exception;
    void afterCompletion(WebRequest var1, Exception var2) throws Exception;
}

WebRequestInterceptor接口的三个方法介绍如下:

  • preHandle(WebRequest var1)方法:该方法将在Controller方法调用之前调用,这个方法与HandlerInterceptor接口的preHandle方法不同,WebRequestInterceptor的preHandle方法没有返回值,一般用来进行资源的准备工作,比如某些情况下需要提前准备一个session对象,我们可以利用WebRequest的setAttribute(name,value,scope)方法把session放到WebRequest中,setAttribute方法的第三个参数scope是Integer类型的,在WebRequest的父接口RequestAttributes中对它定义了三个常量:
    1. SCOPE_REQUEST:它的值是0,表示只有在request中可以访问;
    2. SCOPE_SESSION:它的值是1,如果环境允许的话,它代表的是一个局部隔离的session,否则就代表普通的session,并且可以在该session中可以访问;
    3. SCOPE_GLOBAL_SESSION:它的值是2,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session范围内可以访问。
  • postHandle(WebRequest var1, ModelMap var2)方法:该方法将在Controller方法被调用之后调用,但是在视图渲染之前调用。可以在这个方法中改变ModelMap数据模型来改变数据的展示。而WebRequest对象用于传递整个请求数据,在preHandle方法中准备的数据都可以在WebRequest中访问;
  • afterCompletion(WebRequest var1, Exception var2)方法:该方法会在整个请求处理完成,也就是在试图返回并被渲染之后执行,因此该方法常用来进行资源的释放,而WebRequest可以把我们在preHandle方法中准备的资源传递到这里进行释放。Exception表示当前请求的异常对象,如果在Controller中捕获的异常已经被spring的异常处理器处理完成,那么这个异常对象为null。

Interceptor拦截器的执行流程

一、单个拦截器的执行流程

在程序运行时,拦截器的执行是有一定顺序的,该顺序与配置文件中的定义拦截器时的配置顺序有关。
如果配置文件中只定义了一个拦截器,那么该拦截器的执行流程如下:
image.png
程序首先执行拦截器类中的preHandle方法,如果该方法的返回值是true,则程序会继续向下执行处理器中的方法,否则不会再向下执行,在业务控制器Controller处理完请求后,会继续执行postHandle方法,而后会通过DispatcherServlet向客户端返回响应,在DispatcherServlet处理完请求后,才会继续执行afterCopmletion方法。
代码演示:
创建基于ssm的maven项目,名称叫做springmvc-interceptor的项目。配置好web.xml和applicationContext.xml文件。在包com.springmvc.controller中添加如下代码:

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        System.out.println("HelloController的hello方法执行。");
        return "hello";
    }
}

hello.jsp页面为一个简单字符串展示:HelloWorld!.
在com.springmvc.interceptor包中创建MyInterceptor拦截器并实现HandleInterceptor接口,重写相关方法,代码如下:

public class MyInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("MyInterceptor拦截器的preHandle方法执行。");
        return true;
    }
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor拦截器的postHandle方法执行。");
    }
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("MyInterceptor拦截器的afterCompletion方法执行。");
    }
}

在springmvc.xml文件中配置拦截器:

  <mvc:interceptors>
        <mvc:interceptor>
            <!-- mvc:mapping path配置为/**,表示拦截所有请求-->
            <mvc:mapping path="/**"/>
            <!--  mvc:exclude-mapping path配置为/hello22表示不会拦截/hello22请求     -->
            <mvc:exclude-mapping path="/hello22"/>
            <bean class="com.springmvc.interceptor.MyInterceptor"/>
        </mvc:interceptor>
  </mvc:interceptors>

将完全配置好的项目发布到tomcat中运行,访问http://localhost:8080/springmvc-interceptor/hello,页面进入hello.jsp页面。控制台输出如下:
image.png
从结果中可以看出,先执行拦截器的preHandle方法,然后执行了Controller的hello方法,再执行拦截器的postHandle方法,最后执行拦截器的afterCompletion方法。

二、多个拦截器的执行流程

当配置了多个拦截器时,会按照拦截器的配置顺序依次调用执行。但值得注意的是,拦截器的内部执行规律并步遵循普通的Java类一样,拦截器是基于“责任链”模式。
假如有两个拦截器,分别是MyInterceptor1和MyInterceptor2,将MyInterceptor1设置在前面,那么这两个拦截器的执行顺序如下图所示:
在这里插入图片描述

代码演示:
将上面演示用过的MyInterceptor拦截器复制两份,分别命名为MyInterceptor1和MyInterceptor2:
image.png
在springmvc.xml配置文件中定义这两个拦截器:

    <!--    配置拦截器-->
  <mvc:interceptors>
        <!--定义多个拦截器,将会按顺序执行-->
        <mvc:interceptor>
            <!-- mvc:mapping path配置为/**,表示拦截所有请求-->
            <mvc:mapping path="/**"/>
            <!--  mvc:exclude-mapping path配置为/h表示不会拦截/h请求 -->
            <mvc:exclude-mapping path="/h"/>
            <bean class="com.springmvc.interceptor.MyInterceptor1"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/h"/>
            <bean class="com.springmvc.interceptor.MyInterceptor2"/>
        </mvc:interceptor>
</mvc:interceptors>

然后重新发布在tomcat中运行,访问http://localhost:8080/springmvc-interceptor/hello。浏览器跳转到hello.jsp页面,控制台输出:
image.png
由此可知,当多个拦截器同时工作时,它们的preHandle方法会按照配置文件中拦截器的配置顺序依次执行,如果返回值都为true,会戒指执行Controller控制器中的hello方法,而它们的postHandle方法和afterCompletion方法则会按照配置文件中拦截器的配置顺序反向执行。

使用Interceptor拦截器检验用户是否已经登录

通过以上的学习,我们可以利用拦截器的基本特性实现检验用户是否已经登录。如果当前没有登录,那么跳转到login.jsp登录页面,如果用户已经登录,则放行;如果账号或密码错误,则提示账号或密码错误并返回登录页面;已经登录用户退出时,页面自动返回登录页面。具体业务流程图如下:
image.png
在com.springmvc.controller包中创建UserController类:

@Controller
public class UserController {
    @RequestMapping(value = "/login", method = RequestMethod.GET)    //向用户登录页面的跳转方法
    public String loginPage() {
        System.out.println("用户从login的请求到登录跳转login.jsp页面");
        return "login";
    }
    @RequestMapping(value = "/login", method = RequestMethod.POST)    //用户实现登录的方法
    public String login(User user, Model model, HttpSession session) {
        String loginName = user.getLoginName();
        String password = user.getPassword();
        if (loginName != null && loginName.equals("zhangsan") && password != null && password.equals("123456")) {
            System.out.println("用户登录功能实现");
            //将用户添加至session中保存
            session.setAttribute("CURRENT_USER", user);
            return "redirect:index";    //重新定向到主页的index跳转方法
        }
        model.addAttribute("message", "账号或密码错误,请重新登录!");
        return "login";    //跳转到登录页面
    }
    @RequestMapping(value = "/index")    //向主页跳转的方法
    public String indexPage() {
        System.out.println("用户从index请求到主页跳转index.jsp页面");
        return "index";    //跳转到主页面
    }
    @RequestMapping(value = "/logout") //用户退出登录的方法
    public String logout(HttpSession session) {
        session.invalidate();    //清除session
        System.out.println("退出功能实现,清除session,重定向到login的请求");
        return "redirect:login";    //重定向到登录页面的跳转方法
    }
}

login.jsp的表单会提交loginName和password两个字符串,spring会自动把这两个字符串封装到User对象中。
在com.springmvc.interceptor包中创建LoginInterceptor的拦截器类:

public class LoginInterceptor implements HandlerInterceptor {
	 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {   
            String url=request.getRequestURI();//获取请求的URl
            if (!(url.contains("Login")||url.contains("login"))) {
                //非登录请求,获取session,判断是否有用户数据
                if (request.getSession().getAttribute("CURRENT_USER")!=null) {
                    return true;	//说明已经登录,放行
                }else {	//没有登录则跳转到登录页面,
                    request.setAttribute("message", "您还没有登录,请先登录!");
                    request.getRequestDispatcher("/login").forward(request, response);
                }
            }else {
                return true;	//登录请求,放行
            }
            return false;		//默认拦截
        }
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        }
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
        }
    }

在preHandle方法中编写了控制用户登录的逻辑。首先判断是否去往登录页面,如果是则直接返回true放行,如果不是,则检测session中是否有user用户对象,如果没有则重定向到login界面。
在springmvc.xml配置文件中配置LoginInterceptor拦截器:

 <mvc:interceptors>
        <!-- 登录拦截器 -->
      <mvc:interceptor>
             <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/login"/>
            <bean class="com.springmvc.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
    </mvc:interceptors>

创建index.jsp页面:
image.png
创建login.jsp页面:
image.png
将项目发布到tomcat中运行。访问http://localhost:8080/springmvc-interceptor/index
从图中可以看出,用户没有登录就会被拦截器拦截到登录界面:
在这里插入图片描述

输入错误的账户密码会登录失败:
image.png
只有输入账户为zhangsan 密码为123456才会登录成功并跳转到index界面:
image.png如果此时点击退出,就会清除session,然后跳转到登录页面。

如果我的文章对您有帮助,还请您多多支持我。支付宝帮忙扫一下吧
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pengkai火火火

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值