SpringMVC系列文章
1. SpringMVC 概述
1.1 关于三层架构和 MVC 模型
- 在我们常用的
B/S架构(Browser/Server,浏览器/服务器模式)
中,系统标准的三层架构为:表现层,业务层以及持久层,每一层都是各司其职,分工很明确。- 表现层也叫
web层
,负责和客户端进行数据交互,也就是接收客户端的请求,向客户端响应结果。在web层
中又可以细分为展示层和控制层,展示层负责结果的展示,控制层负责接受请求。表现层的设计一般都会采用 MVC 模型(表现层的设计模型,与其他层无关)。 - 业务层也叫
service层
,负责业务逻辑处理,一般会依赖数据层。如果需要对数据持久化,那么应该保证事务一致性。 - 持久层也叫
dao层
,负责数据持久化,简单地说就是用于和数据库进行交互,对数据库中的表进行增删查改。
- 表现层也叫
- MVC 全称为
Model View Controller
,是一种用于设计表现层的模式,每个部分都是各司其职。- Model(模型):数据模型,一般就是
JavaBean
,用于封装数据。 - View(视图):比如
JSP 、 HTML
,用于展示数据给用户。 - Controller(控制器):比如
Servlet
,用于和用户进行交互,也就是接收用户请求,对其进行处理,最终生成响应给用户。
- Model(模型):数据模型,一般就是
1.2 SpringMVC 简单介绍
- SpringMVC 是一种基于 Java 实现的 MVC 设计模型的请求驱动类型的轻量级WEB框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
- SpringMVC 是属于表现层的框架,见图
2. SpringMVC 入门
2.1 入门案例
- 创建 Maven 工程,导入依赖如下
<properties>
<!-- Spring 版本 -->
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<!-- provided:provided依赖范围只有在当JDK或者一个容器已提供该依赖之后才使用,provided依赖在编译和测试时需要,在运行时不需要。比如servlet api被tomcat容器提供 -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
- 创建 SpringMVC 的配置文件,这里我将其命名为
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 注解扫描的包 -->
<context:component-scan base-package="cn.ykf"/>
<!-- 配置视图解析器 -->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置 Spring 开启注解 MVC 的支持 -->
<mvc:annotation-driven/>
</beans>
- 在
web.xml
中配置DispatcherServlet
(核心控制器)
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 服务器启动就创建该控制器,并且加载 spring 配置文件,创建容器 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 对所有请求都拦截 -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 在
webapp
目录下创建index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>入门案例</title>
</head>
<body>
<h3>入门案例</h3>
<%-- 这里使用相对路径,使用绝对路径会变成了 http://localhost:8080/hello --%>
<a href="hello">点击进入入门案例</a><hr/>
</body>
</html>
- 编写控制器
cn.ykf.controller.HelloController
,并且使用注解配置
@Controller
public class HelloController {
@RequestMapping(path = "/hello")
public String hello() {
System.out.println("Hello SpringMVC");
return "success"; // 被视图解析器解析后为 /WEB-INF/pages/success.jsp
}
}
- 在
webapp/WEB-INF/pages
目录下创建success.jsp
,用于展示成功跳转页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功</title>
</head>
<body>
<h3>访问成功</h3>
</body>
</html>
2.2 入门案例执行过程分析
- 当 Tomcat 服务器启动的时候,应用被加载到 Tomcat 容器中。首先读取
web.xml
,因为DispatcherServlet
配置了<load-on-startup>
标签,所以会在服务器一启动就被创建。同时因为DispatcherServlet
指定了 Spring MVC的配置文件springmvc.xml
的位置,所以会读取springmvc.xml
,创建视图解析器InternalResourceViewResolver
和控制器HelloController
对象并加入到 IoC 容器中。(实际上并不止创建了这两个对象) - 当
index.jsp
向服务器发送请求时,由于DispatcherServlet
所映射的路径为<url-pattern>/</url-patttern>
,所以这个请求会先被DispatcherServlet
所拦截到。但是该 Servlet 并不处理请求,而是根据我们请求的 URI 路径/hello
,去匹配@RequestMapping
绑定的路径。 - 根据
@RequestMapping
找到匹配的方法hello()
后,执行并取得返回值success
。借助视图解析器InternalResouceViewResolver
得到最终的结果视图为/WEB-INF/pages/success.jsp
- Tomcat 服务器渲染页面,对客户端浏览器做出响应。
- 以上只是 SpringMVC 的大概执行流程,更具体可以参考以下链接:
2.3 涉及的 SpringMVC 组件
DispatcherServlet
(前端控制器):用户的请求会先达到该控制器,是整个执行流程的控制中心,由它调用其他组件来处理用户的请求,其存在降低了其它组件的耦合性。HandlerMapping
(处理器映射器):负责根据用户的请求找到对应的Handler
即处理器。Handler
(处理器):开发中需要编写的具体业务控制器,也就是Controller
,由它来对具体的用户请求进行处理。HandlerAdapter
(处理器适配器):对Handler
进行执行,是适配器模式的一种应用。ViewResolver
(视图解析器): 负责将处理结果生成View
视图。首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View
视图对象,最后对View
进行渲染将处理结果通过页面展示给用户。View
(视图):展示给用户的具体页面,比如说jsp 页面
- 其中,
HandlerMapping
处理器映射器、HandlerAdapter
处理器适配器、View Resolver
视图解析器称为 SpringMVC 的三大组件。 - 配置文件中的
<mvc:annotation-drivern>
会自动帮我们加载处理器映射器和处理器适配器。
2.4 @RequestMapping 注解
@RequestMapping
用于建立请求 URL 路径和处理请求方法之间的对应关系。- 出现的位置可以在类上或者方法上。
- 当
@RequestMapping
在类上时,相当于是 URL 路径的 一级目录,可以让我们的 URL 按照模块化管理。比如在入门案例的HelloController
类上使用@RequestMapping("/user")
,那么此时我们的请求路径就应该为user/hello
。 - 当
@RequestMapping
在方法上时,如果搭配类上的@RequestMapping
,那么就是 URL 路径的 二级目录;如果单独在方法上时,那么就是一级目录。
- 当
- 属性如下
value/path
:这两个属性互为别名,都是用来指定请求的 URL 路径,methods
:用于指定请求的方式,值为RequestMethod.POST、RequestMethod.POST
等,出现多个值时为或的关系。params
: 用于指定限制请求参数的条件,支持简单的表达式,要求请求参数的key
和value
必须和条件一模一样。譬如:params={"username=ykf","arg"}
表示请求参数中必须要有参数arg
和值为ykf
的username
参数(此时请求参数中这两个参数必须都要存在)。params={"money!=100"}
表示请求参数中不能有值为100
的money
参数(此时请求参数中没有money
也是可以的)。
headers
:用于指定限制请求消息头的条件。- 以上的限制条件属性出现多个时,为与的关系。
- 测试代码如下所示
@Controller
// 定义一级目录
@RequestMapping("/user")
public class UserController{
/**
* 二级目录映射
*
* @return
*/
@RequestMapping("/test1")
public String test1() {
System.out.println("二级目录...");
return "success";
}
/**
* 请求方式的限制,这里限制只能post方式访问
*
* @return
*/
@RequestMapping(value = "/test2", method = {RequestMethod.POST})
public String test2() {
System.out.println("限制post方式访问");
return "success";
}
/**
* 请求参数的限制
*
* @return
*/
// @RequestMapping(value = "/test3", params = {"money!=100"})
// public String test3(String money) {
@RequestMapping(value = "/test3", params = {"username=aa", "arg"})
public String test3(String username, String arg) {
// System.out.println("限制参数" + "----" + money);
System.out.println("限制参数" + "----" + username + "------" + arg);
return "success";
}
/**
* 请求头的限制
*
* @return
*/
@RequestMapping(value = "/test4", headers = {"Accept"})
public String test4() {
System.out.println("限制请求头");
return "success";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>RequestMapping的使用</title>
</head>
<body>
<h3>RequestMapping的使用</h3>
<%-- 访问二级目录 --%>
<a href="user/test1">二级目录</a><hr/>
<%-- Controller限制请求方式为POST,所以超链接方式无法访问(GET) --%>
<a href="user/test2">限制请求方式</a><hr/>
<%-- 限制请求参数,此时去掉任一参数或者修改username的值都无法访问 --%>
<a href="user/test3?username=aa&arg=bb">限制请求参数(1)</a><hr/>
<%-- 如果有money参数,值不能为100,没有的话可以直接访问 --%>
<a href="user/test4?money=100">限制请求参数(2)</a><hr/>
<%-- 限制请求头中必须有 Accept --%>
<a href="user/test5">限制请求头</a><hr/>
</body>
</html>
3. 请求参数的绑定
- 当我们从表单提交数据到后台的时候,请求参数都是基于
key=value
的形式,SprigMVC 可以将请求参数直接绑定到对应方法的参数上。譬如:
<a href="user/deleteUser?uid=1">删除用户</a>
- 该超链接可以向对应的方法发送一个
uid=1
的请求参数,此时我们利用 SpingMVC 就可以这样接收:
/**
* 模拟删除用户
* @return
*/
@RequestMapping("/deleteUser")
public String deleteUser(Integer uid){
System.out.println("删除了 id 为 " + uid+ " 的用户...");
return "success";
}
- 参数绑定支持的数据类型有以下几种:
- 基本数据类型和 String 类型
- POJO 类型,其中可以包含其他 POJO 对象的引用
- 数组和集合类型,譬如
List
、Map
等集合
3.1 基本类型和 String 类型作为参数
- 如果要绑定基本类型或 String 类型的参数,那么要求前端的请求参数名称必须与控制器方法的形参名称保持一致(严格区分大小写),见以下示例:
- jsp代码:
<a href="param/testParam1?id=10001&username=鱼开饭">普通参数的绑定</a>
- 控制器代码:
/**
* 普通参数的绑定
*
* @return
*/
@RequestMapping("/testParam1")
public String testParam1(Integer id, String username) {
System.out.println(id + "----" + username);
return "success";
}
- 运行结果
3.2 POJO 类型作为参数
- 如果要绑定 POJO 类型的参数,那么要求前端的请求参数名称和 POJO 类的属性名称保持一致,同时控制器方法的参数类型为对应的 POJO 类型。特别地,如果 POJO 类中含有其他 POJO 类的引用,那么请求参数名称应该为
外部属性名.内部属性名
,见以下示例: - jsp代码:
<form action="param/testParam2" method="post">
用户名: <input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
金额:<input type="text" name="money"/><br/>
<%-- 外部属性名 user 内部属性名 name,因此请求参数为 user.name --%>
真实姓名:<input type="text" name="user.name"/><br/>
年龄:<input type="text" name="user.age"/><br/>
<input type="submit" value="提交">
</form>
- 控制器代码:
/**
* 绑定到带有其他对象引用的 JavaBean
*
* @param account
* @return
*/
@RequestMapping("/testParam2")
public String testParam3(Account account) {
System.out.println(account);
return "success";
}
- 实体类
Account
类、User
类代码:
// 外部类
public class Account implements Serializable {
private String username;
private String password;
private Double money;
// 其他 JavaBean 对象引用
private User user;
// getters and setters
// ...
}
// 内部类
public class User implements Serializable {
private String name;
private Integer age;
// getters and setters
// ...
}
- 运行结果
3.3 集合类型作为参数
- 如果要绑定集合类型的参数,那么要求集合类型的请求参数必须在 POJO 类中,同时前端的请求参数名称和 POJO 类的集合属性名称保持一致。当给
List
集合中的元素赋值时,使用下标;当给Map
集合中的元素赋值时,使用键值对,见以下示例: - jsp代码:
<form action="param/testParam4" method="post">
用户名: <input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
金额:<input type="text" name="money"/><br/>
<%-- 绑定到 List 集合 --%>
持卡人1名称:<input type="text" name="userList[0].name"/><br/>
持卡人1年龄:<input type="text" name="userList[0].age"/><br/>
持卡人2名称:<input type="text" name="userList[1].name"/><br/>
持卡人2年龄:<input type="text" name="userList[1].age"/><br/>
<%-- 绑定到 Map 集合 --%>
持卡人3名称:<input type="text" name="userMap['one'].name"/><br/>
持卡人3年龄:<input type="text" name="userMap['one'].age"/><br/>
持卡人4名称:<input type="text" name="userMap['two'].name"/><br/>
持卡人4年龄:<input type="text" name="userMap['two'].age"/><br/>
<input type="submit" value="提交">
</form>
- 控制器代码:
/**
* 绑定到带有集合属性的 JavaBean
*
* @param account
* @return
*/
@RequestMapping("/testParam4")
public String testParam4(Account account) {
System.out.println(account);
return "success";
}
Account
类代码:
public class Account implements Serializable {
private String username;
private String password;
private Double money;
// List 集合
private List<User> userList;
// Map 集合
private Map<String, User> userMap;
// getters and setters
// ...
}
- 运行结果
如果直接将参数封装到一个普通 List 集合(不在 JavaBean 中),像这样:
<a href="param/testList?args=test1,test2,test3">普通集合的绑定</a>
@RequestMapping("/testList")
public String testList(List<String> args) {
System.out.println(args);
return "success";
}
那么会报 500 错误,提示 List 没有默认构造函数无法初始化,如图
在其他博客(SpringMVC学习01:请求路径匹配和参数绑定)看到的解决方法是:使用@RequestParam
注解进行参数绑定(关于@RequestParam
的使用请看下文),像这样:
public String testList(@RequestParam("args") List<String> args) {
// ...
}
3.4 请求参数乱码问题
- 对于
GET
方式提交的请求,如果参数中含有中文,那么是不会乱码的,因为 Tomcat 8 已经将乱码问题解决了。如果GET
方式请求乱码,那么可以在 Tomcat 安装目录下的conf\server.xml
中进行修改,如图
- 而对于
POST
方式提交的参数乱码问题,则应该在web.xml
中配置一个 SpringMVC 提供的过滤器CharacterEncodingFilter
,见代码
<web-app>
<!-- 配置编码过滤器 -->
<filter>
<filter-name>characterEncodingFilter</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>
</filter>
<!-- 过滤所有请求 -->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- servlet 配置写在下方 -->
</web-app>
<Filter>
位置最好出现在<servlet>
之前
3.5 自定义类型转换器
- 我们都知道,从表单提交的任何数据类型都是字符串类型。但是当提交到控制器方法时,对于某些参数我们可以用其它的数据类型(
Integer
、Double
等)来接收。譬如:前端提交的请求参数中有id=1
,那么此时我们在控制器方法中可以直接使用public String test(Integer id){}
来接收,这是因为 SpringMVC 有着内置的转换器,可以帮我们把一些常用的数据类型进行转换。 - 可是有的时候会出现这样的情况,前端提交了一个日期字符串,控制器方法使用
Date
类型的变量进行接收,由于日期字符串有很多种格式,所以 SpringMVC 的内置转换器不一定能帮我们转换成Date
类型,譬如: - jsp代码
<a href="param/testStringToDate?date=2020-02-02">字符串转日期</a>
- 控制器方法代码
@RequestMapping("/testStringToDate")
public String testStringToDate(Date date) {
System.out.println(date);
return "success";
}
- 运行报错
- 此时我们就可以根据自己的需求来定义自定义的类型转换器,要求实现
Converter<S,T>
接口,其中S
为待转换的类型,T
为目标类型。
/**
* 自定义日期转换器
*
* @author yukaifan
* @ClassName StringToDateConverter
* @date 2020-02-29 9:35
*/
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
// 检查待转换字符串是否为空
if (source == null || "".equals(source.trim())) {
throw new RuntimeException("待转换日期字符串不能为空!");
}
// 解析字符串
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("日期格式有误,必须为 yyyy-MM-dd 格式!");
}
}
}
- 最后我们需要在 SpringMVC 的配置文件中配置该类型转换器
<!-- 引用配置的类型转换服务 -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<!-- 配置类型转换器 -->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!-- 给工厂注入一个新的类型转换器 -->
<property name="converters">
<set>
<!-- 配置自定义类型转换器 -->
<bean class="cn.ykf.converter.StringToDateConverter"></bean>
</set>
</property>
</bean>
3.6 使用 Servlet 原生 API 作为参数
-
SpringMVC 支持使用原始 Servlet API 对象作为方法参数,也就是说,我们可以把这些参数直接写在控制器方法的参数列表上,这些对象如下表所示:
全限定类名 javax.servlet.http.HttpServletRequest
javax.servlet.http.HttpServletResponse
javax.servlet.http.HttpSession
java.security.Principal
java.util.Locale
java.io.InputStream
java.io.OutputStream
java.io.Reader
java.io.Writer
-
示例代码
@RequestMapping("/testServlet")
public String testServlet(HttpServletRequest request, HttpSession session, HttpServletResponse response) {
// ...
}
4. 常用注解
4.1 @RequestParam
- 该注解的作用是把请求中指定名称的参数绑定到控制器方法的某个参数上。常用属性如下:
value / name
: 这两个属性互为别名,都是指定请求参数中的名称required
: 请求参数中是否必须提供此参数,默认值为true
,表示必须提供,否则报错
- jsp代码
<a href="${pageContext.request.contextPath}/anno/testRequestParam?name=鱼开饭">RequestParam</a>
- 关于 EL 表达式
${pageContext.request.contextPath}
失效的原因:可能是因为web.xml
的版本为 2.4 以下,2.4 以下版本isELIgnored
默认值为true
,可以采用以下方法:
- 在 jsp 页面
page
指令中开启 EL 表达式 :<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
- 直接修改
web.xml
版本为 2.4 以上,快捷省事 :<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
- 控制器代码
@Controller
@RequestMapping("/anno")
public class AnnotationController {
/**
* RequestParam 的使用
*
* @param username
* @return
*/
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam("name") String username) {
System.out.println(username);
return "success";
}
}
此时使用
@RequestParam("name")
,那么如果请求参数中没有name
参数,程序就会报错。同时,由于已经使用@RequestParam
进行参数的绑定,所以控制器方法的参数名称username
已经无法绑定了,也就是说,即使请求参数中有参数username
,那么也不会绑定到控制器方法的username
参数上了。
4.2 @RequestBody
- 该注解的作用是获取请求体内容,得到的内容为
key1=value1&key2=value2...
,只适用 POST 方式提交的请求,因为 GET 方式无请求体。属性如下:required
: 是否必须有请求体,默认值为true
,表示必须有请求体,GET 方式请求会报错;当为false
时,GET 方式请求得到的为null
- jsp代码
<form action="${pageContext.request.contextPath}/anno/testRequestBody" method="post">
用户名 : <input type="text" name="username"/><br/>
年龄 : <input type="text" name="age"/><br/>
<input type="submit" value="提交"/>
</form>
- 控制器代码
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body) throws UnsupportedEncodingException {
System.out.println(body);
// 中文乱码
System.out.println(URLDecoder.decode(body, "UTF-8"));
return "success";
}
4.3 @PathVariable
- 该注解的作用是将 URL 中的占位符绑定到控制器方法的参数上,例如
localhost:8080/springmvc/user/{uid}
,这里的{uid}
就是一个占位符。属性如下:value
:指定 URL 中的占位符名称required
: 是否必须提供占位符
- jsp代码
<a href="${pageContext.request.contextPath}/anno/testPathVariable/10001">PathVariable</a>
- 控制器代码
@RequestMapping("/testPathVariable/{uid}")
public String testPathVariable(@PathVariable("uid") String id) {
System.out.println(id);
return "success";
}
该注解可以用于绑定 REST 风格 URL 的占位符,restful 有以下特点:
- 资源(Resources):每一个 URI 对应着一个资源
- 表现层(Representation):把资源具体呈现出来的形式
- 状态转化(State Transfer):客户端使用 GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源,譬如:
- /account/1 HTTP GET : 得到 id = 1 的 account
- /account/1 HTTP DELETE: 删除 id = 1 的 account
- /account/1 HTTP PUT: 更新 id = 1 的 account
- /account HTTP POST: 新增 account
4.4 @RequestHeader 和 @CookieValue
@RequestHeader
用于获取请求消息头。属性如下:value
: 要获取的消息头名称required
: 是否必须有此消息头
@CookieValue
用于获取 Cookie 的值。属性如下:value
: 要获取的 Cookie名称required
: 是否必须有此 Cookie
- jsp代码
<%-- RequestHeader --%>
<a href="${pageContext.request.contextPath}/anno/testRequestHeader">RequestHeader</a><hr />
<%-- CookieValue --%>
<a href="${pageContext.request.contextPath}/anno/testCookieValue">CookieValue</a><hr />
- 控制器代码
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader("Accept") String header){
System.out.println(header);
return "success";
}
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String cookieValue){
System.out.println(cookieValue);
return "success";
}
4.5 @ModelAttribute
- 该注解用于修饰方法或者参数,如果修饰方法,那么该方法(有无返回值均可)会在控制器方法之前执行;如果修饰参数,可以获取指定的数据并赋值给参数。当表单提交的数据不是完整的实体类数据时,我们就可以借助该注解,对空属性进行预处理(譬如赋值为数据库记录中该对象原本的值)。属性如下:
value
: 用于获取数据的key
,key
可以是 POJO 类的属性名称,也可以是 Map 集合的key
- jsp 代码
<%-- ModelAttribute --%>
<form action="${pageContext.request.contextPath}/anno/testModelAttribute">
姓名 : <input type="text" name="name"/><br/>
年龄 : <input type="text" name="age"/><br/>
<input type="submit" value="提交"/>
</form><hr />
- 控制器代码(
@ModelAttribute
修饰的方法带返回值)
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println(user);
return "success";
}
/**
* 带返回值,对提交数据进行预处理
*
* @param name
* @return
*/
@ModelAttribute
public User initUser(String name) {
// 模拟根据姓名从数据库查出对应的 User 对象
User user = new User();
user.setName(name);
user.setAge(10);
// 待会 testModelAttribute 获取到的 User 对象就带有 Date 属性
user.setDate(new Date());
return user;
}
控制器代码(@ModelAttribute
修饰的方法不带返回值)
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("one") User user) {
System.out.println(user);
return "success";
}
/**
* 不带返回值,将数据放入 Map 集合中
*
* @param name
* @param map
*/
@ModelAttribute
public void initUser(String name, Map<String, User> map) {
// 模拟根据姓名从数据库查出对应的 User 对象
User user = new User();
user.setName(name);
user.setAge(10);
user.setDate(new Date());
map.put("one", user);
}
4.6 @SessionAttributes
- 该注解可以将数据存入 Session 域,使得数据在多次执行控制器方法的时候共享,只能作用在类上。属性如下:
value
: 用于指定存入的属性名称type
: 用于指定存入的数据类型
- jsp代码
<%-- SessionAttributes --%>
<a href="${pageContext.request.contextPath}/anno/testPut">存入数据到 SessionAttribute</a>
<a href="${pageContext.request.contextPath}/anno/testGet">从 SessionAttribute 取出数据</a>
<a href="${pageContext.request.contextPath}/anno/testRemove">从 SessionAttribute 移除数据</a>
- 控制器代码
@Controller
@RequestMapping("/anno")
// 指定要存入 session 中的数据
@SessionAttributes({"msg"})
public class AnnotationController {
/**
* 存数据到 session
*
* @param model
* @return
*/
@RequestMapping("/testPut")
public String testPut(Model model) {
model.addAttribute("msg", "存入的数据");
return "success";
}
/**
* 从 session 中取数据
*
* @param model
* @return
*/
@RequestMapping("/testGet")
public String testGet(ModelMap model) {
System.out.println(model.get("msg"));
return "success";
}
/**
* 从 session 中移除数据
*
* @param status
* @return
*/
@RequestMapping("/testRemove")
public String testRemove(SessionStatus status) {
status.setComplete();
return "success";
}
}