目录
1.1.2 处理器:StudentController.doForward()
1.1.4 请求转发到的视图解析器范围之外的新视图:hello.jsp
1.1.5 请求转发到的视图解析器范围之内的新视图:show.jsp
1.1.6 SpringMVC的配置文件中配置了视图解析器:springmvc.xml
1.2.1 处理器:StudentController.doRedirect()
1.2.3 请求重定向到的新视图:helloRedirect.jsp
2.1 定义一个后端业务处理器: StudentController.java(可能抛出Name/AgeException等)
2.2 新建一个自定义的全局异常类:MyUserException.java
2.3 定义全局异常类的子类用来处理姓名异常: NameException.java
2.4 定义全局异常类的子类用来处理年龄异常: AgeException.java
2.5 定义一个普通类用作全局异常处理类(处理姓名/年龄异常): GlobalExceptionHandler.java
2.6 定义一个姓名异常的视图: nameError.jsp
2.8 定义一个其他的未知异常的视图: defaultError.jsp
2.10 定义一个StudentController处理器处理业务成功后跳转到的数据展示视图: show.jsp
2.11 在spriingmvc.xml文件中声名GlobalExceptionHandler处理器的扫描
3.1.7 编写拦截器MyInterceptor(需要 implement HandlerInterceptor)
3.1.9 编写处理器StudentController处理完成后转发到的页面:show.jsp
3.1.10 编写拦截器返回false时拦截器内部转发到的友好页面:tips.jsp
3.1.12 测试postHandler()方法:后处理方法
(2)在postHandler()方法中对处理器返回的结果做二次修正
(3)将修正后的结果在转发的新的视图中展示出来:postHandlerJsp.jsp
3.1.13 测试afterCompletion()方法:最后执行的方法
3.1.14 经过preHandler/postHandler/afterCompletion之后的项目结构图
3.2.3 在springmvc.xml文件中配置多个拦截器
3.2.4 当项目中存在多个拦截器时, 拦截器的执行流程图和执行顺序
SpringMVC的核心技术
1、请求转发和请求重定向
当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式: 请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。
注意:对于请求转发的页面,可以是 WEB-INF 中页面。而重定向的页面, 是不能为 WEB-INF 中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。
SpringMVC框架把原来 Servlet 中的请求转发和重定向操作进行了封装。 现在可以使用简单的方式实现转发和重定向。
forward:表示转发,实现了:request.getRequestDispatcher("xx.jsp").for ward( )
redirect:表示重定向,实现了:response.sendRedirect("xxx.jsp")
1.1 请求转发的实例
处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward: ,而且此时的视图不再与视图解析器一同工作,这样可以在即使配置了视图解析器时也可以指定到不同位置的视图。视图页面必须写出相对于项目根的路径。注:forward 操作不需要视图解析器。处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径。
1.1.1 项目结构:从ssm-web项目中修改而来
ssm-web完整项目见:https://blog.youkuaiyun.com/cmm0401/article/details/112065678
1.1.2 处理器:StudentController.doForward()
package com.wind.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class StudentController {
/**
* 处理器方法返回ModelAndView,实现转发【forward】。
* 语法:modelAndView.setViewName("forward:视图文件的完整路径名");
* forward的特点:forward不和项目中的视图解析器不同工作的,就当做项目中没有视图解析器,
* 也即可以转发到视图解析器之外的视图中去。
*/
@RequestMapping(value = "doForward.do", method = RequestMethod.POST)
public ModelAndView doForward() {
//创建一个视图
ModelAndView modelAndView = new ModelAndView();
//添加数据
modelAndView.addObject("msg", "实现请求的转发功能");
modelAndView.addObject("fun", "doForward方法执行了");
//添加视图
/**
* 方式1:隐式转发
* modelAndView.setViewName("show");
*/
/**
* 方式2:显示转发
* modelAndView.setViewName("forward:/WEB-INF/jsp/show.jsp");
*/
/**
* 方式2:显示转发,这个是转发到非jsp目录下的页面中去,第1种方式就不支持了
*/
modelAndView.setViewName("forward:/hello.jsp");
return modelAndView;
}
}
1.1.3 没转发之前的主视图:index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<%
String basePath = request.getScheme()
+ "://" + request.getServerName()
+ ":" + request.getServerPort()
+ request.getContextPath() + "/";
%>
<head>
<title>Title</title>
<base href="<%=basePath%>"/>
</head>
<body>
<p>当你的方法返回ModelAndView的时候实现转发forward</p>
<form action="doForward.do" method="post">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交请求">
</form>
</body>
</html>
1.1.4 请求转发到的视图解析器范围之外的新视图:hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/hello.jsp 从request作用域中取出数据</h3>
<h3>msg数据是:${msg}</h3>
<h3>fun数据是:${fun}</h3>
</body>
</html>
1.1.5 请求转发到的视图解析器范围之内的新视图:show.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/view/show.jsp 从request作用域中取出数据</h3>
<h3>msg数据是:${msg}</h3>
<h3>fun数据是:${fun}</h3>
</body>
</html>
1.1.6 SpringMVC的配置文件中配置了视图解析器:springmvc.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 https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--SpringMVC的配置文件,用来声名Controller和其他web相关的对象-->
<!--配置组件扫描器-->
<context:component-scan base-package="com.wind.controller"/>
<!--视图解析器:添加前缀和后缀。
SpringMVC框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver
中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,
视图解析器会自动完成拼接。-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--视图文件的路径-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--视图文件的扩展名-->
<property name="suffix" value=".jsp"/>
</bean>
<!--注册注解驱动。
(1)响应ajax请求,返回json字符串。
(2)解决静态资源访问问题。-->
<mvc:annotation-driven/>
<!--加载静态资源图片啊,jQuery文件啊等等-->
<mvc:resources location="js/" mapping="/js/**"/>
<mvc:resources location="images/" mapping="/images/**"/>
</beans>
1.1.7 转发测试
1.2 请求重定向的实例
1.2.1 处理器:StudentController.doRedirect()
package com.wind.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class StudentController {
/**
* 处理器方法返回ModelAndView,实现转发【forward】。
* 语法:modelAndView.setViewName("forward:视图文件的完整路径名");
* forward的特点:forward不和项目中的视图解析器不同工作的,就当做项目中没有视图解析器,
* 也即可以转发到视图解析器之外的视图中去。
*/
@RequestMapping(value = "doForward.do", method = RequestMethod.POST)
public ModelAndView doForward() {
//创建一个视图
ModelAndView modelAndView = new ModelAndView();
//添加数据
modelAndView.addObject("msg", "实现请求的转发功能");
modelAndView.addObject("fun", "doForward方法执行了");
//添加视图
/**
* 方式1:隐式转发
* modelAndView.setViewName("show");
*/
/**
* 方式2:显示转发
* modelAndView.setViewName("forward:/WEB-INF/jsp/show.jsp");
*/
/**
* 方式2:显示转发,这个是转发到非jsp目录下的页面中去,第1种方式就不支持了
*/
modelAndView.setViewName("forward:/hello.jsp");
return modelAndView;
}
/**
* 处理器方法返回ModelAndView,实现重定向【redirect】。
* 语法:modelAndView.setViewName("redirect:视图文件的完整路径名");
* redirect的特点:redirect不和项目中的视图解析器不同工作的,就当做项目中没有视图解析器,
* <p>
* 框架对重定向做的操作:
* 框架会把Model中的简单的数据类型转化为String类型,并把它们作为helloRedirect.jsp的GET请求参数传递过去,
* 目的是在doDirect.do和helloRedirect.jsp之间传递数据。
* <p>
* 所以需要在试图中使用:${param.myage} 或者 ${param} 即可,同时,重定向不同访问/WEB-INF项目下资源。
*/
@RequestMapping(value = "doDirect.do", method = RequestMethod.GET)
public ModelAndView doRedirect(String name, Integer age) {
//创建一个视图
ModelAndView modelAndView = new ModelAndView();
//添加数据,这两个数据是存储在request作用域中的
modelAndView.addObject("myname", name);
modelAndView.addObject("myage", age);
//添加视图
modelAndView.setViewName("redirect:/helloRedirect.jsp");
//测试结果:http://localhost:8080/ssm-web/helloRedirect.jsp?myname=fb&myage=22
return modelAndView;
}
}
1.2.2 没重定向之前的主视图:index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<%
String basePath = request.getScheme()
+ "://" + request.getServerName()
+ ":" + request.getServerPort()
+ request.getContextPath() + "/";
%>
<head>
<title>Title</title>
<base href="<%=basePath%>"/>
</head>
<body>
<p>当你的方法返回ModelAndView的时候实现转发forward</p>
<form action="doForward.do" method="post">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交请求">
</form>
<br/>
<p>当你的方法返回ModelAndView的时候实现重定向direct</p>
<form action="doDirect.do" method="get">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交请求">
</form>
</body>
</html>
1.2.3 请求重定向到的新视图:helloRedirect.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/helloRedirect.jsp 从request作用域中取出数据</h3>
<h3>myname数据是:${param.myname}</h3>
<h3>myage数据是:${param.myage}</h3>
<h3>parm数据是:${param}</h3>
<h3>parm数据是:<% request.getParameter("myname");%></h3>
</body>
</html>
1.2.4 重定向的测试
2、异常处理
SpringMVC 框架处理异常的常用方式:使用@ExceptionHandler 注解处理异常。
2.1 定义一个后端业务处理器: StudentController.java(可能抛出Name/AgeException等)
package com.wind.controller;
import com.wind.exception.AgeException;
import com.wind.exception.MyUserException;
import com.wind.exception.NameException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class StudentController {
@RequestMapping(value = "doException.do", method = RequestMethod.POST)
public ModelAndView doException(String name, Integer age) throws MyUserException {
//创建一个视图
ModelAndView modelAndView = new ModelAndView();
//参数校验时可能会抛出异常
if (name == null || !name.equals("yangguo")) {
throw new NameException("姓名输入错误了。。。");
}
if (age == null || age > 100) {
throw new AgeException("年龄输入错误了。。。");
}
//添加数据,这两个数据是存储在request作用域中的
modelAndView.addObject("myname", name);
modelAndView.addObject("myage", age);
//添加视图
modelAndView.setViewName("show");
return modelAndView;
}
}
2.2 新建一个自定义的全局异常类:MyUserException.java
package com.wind.exception;
public class MyUserException extends Exception {
public MyUserException() {
super();
}
public MyUserException(String message) {
super(message);
}
}
2.3 定义全局异常类的子类用来处理姓名异常: NameException.java
package com.wind.exception;
public class NameException extends MyUserException {
public NameException() {
super();
}
public NameException(String message) {
super(message);
}
}
2.4 定义全局异常类的子类用来处理年龄异常: AgeException.java
package com.wind.exception;
public class AgeException extends MyUserException {
public AgeException() {
super();
}
public AgeException(String message) {
super(message);
}
}
2.5 定义一个普通类用作全局异常处理类(处理姓名/年龄异常): GlobalExceptionHandler.java
package com.wind.handler;
import com.wind.exception.AgeException;
import com.wind.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**
* @ControllerAdvice :控制器增强,也就是说给控制器类增加额外的功能(异常处理功能)
* (1)这个注解放在类的上面使用。
* (2)必须让SpringMVC框架知道这个注解所在的包名,需要在SpringMVC配置文件中声名组件扫描器,指定@ControllerAdvice所在的包名
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//定义方法来处理在Controller业务中抛出的各种异常
/**
* (1)处理异常的方法和控制器方法的定义一样,可以有多个参数,有返回值(ModelAndView,String,void等)。
* (2)形参Exception:表示在Controller中抛出的异常对象,通过此形参即可获取异常对象。
* (3)@ExceptionHandler(异常类.class):在方法上加注解,标识异常的类型,代表这个异常类型的异常被抛出时由此方法处理。
*/
//这是处理NameException异常类型的异常
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception exception) {
/**
* 异常发生处理器:
* (1)需要把异常记录下来,落到DB中、日志文件中等,记录异常发生的时间、哪个方法抛出的、异常错误的内容是啥。
* (2)发送异常通知给相关人员。
* (3)给用户一个友好的提示。
*/
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "姓名必须是yangguo才能访问,其他用户不能访问此页面");
modelAndView.addObject("ex", exception);
modelAndView.setViewName("nameError");
return modelAndView;
}
//这是处理AgeException异常类型的异常
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你的年龄不能大于100岁");
modelAndView.addObject("ex", exception);
modelAndView.setViewName("ageError");
return modelAndView;
}
//这是处理NameException、AgeException异常类型之外的其他的未知的异常
@ExceptionHandler
public ModelAndView doDefaultException(Exception exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "其他的未知的异常发生了");
modelAndView.addObject("ex", exception);
modelAndView.setViewName("defaultError");
return modelAndView;
}
}
2.6 定义一个姓名异常的视图: nameError.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/jsp/nameError.jsp</h3>
提示信息:${msg}<br/>
系统消息:${ex.message}<br/>
</body>
</html>
2.7 定义一个年龄异常的视图: ageError.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/jsp/ageError.jsp</h3>
提示信息:${msg}<br/>
系统消息:${ex.message}<br/>
</body>
</html>
2.8 定义一个其他的未知异常的视图: defaultError.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/jsp/defaultError.jsp</h3>
提示信息:${msg}<br/>
系统消息:${ex.message}<br/>
</body>
</html>
2.9 定义一个前端页面的主视图: index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<%
String basePath = request.getScheme()
+ "://" + request.getServerName()
+ ":" + request.getServerPort()
+ request.getContextPath() + "/";
%>
<head>
<title>Title</title>
<base href="<%=basePath%>"/>
</head>
<body>
<p>处理异常的:全局异常处理</p>
<form action="doException.do" method="post">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交请求">
</form>
<br/>
</body>
</html>
2.10 定义一个StudentController处理器处理业务成功后跳转到的数据展示视图: show.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/jsp/show.jsp 从request作用域中取出数据</h3>
<h3>myname数据是:${myname}</h3>
<h3>myage数据是:${myage}</h3>
</body>
</html>
2.11 在spriingmvc.xml文件中声名GlobalExceptionHandler处理器的扫描
<?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 https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--SpringMVC的配置文件,用来声名Controller和其他web相关的对象-->
<!--配置组件扫描器-->
<context:component-scan base-package="com.wind.controller"/>
<!--视图解析器:添加前缀和后缀。
SpringMVC框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver
中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,
视图解析器会自动完成拼接。-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--视图文件的路径-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--视图文件的扩展名-->
<property name="suffix" value=".jsp"/>
</bean>
<!--注册注解驱动。
(1)响应ajax请求,返回json字符串。
(2)解决静态资源访问问题。-->
<mvc:annotation-driven/>
<!--注册组件扫描器-->
<context:component-scan base-package="com.wind.handler"/>
<!--加载静态资源图片啊,jQuery文件啊等等-->
<mvc:resources location="js/" mapping="/js/**"/>
</beans>
2.12 业务成功的测试
2.13 姓名异常测试
2.14 年龄异常测试
2.15 其他未知的异常测试
2.16 项目结构图
3、拦截器
SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。
其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。
当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。
3.1 定义一个拦截器:步骤如下
3.1.1 引入依赖 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wind</groupId>
<artifactId>ssm-web4</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>ssm-web Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.3.2</spring.version>
<mybatis.version>3.4.6</mybatis.version>
<mybatis.spring.version>2.0.3</mybatis.spring.version>
<mysql.version>8.0.22</mysql.version>
<druid.version>1.2.4</druid.version>
</properties>
<dependencies>
<!--Spring事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!--Spring原生JDBC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--SpringMVC使用的Servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--SpringMVC使用的jsp依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--MyBatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!--MyBatis与SpringMVC整合时需要的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mysql数据库连接池依赖:使用的是德鲁伊数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--springMVC序列化用的jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
<!--单元测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>ssm-web</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
<!--
maven默认扫描src/main/java中的文件而不理会src/main/resources中的xml文件,
所以后来添加了resource节点,这样就将src/main/resources中的xml文件改变maven默认的扫描策略,
防止造成src/main/resources下的配置文件打包丢失。
编译之后的文件中少了mapper.xml,这个和maven有关,maven编译src/java代码的时候,
默认只会对java文件进行编译然后放在target/classes目录,需要在pom.xml中加入下面配置-->
<!--如果不添加此节点,mapper.xml文件、config.properties文件、config.spring文件等
都不会被加载到target的classes中去,也就不能被使用,也就会报错。-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
3.1.2 编写springmvc.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 https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--SpringMVC的配置文件,用来声名Controller和其他web相关的对象-->
<!--配置组件扫描器-->
<context:component-scan base-package="com.wind.controller"/>
<!--视图解析器:添加前缀和后缀。
SpringMVC框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver
中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,
视图解析器会自动完成拼接。-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--视图文件的路径-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--视图文件的扩展名-->
<property name="suffix" value=".jsp"/>
</bean>
<!--注册注解驱动。
(1)响应ajax请求,返回json字符串。
(2)解决静态资源访问问题。-->
<mvc:annotation-driven/>
<!--加载静态资源图片啊,jQuery文件啊等等-->
<mvc:resources location="js/" mapping="/js/**"/>
<!--
1.在SpringMVC配置文件中声名拦截器:拦截器可以有一个或者多个。
2.path:指定拦截器拦截的URI地址,可以使用通配符 **
(1)**:表示任意的字符,可以表示文件、目录、多级目录下的文件。
(2)http://localhost:8080/student/addStudent.do
(3)http://localhost:8080/user/addUser.do
3.class:声名拦截器对象
-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.wind.handler.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
3.1.3 编写spring.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"
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">
<!--Spring的配置文件,用来声名service、dao、工具类等对象-->
<!--加载连接mysql时需要的配置文件-->
<context:property-placeholder location="classpath:config/jdbc.properties"/>
<!--声名数据源,连接数据库-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--配置数据库连接池的初始化大小、最小、最大-->
<property name="initialSize" value="5"/>
<property name="minIdle" value="5"/>
<property name="maxActive" value="20"/>
<!--配置获取连接等待超时的时间,单位是毫秒-->
<property name="maxWait" value="10000"/>
<!--配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒-->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!--配置一个连接在池中最小生存的时间,单位是毫秒-->
<property name="minEvictableIdleTimeMillis" value="300000"/>
</bean>
<!--声名一个SqlSessionFactoryBean,用它来创建SqlSessionFactory-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:config/mybatis.xml"/>
</bean>
<!--声名MyBatis的扫描器,创建dao接口接口对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<property name="basePackage" value="com.wind.dao"/>
</bean>
<!--声名service的注解@Service所在的包名-->
<!--<context:component-scan base-package="com.wind.service,com.wind.serviceImpl"/>-->
<context:component-scan base-package="com.wind.service*"/>
<!--事务的配置:注解的配置方式;aspectJ的配置方式。可以后期去加。-->
</beans>
3.1.4 编写mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 打印SQL-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--给实体类设置别名-->
<typeAliases>
<package name="com.wind.entity"/>
</typeAliases>
<!--SQL Mapper映射文件的位置-->
<mappers>
<!--name:是包名,这个包下的mapper文件能够一次性加载-->
<!--package:使用这个属性的前提是:
(1)mapper文件名称和dao接口名必须完全一样,包括大小写。
(2)mapper文件和dao接口必须在同一目录下。-->
<package name="com.wind.dao"/>
</mappers>
</configuration>
3.1.5 编写db.properties
##数据库驱动
jdbc.driverClassName=com.mysql.jdbc.Driver
##MySQL连接信息
jdbc.url=jdbc:mysql://127.0.0.1:3306/RUNOOB?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
##用户名
jdbc.username=root
##密码
jdbc.password=root
3.1.6 编写控制器:StudentController
package com.wind.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class StudentController {
@RequestMapping(value = "doIntercept.do", method = RequestMethod.POST)
public ModelAndView doIntercept(String name, Integer age) {
System.out.println("====StudentController.doIntercept()方法执行了====");
//创建一个视图
ModelAndView modelAndView = new ModelAndView();
//添加数据,这两个数据是存储在request作用域中的
modelAndView.addObject("myname", name);
modelAndView.addObject("myage", age);
//添加视图
modelAndView.setViewName("show");
return modelAndView;
}
}
3.1.7 编写拦截器MyInterceptor(需要 implement HandlerInterceptor)
package com.wind.handler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 当前这个类是一个拦截器类,用于拦截用户的请求
*/
@Component
public class MyInterceptor implements HandlerInterceptor {
/**
* 1、preHandle:预处理方法,很重要,是整个项目中所有请求的门户,可以控制请求是否需要被处理。
* 返回true:表示请求通过了拦截器的验证,可以继续执行后续的业务逻辑。
* 返回false:表示请求没有通过拦截器的验证,不可以继续执行后续的业务逻辑,请求到这儿就截止了。
* <p>
* 2、参数:
* HttpServletRequest:请求参数
* HttpServletResponse:应答结果
* handler:被拦截的控制器
* <p>
* 3、返回值boolean:
* (1)true:表示请求通过了拦截器的验证,可以继续执行后续的业务逻辑。
* MyInterceptor.preHandle()方法执行了...
* ====StudentController.doIntercept()方法执行了====
* MyInterceptor.postHandle()方法执行了...
* MyInterceptor.afterCompletion()方法执行了...
* (2)false:表示请求没有通过拦截器的验证,不可以继续执行后续的业务逻辑,请求到这儿就截止了。
* MyInterceptor.preHandle()方法执行了...
* <p>
* 4、特点:在这个方法中可以获得用户请求信息,从而判断用户请求信息是否符合要求,
* 比如可以判断用户是否登陆了,是否有权限去访问某个URL链接地址。
* (1)如果符合要求:可以放行请求,由处理器接着执行真正的业务逻辑。
* (2)如果不符合要求:可以截断请求,请求不能被处理。
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//注意:这里面可以做你自己的业务逻辑,根据业务逻辑需要来返回true或者false。
System.out.println("MyInterceptor.preHandle()方法执行了...");
//当返回false的时候,可以转发到一个jsp页面给用户一个提示。
request.getRequestDispatcher("/tips.jsp").forward(request, response);
return false;
}
/**
* 1、postHandle:后处理方法
* 2、参数:
* HttpServletRequest:请求参数
* HttpServletResponse:应答结果
* handler:被拦截的控制器
* ModelAndView:处理器方法的返回值对象
* 3、特点:
* (1)无返回值。
* (2)在处理器方法执行之后才执行的。
* (3)可以对处理器方法执行之后的结果ModelAndView做数和视图的修正,可以影响最终吐给用户的数据。
* (4)主要是对原处理器执行结果做的二次修正。
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle()方法执行了...");
}
/**
* 1、afterCompletion:最后执行的方法
* 2、参数:
* HttpServletRequest:请求参数
* HttpServletResponse:应答结果
* handler:被拦截的控制器
* Exception:程序发生的异常
* 3、特点:
* (1)在"请求处理完成"之后执行的方法。
* (2)什么是"请求处理完成"呢?框架中规定是在你的处理器对视图处理完成之后,对视图做了forward处理,就算是"请求处理完成"了。
* (3)一般是做资源回收的,程序在处理请求的执行中创建了一些对象,在这里可以删除,回收内存。
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion()方法执行了...");
}
}
3.1.8 编写前端主页面:index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<%
String basePath = request.getScheme()
+ "://" + request.getServerName()
+ ":" + request.getServerPort()
+ request.getContextPath() + "/";
%>
<head>
<title>Title</title>
<base href="<%=basePath%>"/>
</head>
<body>
<p>一个拦截器</p>
<form action="doIntercept.do" method="post">
姓名:<input type="text" name="name"><br/>
年龄:<input type="text" name="age"><br/>
<input type="submit" value="提交请求">
</form>
<br/>
</body>
</html>
3.1.9 编写处理器StudentController处理完成后转发到的页面:show.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/jsp/show.jsp 从request作用域中取出数据</h3>
<h3>myname数据是:${myname}</h3>
<h3>myage数据是:${myage}</h3>
</body>
</html>
3.1.10 编写拦截器返回false时拦截器内部转发到的友好页面:tips.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<%
String basePath = request.getScheme()
+ "://" + request.getServerName()
+ ":" + request.getServerPort()
+ request.getContextPath() + "/";
%>
<head>
<title>Title</title>
<base href="<%=basePath%>"/>
</head>
<body>
<p>tips.jap:请求被拦截了,不能继续向后执行了</p>
</body>
</html>
3.1.11 测试preHandler()方法:预处理方法
(1)preHandler返回true时:
(2)preHandler返回false时:
3.1.12 测试postHandler()方法:后处理方法
(1)首先preHandler()需要返回true
(2)在postHandler()方法中对处理器返回的结果做二次修正
package com.wind.handler;
import org.springframework.stereotype.Component;
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;
/**
* 当前这个类是一个拦截器类,用于拦截用户的请求
*/
@Component
public class MyInterceptor implements HandlerInterceptor {
/**
* 1、preHandle:预处理方法,很重要,是整个项目中所有请求的门户,可以控制请求是否需要被处理。
* 返回true:表示请求通过了拦截器的验证,可以继续执行后续的业务逻辑。
* 返回false:表示请求没有通过拦截器的验证,不可以继续执行后续的业务逻辑,请求到这儿就截止了。
* <p>
* 2、参数:
* HttpServletRequest:请求参数
* HttpServletResponse:应答结果
* handler:被拦截的控制器
* <p>
* 3、返回值boolean:
* (1)true:表示请求通过了拦截器的验证,可以继续执行后续的业务逻辑。
* MyInterceptor.preHandle()方法执行了...
* ====StudentController.doIntercept()方法执行了====
* MyInterceptor.postHandle()方法执行了...
* MyInterceptor.afterCompletion()方法执行了...
* (2)false:表示请求没有通过拦截器的验证,不可以继续执行后续的业务逻辑,请求到这儿就截止了。
* MyInterceptor.preHandle()方法执行了...
* <p>
* 4、特点:在这个方法中可以获得用户请求信息,从而判断用户请求信息是否符合要求,
* 比如可以判断用户是否登陆了,是否有权限去访问某个URL链接地址。
* (1)如果符合要求:可以放行请求,由处理器接着执行真正的业务逻辑。
* (2)如果不符合要求:可以截断请求,请求不能被处理。
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
//注意:这里面可以做你自己的业务逻辑,根据业务逻辑需要来返回true或者false。
System.out.println("MyInterceptor.preHandle()方法执行了...");
//当返回false的时候,可以转发到一个jsp页面给用户一个提示。
//request.getRequestDispatcher("/tips.jsp").forward(request, response);
return true;
}
/**
* 1、postHandle:后处理方法
* 2、参数:
* HttpServletRequest:请求参数
* HttpServletResponse:应答结果
* handler:被拦截的控制器
* ModelAndView:处理器方法的返回值对象
* 3、特点:
* (1)无返回值。
* (2)在处理器方法执行之后才执行的。
* (3)可以对处理器方法执行之后的结果ModelAndView做数和视图的修正,可以影响最终吐给用户的数据。
* (4)主要是对原处理器执行结果做的二次修正。
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle()方法执行了...");
//对处理器执行的结果做一个二次修正
if (modelAndView != null) {
modelAndView.addObject("mydate", new Date());
modelAndView.setViewName("postHandleJsp");
}
}
/**
* 1、afterCompletion:最后执行的方法
* 2、参数:
* HttpServletRequest:请求参数
* HttpServletResponse:应答结果
* handler:被拦截的控制器
* Exception:程序发生的异常
* 3、特点:
* (1)在"请求处理完成"之后执行的方法。
* (2)什么是"请求处理完成"呢?框架中规定是在你的处理器对视图处理完成之后,对视图做了forward处理,就算是"请求处理完成"了。
* (3)一般是做资源回收的,程序在处理请求的执行中创建了一些对象,在这里可以删除,回收内存。
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion()方法执行了...");
}
}
(3)将修正后的结果在转发的新的视图中展示出来:postHandlerJsp.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/jsp/postHandleJsp.jsp 从request作用域中取出数据</h3>
<h3>myname数据是:${myname}</h3>
<h3>myage数据是:${myage}</h3>
<h3>拦截器增加的数据:${mydate}</h3>
</body>
</html>
(4)测试postHandler()方法
3.1.13 测试afterCompletion()方法:最后执行的方法
(1)afterCompletion():
/**
* 1、afterCompletion:最后执行的方法
* 2、参数:
* HttpServletRequest:请求参数
* HttpServletResponse:应答结果
* handler:被拦截的控制器
* Exception:程序发生的异常
* 3、特点:
* (1)在"请求处理完成"之后执行的方法。
* (2)什么是"请求处理完成"呢?框架中规定是在你的处理器对视图处理完成之后,对视图做了forward处理,就算是"请求处理完成"了。
* (3)一般是做资源回收的,程序在处理请求的执行中创建了一些对象,在这里可以删除,回收内存。
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
eTime = System.currentTimeMillis();
System.out.println("MyInterceptor.afterCompletion()方法执行了..." +
"preHandler到afterCompletion执行结束用时=" + (eTime - bTime));
}
(2)测试结果:
3.1.14 经过preHandler/postHandler/afterCompletion之后的项目结构图
3.2 在项目中声名多个拦截器时
3.2.1 声名拦截器1的类
package com.wind.handler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 当前这个类是一个拦截器类,用于拦截用户的请求
*/
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("1111111-MyInterceptor.preHandle()方法执行了...");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("1111111-MyInterceptor.postHandle()方法执行了...");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("1111111-MyInterceptor.afterCompletion()方法执行了...");
}
}
3.2.2 声名拦截器2的类
package com.wind.handler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 当前这个类是一个拦截器类,用于拦截用户的请求
*/
@Component
public class MyInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("2222222-MyInterceptor.preHandle()方法执行了...");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("2222222-MyInterceptor.postHandle()方法执行了...");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("2222222-MyInterceptor.afterCompletion()方法执行了...");
}
}
3.2.3 在springmvc.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 https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--SpringMVC的配置文件,用来声名Controller和其他web相关的对象-->
<!--配置组件扫描器-->
<context:component-scan base-package="com.wind.controller"/>
<!--视图解析器:添加前缀和后缀。
SpringMVC框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver
中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,
视图解析器会自动完成拼接。-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--视图文件的路径-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--视图文件的扩展名-->
<property name="suffix" value=".jsp"/>
</bean>
<!--注册注解驱动。
(1)响应ajax请求,返回json字符串。
(2)解决静态资源访问问题。-->
<mvc:annotation-driven/>
<!--加载静态资源图片啊,jQuery文件啊等等-->
<mvc:resources location="js/" mapping="/js/**"/>
<!--
1.在SpringMVC配置文件中声名拦截器:拦截器可以有一个或者多个。
2.path:指定拦截器拦截的URI地址,可以使用通配符 **
(1)**:表示任意的字符,可以表示文件、目录、多级目录下的文件。
(2)http://localhost:8080/student/addStudent.do
(3)http://localhost:8080/user/addUser.do
3.class:声名拦截器对象
4.拦截器在框架内部其实存储在一个ArrayList中的,先声明的放在ArrayList中的前面,后声明的放在ArrayList中的后面。
也即:对于多个拦截器而言,先声明的先执行,后声明的后执行。
-->
<mvc:interceptors>
<!--声名第一个拦截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.wind.handler.MyInterceptor"/>
</mvc:interceptor>
<!--声名第二个拦截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.wind.handler.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
3.2.4 当项目中存在多个拦截器时, 拦截器的执行流程图和执行顺序
3.3 过滤器和拦截器之间的区别与联系