概述
Spring MVC 是 Spirng 处理 WEB 层请求的一个 组件(模块)。
MVC 是指:
- Model:模型层,包含实体类、业务逻辑基层、数据访问层。
- View:视图层,用于页面的显示。
- Controller:控制层,用来接收客户端的请求,并返回响应到客户端。
入门程序
- 新建 Maven 项目,选择 webapp 模板
- 然后补全目录
- 修改 Maven 编译级别
改成合适的 Java 版本
- 添加 Spring MVC 依赖和 Servlet 依赖
<!-- Spring MVC 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- Spring context 命名空间-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!-- 添加 servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
导入 servlet 时要,注意和你使用的 Tomcat 版本
如果使用 Tomcat 10+,应该导入 jakarta.servlet
如果使用 Tomcat 10 以下的版本,应该打入 javax.servlet
具体参考:https://tomcat.apache.org/whichversion.html
- 将 pom.xml 中 标签的内容替换为:
<build>
<!-- 指定资源文件 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
指定资源文件所在位置
- 在 webapp 目录下新建 admin 目录,再在 admin 下新建 hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
hello world
</body>
</html>
- 添加 springmvc 配置文件:
spring-mvc.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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 添加包扫描,开启对 @Controller 注解的支持 -->
<context:component-scan base-package="com.guyi.springmvc.controller"/>
<!-- 添加视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置前缀 -->
<property name="prefix" value="/admin/"/>
<!-- 配置后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 将
src/main/webapp/WEB-INF/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">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化springmvc配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置拦截的请求(需要处理的请求) -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 创建一个 controller 包,在这个包下创建一个 HelloController.java
@Controller
@RequestMapping("/entry")
public class HelloController {
@RequestMapping("/hello.action")
public String hello() {
System.out.println("GET hell");
return "hello";
}
}
- 配置 Tomcat,这里使用 Tomcat 8.5, 如下设置:
- 启动项目,浏览器访问 localhost:8080/springmvc/entry/hello,显示如下内容:
细节
- 下图中的配置是 web.xml 中的配置:
作用是将特定 URL 模式映射到一个特定的 Servlet 处理器,例如:Spring MVC 的 DispatcherServlet。
当 <servlet-mapping>
元素的 <url-pattern>
设置为 “/” 时,访问 “localhost:8080/springmvc/entry/hello” 时,Controller 的响应结果会被拼接为 “/admin/hello.jsp”,这时,会去访问 "“localhost:8080/springmvc/admin/hello.jsp”,可以正常访问到资源。
而当 设置为 “/*” 时,访问 “localhost:8080/springmvc/entry/hello” 时,Controller 的响应结果会被拼接为 “/springmvc/admin/hello.jsp”,这时,会去访问 "“localhost:8080/springmvc/springmvc/admin/hello.jsp”,多了一个 springmvc,不可以访问到资源。
原因如下:
当 <servlet-mapping>
元素的 <url-pattern>
设置为 “/” 时,表示将 DispatcherServlet 映射到 Web 应用程序的根目录下的所有 URL。例如,入门程序的上下文路径是" /springmvc",那么所有的URL,都会被 DispatcherServlet 处理,比如:请求 “/springmvc/entry/hello”。
当 设置为 “/*” 时,表示将 DispatcherServlet 映射到所有的 URL,包括根路径。这意味着所有的 URL 都会交给 DispatcherServlet 处理,包括静态资源(如CSS、JS文件等)。这种配置可能会导致性能问题,因为 DispatcherServlet 将会处理所有的请求。
因此, 设置为 “/” 比较推荐,它只映射到应用程序的根路径下的 URL,而不会处理静态资源,可以更有效地利用服务器资源。
Spring MVC 的执行流程
Spring MVC 注解开发
@RquestMapping
作用
- 用来映射服务器访问的路径
- 这个注解可以写在方法上,为这个方法注册一个可以访问的名称(路径)
- 这个注解可以加在类上,用于区分不同类中相同访问路径的方法(方法上
@RequsestMapping()
指定的路径相同) - 这个注解可以区分 GET 请求和 POST 请求 :
@RequestMapping("/path", method=RequestMethod.POST)
支持的请求类型如下:
如果不指定 method 的值,则任意请求都可以访问
详解 RequestMethod 各值的作用
RequestMethod 是 Spring 框架中的一个枚举类,用于表示 HTTP 请求的方法类型。它定义了一些常用的 HTTP 方法,包括:
- GET:用于获取资源。一般用于查询操作。
- POST:用于提交数据,向服务器发送数据。一般用于创建资源。
- PUT:用于更新已存在的资源。
- DELETE:用于删除资源。
- HEAD:用于获取资源的头部信息,但不返回具体内容。
- OPTIONS:用于获取服务器支持的 HTTP 方法。
- PATCH:用于局部更新资源。
这些枚举值在 Spring MVC 中的 @Controller 和 @RequestMapping 注解中经常被用到,用于指定处理请求的方法控制器应该对应的 HTTP 请求方法。
例如,使用 @RequestMapping 注解时,可以通过指定 RequestMethod 来限制处理方法只能响应特定的 HTTP 方法,如 @RequestMapping(value = "/example", method = RequestMethod.GET)
表示只能处理 GET 请求。
再比如:@RequestMapping(value = "/example", method = RequestMethod.HEAD)
,表示只处理处理 HTTP 请求中的 HEAD 方法,并且映射到路径 “/example”。
HEAD 方法是 HTTP 中的一个请求方法,它与 GET 方法类似,区别在于 HEAD 方法只请求服务器返回特定资源的头部信息,而不返回具体内容。通常用于获取资源的元数据,如获取内容类型、内容长度、最后修改时间等,而不需要获取实际的资源内容。
因此,在处理HEAD请求时,应注意不要在响应中包含具体内容,只返回头部信息即可。
基本使用
@Controller
@RequestMapping("/xx")
public class xxController {
@RequestMapping("/yy")
public String yyy() {}
}
访问的时候,localhost:8080/springmvc/xx/yy
如果将类上的 @RequestMapping("/xx")
去掉,应当访问 localhost:8080/springmvc/yy
接收请求的参数
- 单个数据获取
假如有一个 form 表单
<h2>单个数据获取</h2>
<form action="${pageContext.request.contextPath}/one">
姓名: <input type="text" name="name"/><br>
年龄: <input type="text" name="age"/><br>
<input type="submit" value="提交">
</form>
Spring MVC 接收表单提交的数据
/**
* 姓名: <input type="text" name="myName"/><br>
* 年龄: <input type="text" name="age"/><br>
* 形参就是 name 属性的值, SpringMVC 可以进行自动类型转换
*/
@RequestMapping("/one")
public String One(String name, int age) {
System.out.println("myName: " + myName);
System.out.println("age: " + age);
return "main";
}
- 对象封装方式获取
可以在入门程序的 index.jsp
中添加一个表单
<h2>对象封装方式获取</h2>
<form action="${pageContext.request.contextPath}/entry/user">
姓名: <input type="text" name="name"/><br>
年龄: <input type="text" name="age"/><br>
<input type="submit" value="提交">
</form>
然后创建一个实体类
public class User implements Serializable {
private static final long serialVersionUID = 8351864396859634105L;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
// todo
// toString、setter、getter 方法自行添加
}
在 HelloController 类中添加如下内容:
@RequestMapping(value = "/user")
public void getUser(User user) {
System.out.println(user);
}
启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/)
,在输入框中输入内容,然后点击提交按钮,IDE 控制台应该打印如下内容:
具体的值应该和你输入的内容对应
- 动态占位符获取
还是在 index.jsp
中添加内容:
<h2>动态占位符提交数据</h2>
<a href="${pageContext.request.contextPath}/entry/dynamic/李四/22">动态提交</a>
如果页面出现乱码,在 index.jsp
的最上方添加:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
在 HelloController 类中添加如下内容:
@RequestMapping("/dynamic/{name}/{age}")
public void byDynamic(@PathVariable String name, @PathVariable("age") int age) {
System.out.println("姓名: " + name);
System.out.println("年龄: " + age);
}
启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/)
,点击刚添加的链接,IDE 控制台应该打印如下内容:
- 映射名称不一致
还是在 index.jsp
中添加内容:
<h2>映射名称不一致</h2>
<form action="${pageContext.request.contextPath}/entry/variation">
姓名: <input type="text" name="xxxName"/><br>
年龄: <input type="text" name="age"/><br>
<input type="submit" value="提交">
</form>
在 HelloController 类中添加如下内容:
@RequestMapping("/variation")
public void variationParams(@RequestParam("xxxName") String name, @RequestParam("age") Integer xxxAge) {
System.out.println("name:" + name);
System.out.println("xxxAge:" + xxxAge);
}
当请求的参数名称无法和方法的形参名称对应起来是,可以使用 @RequestParam()
注解来建立映射关系,将 @RequestParam()
的 value
属性值设置为请求中的对应的参数名称即可。
启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/)
,在输入框中输入内容,然后点击提交按钮,IDE 控制台应该打印如下内容:
具体的值应该你输入的内容对应
- 手动获取
在 index.jsp
中添加:
<h2>手工提取数据</h2>
<form action="${pageContext.request.contextPath}/manual">
姓名: <input type="text" name="name"/><br>
年龄: <input type="text" name="age"/><br>
<input type="submit" value="提交">
</form>
在 HelloController 类中添加如下内容:
@RequestMapping("/manual")
public void byManual(HttpServletRequest httpServletRequest) {
String name = httpServletRequest.getParameter("name");
int age = Integer.parseInt(httpServletRequest.getParameter("age"));
System.out.println("name: " + name);
System.out.println("age: " + age);
}
启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/)
,在输入框中输入内容,然后点击提交按钮,IDE 控制台应该打印如下内容:
具体的值应该你输入的内容对应
@RequestBody
这个标签可以用来处理 POST、PUT、PATCH、DELETE 请求的参数,这个标签不可以用于处理 GET 请求。将请求内容转化为对应的 Java 对象。
示例:
- 在
controller
包下添加一个 RequestBodyController 类:
@Controller
@RequestMapping("/post")
public class RequestBodyController {
@RequestMapping(value = "/test", method = RequestMethod.POST)
@ResponseBody
public User test(@RequestBody User user) {
System.out.println(user);
return user;
}
}
- 在
webapp/admin
下添加test_requestbody.jsp
文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.3.js"></script>
<script type="text/javascript">
function showStu() {
const name = $("#name").val();
const age = $("#age").val();
const user = {
name,
age
}
user.name = name
user.age = age
const userJson = JSON.stringify(user);
$.ajax({
url: "${pageContext.request.contextPath}/post/test",
type: "post",
data: userJson,
dataType: "json",
contentType: "application/json;charset=UTF-8",
success: function (ref){
alert("返回结果: " + JSON.stringify({result:ref}))
$("#myDiv").html("成功")
},
});
}
</script>
<body>
姓名: <input id="name" type="text" name="name"/><br>
年龄: <input id="age" type="text" name="age"/><br>
<input type="button" value="提交" onclick="showStu()">
<div id="myDiv">等待服务器返回数据</div>
</body>
</html>
- 浏览器访问
localhost:8080/springmvc/admin/test_requestbody.jsp
,点击按钮,观察效果。
Controller 中方法的返回值
- String
- 客户端资源的地址
- 自动拼接前缀和后缀
- 屏蔽自动拼接字符串
- 指定返回路径
- Object
- 返回 json 格式的对象
- 自动将对象或者集合转为 json
- 一般用于 ajax 请求
- 注意:底层使用的是 Jackson 工具进行转化,因此必须添加 Jackson 依赖
- void
- 一般用于 ajax 请求
- 基本数据类型
- 一般用于 ajax 请求
- ModelAndView
- 返回数据和视图对象
- 使用的很少
示例:完成 ajax 请求访问服务器,返回 Student 集合
- 添加 Jackson 依赖
<!-- jackson 依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.1</version>
</dependency>
- 在 webapp 目录下新建 js,添加 JQuer 函数库
- 在 webapp/admin 目录下新建一个 Student.jsp
- 在 Student.jsp 添加如下内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>学生页</title>
</head>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.3.js"></script>
<script type="text/javascript">
function showStu() {
$.ajax({
url: "${pageContext.request.contextPath}/student/list",
type: "get",
dataType: "json",
success: function (stuList){
var s = ""
$.each(stuList, function (i, stu) {
s += stu.name + "-------" + stu.age + "<br>"
})
// 回显数据
$("#myDiv").html(s)
}
});
}
</script>
<body>
<a href="javaScript:showStu()">访问服务器返回学生集合</a>
<div id="myDiv">等待服务器返回数据</div>
</body>
</html>
该内容不是本篇文章重点,不做解释
- 添加一个 Student 类
public class Student implements Serializable {
private static final long serialVersionUID = 4671473146568140537L;
/**
* 学生姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
// todo
// 无参构造器、全参数构造器、toString、setter、getter 自行添加
}
- 创建一个 UserController:
@Controller
@RequestMapping("/student")
public class UserController {
@RequestMapping("/list")
@ResponseBody
public List<Student> getList() {
List<Student> list = new ArrayList<>();
Student student1 = new Student("张三", 22);
Student student2 = new Student("李四", 23);
Student student3 = new Student("王五", 24);
list.add(student1);
list.add(student2);
list.add(student3);
// Spring MVC 会将集合转为数组
return list;
}
}
- 在 springmvc.xml 文件中添加注解驱动:mvc:annctationdriven/,它用来解析 @ResponseBody 注解:
添加 mvc 命名空间:
xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
添加注解驱动器
<!-- 添加注解驱动, 专门处理 ajax 请求的 -->
<mvc:annotation-driven>
<!-- 解决需要添加后缀问题 -->
<mvc:path-matching suffix-pattern="true"/>
</mvc:annotation-driven>
<!-- 定义 DefaultServletHttpRequestHandler -->
<mvc:default-servlet-handler />
对于 <mvc:default-servlet-handler />:
对进入 DispatcherServlet 的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。
如果没有配置这个内容,加载 js 的时候可能会出现问题。
- 启动项目,访问
[http://localhost:8080/springmvc/admin/Student.jsp](http://localhost:8080/springmvc/admin/Student.jsp)
内容变为:
Spirng MVC 解决中文乱码
在执行上面的例子时,如果有传入中文数据,打印出来的内容可能存在乱码,可以通过以下方式解决:
在 web.xml
文件中添加如下配置:
<!-- 中文编码过滤器配置 -->
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 配置参数 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<!-- 拦截所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
这样一般就不会出现乱码问题了。
这段配置一定不要去背!!!要用的时候复制即可。
请求转发和请求重定向
请求转发:forward
请求重定向:redirect
请求转发是基于服务器的跳转。
重定向是基于客户端的跳转,直接体现是浏览器地址栏上的 url 会改变。
为了便于描述,后续将被
**@RequestMapping()**
标记的方法称之为 action
在 Spring MVC 中默认是请求转发,如果要开启重定向,在返回地址上添加一个 redirect:
。
示例:
// 需要重定向
return "redirect:/other.action";
使用这种操作,Spring MVC 将不会对请求地址进行前后缀的拼接,可以将请求重定向到另外一个 action 中。
如果你希望在请求转发时,不进行前后缀的拼接,可以使用如下方式:
return "forward:/other";
使用这种操作,Spring MVC 将不会对请求地址进行前后缀的拼接,可以将请求跳转至另外一个 action 中。
如果不加 forward:
,进行的也是请求转发,只不过Spring MVC 会根据设置的前后缀进行请求拼接。
实际测试一下:
- 在
admin
目录下新建三个jsp
文件
request.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>转发和重定向</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/one">请求转发页面(默认)</a><br><br>
<a href="${pageContext.request.contextPath}/two">请求转发action</a><br><br>
<a href="${pageContext.request.contextPath}/three">重定向页面</a><br><br>
<a href="${pageContext.request.contextPath}/four">重定向action</a><br><br>
</body>
</html>
test_forward.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>跳转</title>
</head>
<body>
你的请求被转发到这里,地址栏的 url 还是原来的!
</body>
</html>
test_redirect.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>重定向</title>
</head>
<body>
你的请求被重定向到这里,地址栏的 url 还是改变了!
</body>
</html>
- 在
controller
包下创建RequestTypeController.java
@Controller
public class RequestTypeController {
@RequestMapping("/other")
public String other() {
return "hello";
}
@RequestMapping("/one")
public String one() {
System.out.println("这是请求转发页面跳转");
// 默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
return "test_forward";
}
@RequestMapping("/two")
public String two() {
System.out.println("这是请求转发action");
// "forward:" 表示这是请求转发action, 不要进行前后缀的拼接
return "forward:/other";
}
@RequestMapping("/three")
public String three() {
System.out.println("这是重定向页面");
return "redirect:/admin/test_redirect.jsp";
}
@RequestMapping("/four")
public String four() {
System.out.println("这是重定向action");
return "redirect:/other";
}
}
- 启动项目,访问
[http://localhost:8080/springmvc/admin/request.jsp](http://localhost:8080/springmvc/admin/request.jsp)
,依次点击链接,观察页面变化,特别是浏览器地址栏 url 的变化。
这里的效果不好展示,感兴趣的可以自行测试效果。
常用的 Controller 方法参数类型
- HttpServletRequest:用于获取 HTTP 请求的信息,如请求头、请求参数等。
- HttpServletResponse:用于操作 HTTP 响应,如设置响应头、发送响应内容等。
- HttpSession:用于获取或操作当前会话的状态信息,如存储和获取属性值等。
- Model:用于在请求处理方法中传递数据,以供视图层展示。可以通过添加属性到 Model 对象中,然后在视图层渲染时访问这些属性。
- Map:作为 Model 的替代选择,也可以用于传递数据到视图层。Map 对象可以用来存储键值对对象,将数据传递给视图层。
- ModelMap:继承自 LinkedHashMap,即在 Map 的基础上扩展了一些 Spring MVC 特有的功能。主要用于封装数据以供视图渲染,同时提供了方便的方法用于操作和访问 Model 数据。
注意:Model、Map、ModelMap 和 request 一样,都是用请求作用域进行数据传递,所以服务器的跳转必须是请求跳转,无法在重定向时使用。
日期处理
日期的提交处理
单个日期处理
假如有一个表单
<form action="${pageContext.request.contextPath}/testDate">
日期: <input type="date" name="date"/><br>
<input type="submit" value="提交"/><br>
</form>
接收时,可以使用如下代码进行日期格式化:
@Controller
public class MyDateAction {
@RequestMapping("/testDate")
public String myDate(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
System.out.println("日期: " + date);
return "show";
}
}
这里使用的是 @DateTimeFormat()
注解进行日期格式化,这个注解的 pattern
属性可以用来指定日期的格式。
类中的全局日期处理器
@Controller
public class MyDateAction {
/**
* 格式化日期的刷子
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
/**
* 注册一个全局日期处理的注解
* @param dataBinder 数据(可以绑定的不止有日期)绑定
*/
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
/*
需要处理的数据类型:Date.class
使用的工具: new CustomDateEditor(sdf, true)
sdf: 工具
true: 允许为空
*/
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}
@RequestMapping("/testDate")
public String myDate(Date date) {
System.out.println("日期: " + date);
return "show";
}
}
给实体类的日期属性注入依赖
在对应的属性或者 setter 方法上添加 @DateTimeForamt(pattern="yyyy-MM-dd")
给 JSON 进行数据处理
在对应属性或者 getter 方法(一般情况下标注的是 getter,但是也可以用于 setter 方法)上添加 @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
<mvc:annotation-driven/>
这个标签之前已经使用过了,这里主要介绍它的作用。
<mvc:annotation-driven/>
标签会自动注册两个 Bean:
- DefaultAnnotationHandlerMapping(处理映射器)
主要处理 @RequestMapping
注解,并将其注册到请求映射表中
- AnntationMethodHandlerAdapter(处理适配器)
主要处理请求,确定每个请求需要用哪个 Controller 类的哪个方法来处理
这个标签的主要作用:
- 支持
@Controller
注解:可以使用@Controller
注解标记控制器类,使其成为 Spring MVC 的处理器。 - 支持
@RequestMapping
注解:可以使用@RequestMapping
注解来映射 URL 和处理方法,方便定义请求和处理之间的映射关系。 - 支持
@PathVariable
注解:可以在处理方法的参数中使用@PathVariable
注解,将 URL 路径中的变量值作为参数传递。
@RequestMapping("/users/{id}")
public String getUser(@PathVariable("id") Long userId, Model model) {
// todo
}
- 支持
@RequestParam
注解:可以在处理方法的参数中使用@RequestParam
注解,获取请求参数的值。
@RequestMapping("/search")
public String searchUsers(@RequestParam("q") String query, Model model) {
// todo
}
- 支持
@ResponseBody
注解:可以将方法的返回值直接作为 HTTP 响应的内容,而无需处理视图解析。
@RequestMapping("/api/users")
@ResponseBody
public List<User> getUsers() {
// 返回 User 对象集合
return userService.getUsers();
}
- 支持
@RequestBody
注解:可以将 HTTP 请求的内容转换为 Java 对象,方便处理请求体的数据。
@RequestMapping(value = "/api/user", method = RequestMethod.POST)
public ResponseEntity<?> createUser(@RequestBody User user) {
// 处理用户创建逻辑
return ResponseEntity.ok().build();
}
需要注意的是,@RequestBody
注解不能对 GET 请求使用,可以对 POST、PUT、PATCH、DELETE 请求使用。
- 支持
@ModelAttribute
注解:可以在请求处理方法中使用@ModelAttribute
注解,将从请求中获取的数据绑定到模型对象上。
@ModelAttribute("user")
public User getUser() {
return new User();
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String registerUser(@ModelAttribute("user") User user, Model model) {
// 处理用户注册逻辑
model.addAttribute("message", "User registered successfully");
return "registration-success";
}
注意:在请求处理方法中使用 @ModelAttribute 注解,将从请求中获取的数据绑定到模型对象上。
访问 WEB-INF 下的资源
资源放在 WEB-INF 目录下可以保证资源的安全性。**在这个目录下的动态资源不可以直接被访问,必须通过请求转发的方式进行访问。**这样可以避免通过地址栏直接对资源的访问。
注意:通过重定向是无法访问放在 WEB-INF 目录下的动态资源的。
拦截器
Spring 中的 Interceptor 拦截器,主要的作用是拦截指定的用户请求,并进行相应的预处理与后处理。
值得注意的是,拦截器可以在请求处理的不同阶段进行操作,包括前置处理、后置处理和完成后处理。通过配置拦截器链,可以将自定义的拦截器应用到特定的请求或请求路径上。
拦截器的执行时机:
- preHandle():预处理,在请求被处理之前进行操作。
- postHandle():后处理,在请求处理之后,但结果还没渲染前进行操作,可以改变响应结果。
- afterCompletion:最终处理,所有的请求响应结束后执行善后工作,清理对象,关闭资源。
拦截器的应用场景
- 日志记录:记录请求信息的日志。
- 权限检查:如登录检查等。
- 性能检测:检测方法的执行时间。
实现拦截器的两种方式
实现 HandlerInterceptor 接口
- 创建一个 Interceptor 包,在这个包下编写一个实现了
HandlerInterceptor
的类:
@Component // 会注册一个 id 为 myInterceptor 的 Bean
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 在请求处理之前执行的操作
return true; // 返回 true 表示继续处理请求,返回 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 {
// 在请求完成后执行的操作,包括视图渲染完成后
}
}
可以根据需要,在对应的方法中编写对应的处理代码
- 在 spring-mvc.xml 文件中添加
<mvc:interceptors>
来配置拦截器
<!-- 添加包扫描 -->
<!--<context:component-scan base-package="com.guyi.springmvc.controller/>-->
<context:component-scan base-package="com.guyi.springmvc.controller,com.guyi.springmvc.interceptor"/>
<!-- 使用这种方式会拦截所有请求 -->
<mvc:interceptors>
<ref bean="myInterceptor"/>
</mvc:interceptors>
如果需要指定拦截的目标:
<!-- 使用这种方式会拦截所有请求 -->
<mvc:interceptors>
<!-- 表示放行 /admin/hello.jsp -->
<mvc:exclude-mapping path="/admin/hello.jsp"/>
<ref bean="myInterceptor"/>
</mvc:interceptors>
该方法支持统配,比如:/admin/*
实现 HandlerInterceptorAdapter 类
- 在 Interceptor 包下创建一个类并继承 HandlerInterceptorAdapter 类
@Component
public class MyInterceptor extends HandlerInterceptorAdapter {
// 实现需要的拦截器逻辑
}
- 根据需要,重写对应方法来实现对应功能,比如:
- preHandle:在请求处理之前被调用,返回值决定是否继续处理请求。
- postHandle:在请求处理之后被调用,但是在视图被渲染之前调用,可以对ModelAndView进行操作。
- afterCompletion:在整个请求完成之后调用,可以进行一些资源清理工作。
- 在 spring-mvc.mxl 中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截的路径 -->
<mvc:mapping path="/**"/>
<ref bean="myInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
异常处理器
局部异常处理器
编写一个自定义异常处理器: ExceptionHandlerTestController.java
@Controller
@RequestMapping("/exception")
public class ExceptionHandlerTestController {
/**
* 模拟出现空指针异常的情况
*/
@RequestMapping("/null_pointer")
public String nullPointer() {
String s = null;
s.equals("");
return "hello";
}
/**
* 局部异常处理器
*/
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String localException(HttpServletRequest request, Exception e) {
System.out.println("异常对象 = " + e);
request.setAttribute("error", e);
return "exception";
}
}
在 webapp/admin
下添加一个发生异常时跳转的页面:exception.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常</title>
</head>
<body>
请求处理出现了异常
</body>
</html>
启动项目访问:http://localhost:8080/springmvc/exception/null_pointer,浏览器会显示如下内容:
IDE 控制台打印:
全局异常处理器
新建一个 exception
包,在这个包下新建一个全局异常处理器类。
全局异常处理器的优先级低于局部异常处理器。
/**
* 全局异常处理器
*/
@ControllerAdvice // 使用 @ControllerAdvice 用来标识全局异常处理器
public class GlobalExceptionHandler {
@ExceptionHandler({NumberFormatException.class, ClassCastException.class})
public String globalException(Exception e, HttpServletRequest request) {
System.out.println("异常对象 = " + e);
request.setAttribute("error ", e);
return "exception";
}
}
在 ExceptionHandlerTestController.Java 中添加一个方法
/**
* 模拟出现数据格式化异常
*/
@RequestMapping(value = "/number_format")
public String numberFormat() {
int number = Integer.parseInt("hello");
return "hello";
}
在 spring-mvc.xml 中将 exception
包加入被扫描的包
然后重启项目,浏览器访问 http://localhost:8080/springmvc/exception/number_format,跳转页面和局部异常处理器一致, IDE 控制台打印内容如下:
对于全局异常处理器,还可以使用 @RestControllerAdvice
注解定义。
@RestControllerAdvice
和 @ControllerAdvice
是 Spring 框架中用于全局异常处理和全局数据处理的注解。它们的区别主要在于处理的目标和返回值类型。
@ControllerAdvice
主要用于处理控制器层(Controller)的异常和数据。它可以定义全局异常处理器(Global Exception Handler),通过 @ExceptionHandler
注解来处理指定异常类型的方法,并返回自定义的错误页面或错误信息。另外,@ControllerAdvice
还可以使用 @ModelAttribute
注解来定义全局数据,在所有被控制器绑定的请求方法中都可以访问这些数据。
@RestControllerAdvice
是 Spring 4.3 版本引入的注解,在 @ControllerAdvice
的基础上增加了@ResponseBody
的功能(源码中 @RestControllerAdvice
被 @ResponseBody
注解标注)。它主要用于处理控制器层的异常,并以 JSON、XML 等形式返回处理结果。与 @ControllerAdvice
相比,@RestControllerAdvice
的异常处理方法和全局数据方法都会自动加上 @ResponseBody
注解,不需要再通过 @ResponseBody
注解单独指定返回类型。
简而言之,@RestControllerAdvice
主要是用于处理控制器层的异常,并返回 JSON 等形式的结果;而 @ControllerAdvice
用于处理控制器层的异常和数据,返回可定制的错误页面或错误信息。
Spring MVC 常用注解汇总
映射 URL
注解 | 作用 |
---|---|
@RequestMapping | 用来标记类和方法,用于映射URL路径到控制器的处理方法。可以用在类级别和方法级别。 |
@PostMapping | 作用和 @RequestMapping 一样,但是只能处理 POST 请求 |
@GetMapping | 作用和 @RequestMapping 一样,但是只能处理 GET 请求 |
@PutMapping | 作用和 @RequestMapping 一样,但是只能处理 PUT 请求 |
@PatchMapping | 作用和 @RequestMapping 一样,但是只能处理 PATCH请求 |
@DeleteMapping | 作用和 @RequestMapping 一样,但是只能处理 DELETE 请求 |
接收请求参数
注解 | 作用 |
---|---|
@PathVariable | 用于标记形参,获取 URL 中的参数 |
@RequestParam | 用于标记形参,获取 URL 中的参数 |
@RequestBody | 用于标记形参,将请求体的主体内容反序列化为 Java 对象 |
响应请求
注解 | 作用 |
---|---|
@RequestBody | 标记类和方法,用于将方法返回值序列化为响应的主体内容,不跳转到视图解析器 |
@ModelAttribute | 用于标记方法和参数,将方法参数或方法返回值绑定到向 Web 视图公开的命名模型属性 |
@SessionAttributes | 标记类,用于将模型对象中的属性暂存到会话中 |
其他
注解 | 作用 |
---|---|
@InitBinder | 用于标记方法,完成配置数据绑定器,比如日期格式化 |
@Validated | 用于标记方法,开启方法参数的校验功能,并触发校验框架对参数进行校验。 |
@ControllerAdvice | 用于处理控制器层的异常和数据,返回可定制的错误页面或错误信息。 |
@RestControllerAdvice | 处理控制器层的异常,并返回JSON等形式的结果 |
@ExceptionHandler | 用于标记方法,处理控制器内部异常,返回自定义的错误视图或错误响应 |