Spring MVC拦截器(Interceptor )

版权声明:本文为博主原创,未经同意不得转载 https://blog.csdn.net/weixin_42950079/article/details/88915516

RGB颜色对照表

Spring MVC拦截器简介

SpringMVC 中的 Interceptor 拦截器的主要作用就是拦截用户的 url 请求,并在执行 handler方法(控制器方法)的前中后加入某些特殊请求,例如通过拦截器进行权限验证、记录请求信息的日志、判断用户是否登录等。类似于 Servlet 开发中的过滤器Filter。

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

Spring MVC 拦截器的实现一般有两种方法:

1、继承 HandlerInterceptorAdapter 抽象类   2、实现 HandlerInterceptor 接口


HandlerInterceptor接口定义了三个方法

package org.springframework.web.servlet;

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

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;
}

CD4356
preHandle():该方法在请求到达Handler(Controller)之前,先执行的前置处理方法。当其返回值为true时,表示继续向下执行;当其返回值为false时,请求直接返回,并中断所有后续操作,不会再调用其他拦截器方法,也不会调用handler方法(控制器方法)。该方法主要用于拦截请求。
CD4356
postHandle():该方法在控制器方法调用之后,视图解析之前执行。可以通过该方法对请求域之中的 model 和 view 作出进一步的修改。
CD4356
afterCompletion():该方法在整个请求完成,即DispatcherServlet 渲染了对应的视图之后执行。该方法一般用于实现一些资源清理、记录日志信息等工作。


Spring MVC拦截器的执行流程

CD4356
前端控制器DispatcherServlet在接收客户端发送的request请求后,交给对应的Controller进行处理,在这个过程中,如果配置了拦截器,就会先调用preHandler()方法进行拦截。


多个拦截器的执行顺序

CD4356
Spring MVC中的Interceptor是呈链式调用的,设置多个拦截器时,先按顺序调用preHandler()方法,然后按逆序调用每个拦截器的postHandler()和afterCompletion方法,其中preHandler()方法的执行顺序(拦截器的执行顺序)是由dispatcher-servlet.xml中<mvc:interceptor>节点的配置顺序决定的。


具体内容

在dispatcher-servlet.xml中配置拦截器
<mvc:interceptors>:是拦截器的外部标签,用于配置一组拦截器
<mvc:interceptor>:用来指定一个具体的拦截器
<mvc:mapping path="/**"/>:对任何URL路径进行拦截, /**代表对所有的URL
<mvc:mapping path="/XX/xx"/>:拦截所有以/say结尾的URL请求,XX为类上的映射路径,xx为方法上的映射路径,如果类上和方法上都有映射路径,那么path属性中两个路径必须都包含。
<mvc:exclude-mapping path="/XX/xx"/>:不拦截以/XX/xx结尾的URL请求,XX为类上的映射路径,xx为方法上的映射路径,如果类上和方法上都有映射路径,那么path属性中两个路径必须都包含。

<!--配置拦截器-->
    <mvc:interceptors>
        <!--拦截器1-->
        <mvc:interceptor>
            <!-- 对任何url路径进行拦截,  /**代表对所有的路径 -->
            <mvc:mapping path="/**"/>
            <!--拦截所有以/CD4356/say结尾的路径请求-->
            <mvc:mapping path="/CD4356/say"/>
            <!--不拦截以/CD4356/say结尾的路径请求-->
            <mvc:exclude-mapping path="/CD4356/say"/>
            <!-- 定义在<mvc:interceptor/>内指定其为拦截器 -->
            <bean class="com.CD4356.controller.Interceptor1"/>
        </mvc:interceptor>

        <!-- 当设置多个拦截器时,先按顺序调用preHandle方法,
        然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->

        <!--拦截器2-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.CD4356.controller.Interceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>

配置自定义拦截器类的<bean>,当然也可在拦截器类上添加@Service注解,效果都是一样的

<bean id="interceptor1" class="com.CD4356.controller.Interceptor1"/>
<bean id="interceptor2" class="com.CD4356.controller.Interceptor2"/>

dispatcher-servlet.xml全部内容

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--component-scan告诉Servlet去哪里找Controller, base-package指定src目录下存放Controller的包-->
    <!-- spring可以自动去扫描base-pack下面的包或者子包下面的java文件,
        如果扫描到有Spring的相关注解的类,则把这些类注册为Spring的bean -->
    <context:component-scan base-package="com.CD4356.controller"/>


    <!--配置视图解析器-->
    <!--InternalResourceViewResolver类位于spring-webmvc-4.3.18.RELEASE.jar包中-->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 视图的路径 -->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <!-- 视图名称后缀  -->
        <property name="suffix" value=".jsp"/>
    </bean>

	<bean id="interceptor1" class="com.CD4356.controller.Interceptor1"/>
    <bean id="interceptor2" class="com.CD4356.controller.Interceptor2"/>

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--拦截器1-->
        <mvc:interceptor>
            <!-- 对任何url路径进行拦截,  /**表示拦截所有路径 -->
            <mvc:mapping path="/**"/>
            <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
            <bean class="com.CD4356.controller.Interceptor1"/>
        </mvc:interceptor>

        <!-- 当设置多个拦截器时,先按顺序调用preHandle方法,
        然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->

        <!--拦截器2-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.CD4356.controller.Interceptor2"/>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

web.xml内容

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--加载spring配置文件applicationContext.xml-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <!--ContextLoaderListener类位于spring-web-4.3.18.RELEASE.jar包-->
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--表示允许访问静态的*.jpg文件(png、git等格式的图片需另行配置)
        为什么要加这一段呢? 因为配置springmvc的servlet的时候,使用的路径是"/",
        导致静态资源在默认情况下不能访问,所以要加上这一段,允许访问jpg,并且必须加在springmvc的servlet之前
        如果你配置spring-mvc使用的路径是/*.do,就不会有这个问题了
     -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.jpg</url-pattern>
    </servlet-mapping>

    <!--配置Spring MVC的入口 DispatcherServlet, 把所有的请求都提交到该Servlet-->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <!--DispatcherServlet类位于spring-webmvc-4.3.18.RELEASE.jar包-->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 配置springmvc的前端控制器,可以配置多个前端控制器来拦截不同的url -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <!--将*.form修改成/,就可以拦截所有的url请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

新建一个测试类HiController

package com.CD4356.controller;

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

@Controller("hiController")  /*@Controller表明这是一个controller类型的bean*/
@RequestMapping("/CD4356")
public class HiController {

	@RequestMapping(value = "/say")
    public String hello(Model model){ //参数中传入Model
        model.addAttribute("name","CD4356");
        model.addAttribute("url","https://blog.csdn.net/weixin_42950079");
        System.out.println("handler方法...");
        return "say"; //返回页面模板的名字,到指定的目录下寻找该文件名的文件
    }

}

在/WEB-INF/pages/目录下创建say.jsp文件,内容如下

<%--
  Created by IntelliJ IDEA.
  User: CD4356
  Date: 2019/3/5
  Time: 14:34
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>springmvc项目测试</title>
</head>

<div style="background-color: cadetblue;width: 100px;height: 40px">author: ${name}</div><br>
<font face="华文中宋" size="5">CSDN: ${url}</font>

</body>
</html>

新建一个拦截器Interceptor1,实现HandlerInterceptor接口,并实现接口的方法

package com.CD4356.controller;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class Interceptor1 implements HandlerInterceptor {

    /**
     * 在业务处理器处理请求之前被调用
     * 如果返回false
     *     从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
     * 如果返回true
     *    执行下一个拦截器,直到所有的拦截器都执行完毕
     *    再执行被拦截的Controller
     *    然后进入拦截器链,
     *    从最后一个拦截器往回执行所有的postHandle()
     *    接着再从最后一个拦截器往回执行所有的afterCompletion()
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest,
                             HttpServletResponse httpServletResponse,
                             Object o) throws Exception {
        System.out.println("preHandle1(), 在访问Controller之前被调用");
        return true;
    }

    /**
     * 在业务处理器处理请求执行完成后,生成视图之前执行的动作
     * 可在modelAndView中加入数据,比如当前时间
     */
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle1(), 在访问Controller之后,访问视图之前被调用,这里可以注入一个时间到modelAndView中,用于后续视图显示");
        modelAndView.addObject("date","由拦截器生成的时间:" + new Date());
    }

    /**
     * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
     *
     * 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler, Exception e) throws Exception {
        System.out.println("afterCompletion1(), 在访问视图之后被调用");
    } // 拦截器类HandlerInterceptorAdapter
    
}

新建第二个拦截器Interceptor2,实现HandlerInterceptor接口,并实现接口的方法

package com.CD4356.controller;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class Interceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("preHandle2(), 在访问Controller之前被调用");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle2(), 在访问Controller之后,访问视图之前被调用,这里可以注入一个时间到modelAndView中,用于后续视图显示");
        modelAndView.addObject("date","由拦截器生成的时间:" + new Date());
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion2(), 在访问视图之后被调用");
    }
}

在浏览器中输入 http://localhost:8080/CD4356/say 后打开页面后,IDEA控制台的输出内容如下
CD4356
该输出结果证明了前面所说的多个拦截器的执行顺序


修改Interceptor1的preHandle()方法,让其返回值为false,然后在浏览器中输入 http://localhost:8080/CD4356/say 后打开页面后,IDEA控制台的输出内容如下
CD4356
验证了preHandle()返回值为false后,就会中断后续所有操作,包括handler方法和postHandler()、afterCompletion()拦截器方法的调用


修改Interceptor2的preHandle()方法,让其返回值为false,然后在浏览器中输入 http://localhost:8080/CD4356/say 后打开页面后,IDEA控制台的输出内容如下
CD4356

猜你喜欢

转载自blog.csdn.net/weixin_42950079/article/details/88915516