idea的快捷键
查找某个类crtl+shift+n
查找该类的继承关系ctrl+h
1. SpringMVC简介
1.1 MVC
该博文的总结通过
【尚硅谷】SpringMVC 2021新版教程丨一套快速上手spring mvc
总结的博文大致如下
加上了自已的理解以及对某一部分知识的补充以及完善
所谓的mvc是软件架构的思想按照照模型、视图、控制器划分
- 模型指代JavaBean,作用是处理数据
实体类Bean:存储业务数据的
业务处理 Bean:业务逻辑(service类)和数据访问(dao类)
- 视图层:页面中与用户进行交互
- 控制层:接收请求和响应浏览器(servlet)
主要的工作流程:
通过视图层(html或者jsp界面)信息交互进行发送请求到服务器,在服务器中请求被控制层接收,之后调用相应的模型层处理请求,处理结果返回控制层,控制层根据结果返回到相应的视图,渲染数据后最终响应给浏览器
1.2 SpringMVC
- springmvc是基于spring的一个框架,是spring的一个部件,做web的一个框架
- web底层是servlet,基于此加了一些功能,可以理解成servlet的一个升级->springmvc框架
- 创建对象放入容器中,容器中放置的对象是注解@controller
使用@controller创建控制器对象(普通类的对象),把对象放入到springmvc容器中,把创建的对象作为接受用户的需求,显示处理结果,相当于servlet(继承http)但它不是servlet,只不过功能相似
jsp请求给中央调度器,再把请求转发分配给controller对象(可以多个)
主要的特点是:
Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
2. 入门案例
创建maven工程,默认jar包,要弄成web工程需要转换为web包
2.1 引入依赖包
添加适应的依赖包
- springmvc的依赖包包括spring下的aop、beans、context等基础框架,导入servletAPI依赖包
- 设置的scope范围为provided(如果服务器已提供),如果tomcat有,就不会再将其打包到项目的lib中
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
以上添加的Thymeleaf依赖包
详情可看我这篇文章的知识点
SpringBoot集成Thymeleaf从入门到精通(全)
主要是通过试图控制控制页面的内容
2.2 配置xml文件
xml文件的配置,在module模块中也要选对好路径
一般有默认配置的方式和扩展配置的方式
==默认配置方式==
配置文件默认位于WEB-INF 下,默认名称为`-
servlet.xml`
/
主要是为了jsp不能匹配,本质是servlet,不需要dispatcherservlet做拦截处理
主要是因为匹配了jsp的话,会返回jsp页面,jsp需要请求处理才能返回
/*
是可以匹配所有文件
设置springMVC的核心控制器所能处理的请求的请求路径
- /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
- 但是/不能匹配.jsp请求路径的请求
之所以/不能匹配jsp页面,jsp的本质本身是一个servlet,所以不需要通过DispatcherServlet进行匹配,如果强行要用DispatcherServlet进行匹配jsp页面,当成一个普通的处理,最后jsp页面就不会被显示到(因为jsp页面是需要处理才可以被返回,而如果直接匹配的话,会直接返回jsp页面)
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
==扩展配置方式==
添加一个初始化参数,可以配置配置文件的位置和名称
- 将控制器dispatcherservlet的初始化时间提前到服务器启动的时间。参数名已经在前端中定制好了,是
contextConfigLocation
- 位置和名称是classpath路径名,定义springMVC.xml,这个配置文件配置在resources文件夹下面,如果不配置这些东西,配置文件的xml就在webinf下面
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的 src/main/resources -->
<param-value>classpath:springMVC.xml</param-value>
</init-param>
- servlet在访问的时候初始化,而很多东西都在第一次的时候初始化,影响第一次访问的速度,为了防止这种情况可以将前端控制器的dispatcherservlet的初始化时间提前到服务器启动时
在中间添加一些代码
<!--作为框架的核心组件,在启动过程中有大量的初始化操作要做 而这些操作
放在第一次请求时才执行会严重影响访问速度 因此需要通过此标签将启动控制
DispatcherServlet的初始化时间提前到服务器启动时 -->
<load-on-startup>1</load-on-startup>
2.3 创建请求控制器
- 由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器
- 可以通过注解或者是bean标签进行。通过@Controller注解将其标识为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在
@Controller
public class HelloController {
}
这些注解如果标红,详情可看这篇文章
springmvc中注解标红的解决方法(万能)
2.4 springMVC配置文件
- 扫描组件
<context:component-scan base-package="com.atguigu.mvc.controller"></context:component-scan>
- 视图解析器,主要为了页面的跳转
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
解释以上的配置文件
- order视图解析器的优先级,数字越小越优先
- 代码中设置了内部bean,Thymeleaf解析的时候必须要设置前缀和后缀,才能跳转,templateMode是模板,符合条件的时候,才会跳转
具体跳转的界面为html文件
文件路径在上面的前缀和后缀中
以下主要是Thymeleaf的命名空间,主要格式是
xmlns:th="http://www.thymeleaf.org"
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/target}">访问目标页面target.html</a>
</body>
</html>
2.5 测试界面
@RequestMapping("/")
请求映射
将当前的请求控制器映射方法,通过该注解,实现该注解中的方法
通过请求路径或者请求参数返回,value属性赋值的时候value可以不写
在方法中返回的结果只要是index就可以,因为后缀名html已经有了
@Controller
public class HelloController {
// 通过@RequestMapping注解,可以通过请求路径匹配要处理的具体的请求
// /表示的当前工程的上下文路径
@RequestMapping("/")
public String index(){
return "index";
}
}
/
通过解析前缀,再加上index之后加上后缀就会识别
2.6 总结
- 浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理(在web.xml文件配置中)。
- 前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器(@controller),将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的,控制器方法就是处理请求的方法。
- 处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面
==具体代码步骤的逻辑思路如下==
3. @RequestMapping注解
@RequestMapping
注解的作用就是将请求和处理请求的控制器方法关联
起来,建立映射关系
@RequestMapping
标识一个类:设置映射请求的请求路径的初始信息@RequestMapping
标识一个方法:设置映射请求请求路径的具体信息
主要用在不同模块中的同一个名称,防止名称混乱
@RequestMapping("/testBean")
public String testBean(User user){
System.out.println(user);
return "success";
3.1 value属性
- value属性通过请求的请求地址匹配请求映射,必须设置,至少通过请求地址匹配请求映射
- 一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
@RequestMapping(
value = {"/testRequestMapping", "/test"},
)
public String success(){
return "success";
}
在xml配置文件中这样设置
<a th:href="@{/testRequestMapping}">测试RequestMapping注解的value属性-->/testRequestMapping</a><br>
<a th:href="@{/test}">测试RequestMapping注解的value属性-->/test</a><br>
3.2 method属性
- 通过请求的请求方式(get或post)匹配请求映射
- RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求
若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method 'POST' not supported
@RequestMapping(
value = {"/testRequestMapping", "/test"},
method = {RequestMethod.GET, RequestMethod.POST}
)
在xml配置文件中这样设置
<a th:href="@{/test}">测试RequestMapping注解的method属性-->GET</a><br>
<form th:action="@{/test}" method="post">
<input type="submit" value="测试RequestMapping注解的method属性-->POST">
</form>
SpringMVC中提供了@RequestMapping
的派生注解
- 处理get请求的映射-->
@GetMapping
- 处理post请求的映射-->
@PostMapping
- 处理put请求的映射-->
@PutMapping
- 处理delete请求的映射-->
@DeleteMapping
常用的请求方式有get,post,put,delete
具体这部分内容可查看我在springboot中写过的restful的内容
springboot从入门到精通(全)
@GetMapping("/testGetMapping")
public String testGetMapping(){
return "success";
}
@RequestMapping(value = "/testPut", method = RequestMethod.PUT)
public String testPut(){
return "success";
}
xml文件配置
<a th:href="@{/testGetMapping}">测试GetMapping注解-->/testGetMapping</a><br>
<form th:action="@{/testPut}" method="put">
<input type="submit" value="测试form表单是否能够发送put或delete请求方式">
</form>
3.3 params属性(了解)
==这个配置都要满足才可以,上面两个参数属性只要满足其中一个就可以==
一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
具体参数结构有以下四种
"param"
:要求请求映射所匹配的请求必须携带param请求参数"!param"
:要求请求映射所匹配的请求必须不能携带param请求参数"param=value"
:要求请求映射所匹配的请求必须携带param请求参数且param=value"param!=value"
:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
@RequestMapping(
value = "/testParamsAndHeaders",
params = {"username","password!=123456"}
)
public String testParamsAndHeaders(){
return "success";
}
xml文件配置
<a th:href="@{/testParamsAndHeaders(username='admin',password=123)}">测试RequestMapping注解的params属性-->/testParamsAndHeaders</a><br
3.4 headers属性(了解)
通过请求的请求头信息匹配请求映射,字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
"header"
:要求请求映射所匹配的请求必须携带header请求头信息"!header"
:要求请求映射所匹配的请求必须不能携带header请求头信息"header=value"
:要求请求映射所匹配的请求必须携带header请求头信息且header=value"header!=value"
:要求请求映射所匹配的请求必须携带header请求头信息且header!=value
@RequestMapping(
value = "/testParamsAndHeaders",
params = {"username","password!=123456"},
headers = {"Host=localhost:8080"}
)
public String testParamsAndHeaders(){
return "success";
}
xml文件配置
<a th:href="@{/testParamsAndHeaders(username='admin',password=123)}">测试RequestMapping注解的params属性-->/testParamsAndHeaders</a><br
3.5 ant风格的路径
?
:表示任意的单个字符(不可是?
或者是/
)*
:表示任意的0个或多个字符(不可是?
或者是/
)**
:表示任意的一层或多层目录(a**a
,前后一定都是a
在加/
)
注意:在使用时,只能使用//xxx的方式
//@RequestMapping("/a?a/testAnt")
//@RequestMapping("/a*a/testAnt")
@RequestMapping("/**/testAnt")
public String testAnt(){
return "success";
}
xml文件配置
<a th:href="@{/a1a/testAnt}">测试@RequestMapping可以匹配ant风格的路径-->使用?</a><br>
<a th:href="@{/a1a/testAnt}">测试@RequestMapping可以匹配ant风格的路径-->使用*</a><br>
<a th:href="@{/a1a/testAnt}">测试@RequestMapping可以匹配ant风格的路径-->使用**</a><br>
3.6 路径中的占位符(重点)
原始方式:/deleteUser?id=1
restful方式:/deleteUser/1
这个restful在springboot的框架也有提到,我在上文中也提到了该链接
再次附上链接
具体这部分内容可查看我在springboot中写过的restful的内容
springboot从入门到精通(全)
而且使用restful传入参数的时候可以使用问号或者使用括号两种方式
- 第一种:
th:href="@{/testParamsAndHeaders(username='admin',password=123)}"
- 第二种:
th:href="@{/testParamsAndHeaders?username='admin'&password=123}"
SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping
注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable
注解,将占位符所表示的数据赋值给控制器方法的形参
传入的参数都是在后面加上{名字任意}
之后在代码中通过@PathVariable
注解,将其占位符的数据传入,用的相对应的数据
@RequestMapping("/testPath/{id}/{username}")
public String testPath(@PathVariable("id")Integer id, @PathVariable("username") String username){
System.out.println("id:"+id+",username:"+username);
return "success";
}
xml文件配置
<a th:href="@{/testPath/1/admin}">测试@RequestMapping支持路径中的占位符-->/testPath</a><br>
4. 获取请求参数
通过上文中的路径传入参数获取了请求,之后如何获取参数可继续往下查看
4.1 原生ServletAPI
获取了url请求之后,要获取得到的参数,使用原生的servlet的该如何进行传参
java代码
通过HttpServletRequest
类,将其实参赋值给形参,之后使用其类下的参数进行获取参数getParameter
@RequestMapping("/testServletAPI")
//形参位置的request表示当前请求
public String testServletAPI(HttpServletRequest request){
HttpSession session = request.getSession();
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
return "success";
}
xml 文件配置
<a th:href="@{/testServletAPI(username='admin',password=123456)}">测试使用servletAPI获取请求参数</a><br>
4.2 控制器方法的形参获取请求参数
前端控制器,请求和控制器匹配,之后通过DispatcherServlet
- 在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参
因为springmvc有自已的servlet,而且本身会自动调用,原生的可能会比较麻烦,所以使用springmvc中的DispatcherServlet
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}
xml文件配置
<a th:href="@{/testParam(username='admin',password=123456)}">测试使用控制器的形参获取请求参数</a><br>
如果有同名可以通过字符串或者数组进行输出
- 若使用字符串数组类型的形参,此参数的数组中包含了每一个数据,输出的时候使用
Arrays.toString(hobby)
- 若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果,输出的时候直接使用变量名即可。
@RequestMapping("/testParam")
public String testParam(
String username,
String password,
String[] hobby
//若请求参数中出现多个同名的请求参数,可以再控制器方法的形参位置设置字符串类型或字符串数组接收此请求参数
//若使用字符串类型的形参,最终结果为请求参数的每一个值之间使用逗号进行拼接
System.out.println("username:"+username+",password:"+password+",hobby:"+ Arrays.toString(hobby));
return "success";
}
xml文件配置
<form th:action="@{/testParam}" method="get">
用户名:<input type="text" name="user_name"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobby" value="a">a
<input type="checkbox" name="hobby" value="b">b
<input type="checkbox" name="hobby" value="c">c<br>
<input type="submit" value="测试使用控制器的形参获取请求参数">
</form>
4.3 @RequestParam / @RequestHeader / @CookieValue
==@RequestParam==
将请求参数和控制器方法的形参创建映射关系
一共有三个参数:
value
:指定为形参赋值的请求参数的参数名required
:设置是否必须传输此请求参数,默认值为true,若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为nulldefaultValue
:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""
时,则使用默认值为形参赋值
==@RequestHeader==将请求头信息和控制器方法的形参创建映射关系,参数也是有三个,用法同@RequestParam
==@CookieValue==将cookie数据和控制器方法的形参创建映射关系,参数也是有三个,用法同@RequestParam
- 要想获取session的cookie必须使用这个函数
getSession()
,且只能在原生servlet中使用 @RequestParam(value = "user_name") String username
相当于取了一个别名,前端代码的别名是user_name,将其代码更改为username,之后再调用这参数
@RequestMapping("/testServletAPI")
//形参位置的request表示当前请求
public String testServletAPI(HttpServletRequest request){
HttpSession session = request.getSession();
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
return "success";
}
@RequestMapping("/testParam")
public String testParam(
@RequestParam(value = "user_name", required = false, defaultValue = "hehe") String username,
String password,
String[] hobby,
@RequestHeader(value = "sayHaha", required = true, defaultValue = "haha") String host,
@CookieValue("JSESSIONID") String JSESSIONID){
//若请求参数中出现多个同名的请求参数,可以再控制器方法的形参位置设置字符串类型或字符串数组接收此请求参数
//若使用字符串类型的形参,最终结果为请求参数的每一个值之间使用逗号进行拼接
System.out.println("username:"+username+",password:"+password+",hobby:"+ Arrays.toString(hobby));
System.out.println("host:"+host);
System.out.println("JSESSIONID:"+JSESSIONID);
return "success";
}
xml文件配置
<form th:action="@{/testParam}" method="get">
用户名:<input type="text" name="user_name"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobby" value="a">a
<input type="checkbox" name="hobby" value="b">b
<input type="checkbox" name="hobby" value="c">c<br>
<input type="submit" value="测试使用控制器的形参获取请求参数">
</form>
4.4 通过实体类获取参数
可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
实体类
实体类的变量定义要和后台的数据也相同
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String sex;
private String email;
public User() {
}
public User(Integer id, String username, String password, Integer age, String sex, String email) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.sex = sex;
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
之后通过控制层面的代码进行获取
将其传入实体类对象
@RequestMapping("/testBean")
public String testBean(User user){
System.out.println(user);
return "success";
}
xml文件的配置要和实体类的变量一一对应
</form>
<form th:action="@{/testBean}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男">男<input type="radio" name="sex" value="女">女<br>
年龄:<input type="text" name="age"><br>
邮箱:<input type="text" name="email"><br>
<input type="submit" value="使用实体类接收请求参数">
</form>
最后输出的结果可能会有乱码
如果请求时get,乱码的修改方式通过tomcat的配置文件中的server.xml
具体如下
将其配置文件添加如下代码
修改为
<Connector port="8080" URIEncoding="UTF-8" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
如果请求是post,修改乱码的方式为
服务器启动时去配置,因为servlet最快配置,所以要在这之前进行
具体优先顺序为:监听器,过滤器,servlet
所以我们添加一个过滤器的修改
具体乱码的一个过滤器是这个类CharacterEncodingFilter
所有的请求都可能是post请求,所以设置的路径为/*
在web.xml配置文件中配置过滤器
<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>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
具体这么设置,通过查看其源码
在源码中本身设置的encoding为null,所以需要配置一个UTF-8
本身在源码的实现类中的实现方法中
如果为null就会不执行,所以我们需要设置一个UTF-8的编码方式
再者也不需要修改强制请求的编码,只需要修改强制接收的编码请求为true即可
==SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效==
5. 域对象共享数据
共享数据有三种方式
将请求参数作为参数,调用servlet数据,之后调用dao层的后台数据库作为交互,之后在返回控制层
基本的流程,和之前一样,都是建立框架
- 在pom.xml中添加依赖文件
- 在web.xml中配置编码过滤器以及前端控制器DispatcherServlet
- 在springmvc.xml中添加组件扫描以及视图解析器
之后的控制器代码如下所示
5.1 使用ServletAPI向request域对象共享数据
使用原生servletapi进行获取数据层面
通过一个页面转发获取得到另外一个页面的数据
//转发数据获取得到的页面
<p th:text="${testRequestScope}"></p>
//主页面的跳转
<a th:href="@{/testRequestByServletAPI}">通过servletAPI向request域对象共享数据</a><br>
控制层面的代码
//使用servletAPI向request域对象共享数据
@RequestMapping("/testRequestByServletAPI")
public String testRequestByServletAPI(HttpServletRequest request){
request.setAttribute("testRequestScope", "hello,servletAPI");
return "success";
}
5.2 使用ModelAndView向request域对象共享数据
ModelAndView有Model和View的功能
Model主要用于向请求域共享数据
- View主要用于设置视图,实现页面跳转
该类的了解可通过我之前的博文进行学习
SpringMVC之ModelAndView类详细分析(全)
使用springmvc自带的类来获取
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView mav = new ModelAndView();
//处理模型数据,即向请求域request共享数据
mav.addObject("testRequestScope", "hello,ModelAndView");
//设置视图名称
mav.setViewName("success");
return mav;
}
5.3 使用Model向request域对象共享数据
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("testRequestScope", "hello,model");
System.out.println(model);
return "success";
}
5.4 使用map向request域对象共享数据
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
map.put("testRequestScope", "hello,map");
System.out.println(map);
return "success";
}
5.5 使用ModelMap向request域对象共享数据
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testRequestScope", "hello,ModelMap");
System.out.println(modelMap);
return "success";
}
5.6 总结
- Model、ModelMap、Map的关系
参数其实本质上都是 BindingAwareModelMap
类型的
查看其类中的实现,基本都是BindingAwareModelMap
实现
具体执行方法如下
而且通过输出每一个类的真正执行类都是输出的结果
说明改写了tostring的方法
通过显示其正式的执行类model.getClass().getName()
,结果都是BindingAwareModelMap
- 页面显示的界面代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/testRequestByServletAPI}">通过servletAPI向request域对象共享数据</a><br>
<a th:href="@{/testModelAndView}">通过ModelAndView向request域对象共享数据</a><br>
<a th:href="@{/testModel}">通过Model向request域对象共享数据</a><br>
<a th:href="@{/testMap}">通过map向request域对象共享数据</a><br>
<a th:href="@{/testModelMap}">通过ModelMap向request域对象共享数据</a><br>
</body>
</html>
正式跳转的界面代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
success<br>
<p th:text="${testRequestScope}"></p>
</body>
</html>
5.7 向session域共享数据
在了解这一部分的内容时
可了解session数据的钝化与活化
具体可参考这篇文章
Session 的钝化与活化
登录的时候才会使用到session数据,保存用户的登陆状态,也就是持久化配置(关闭服务器的时候还有反应)
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "success";
}
5.8 向application域共享数据
配置比较大的数据才会使用application
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("testApplicationScope", "hello,application");
return "success";
}
以上两个页面的显示都在这
<a th:href="@{/testSession}">通过servletAPI向session域对象共享数据</a><br>
<a th:href="@{/testApplication}">通过servletAPI向application域对象共享数据</a><br>
第二个跳转的页面显示
<p th:text="${session.testSessionScope}"></p>
<p th:text="${application.testApplicationScope}"></p>
==总结==
传入model视图
都是通过这个方法进行获取
6. SpringMVC的视图
- SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户
- SpringMVC视图的种类很多,默认有转发视图和重定向视图
- 当工程引入jstl的依赖,转发视图会自动转换为JstlView
- 若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView
具体获取视图的源码都是通过
6.1 ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转
@RequestMapping("/testThymeleafView")
public String testThymeleafView(){
return "success";
}
类似这种没有加前缀后缀的都是使用的ThymeleafView来进行解析的
6.2 转发视图
SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况:
当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
例如"forward:/","forward:/employee"
测试代码
不可以使用全路径,使用全路径还不如直接去掉forward,而且之后又是ThymeleafView来解析
@RequestMapping("/testForward")
public String testForward(){
return "forward:/testThymeleafView";
}
6.3 重定向视图
SpringMVC中默认的重定向视图是RedirectView
当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转
重定向不能访问WEB-INF下的资源,但是这里将重定向作为一次请求,请求匹配到的方法是通过转发访问来进行
例如"redirect:/","redirect:/employee"
测试代码
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/testThymeleafView";
}
基于以上转发和重定向的区别异同等
可看我之前的文章
转发和重定向的区别及使用方法(全)
6.4 视图控制器view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用viewcontroller标签进行表示
==当前的请求映射所对应的控制器中没有其他方法的时候可以使用这种==
在springmvc.xml中配置
- path:设置处理的请求地址
- view-name:设置请求地址所对应的视图名称
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
控制器中所有的请求映射不保证失效的话要添加如下配置
在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签
<!--开启mvc的注解驱动-->
<mvc:annotation-driven />
6.5 InternalResourceViewResolver
==如果使用的jsp页面,链接可以不用Thymeleaf的格式,但是路径不能使用相对路径,因为超链接使用的是绝对路径来获取,但是如果写了绝对路径,封锁住了上下文,路径就不是很灵活。为此引入了动态获取的方式==
- jsp的域对象
pageContext
,获取上下文的路径request.contextPath
- 如果改动了页面,jsp是可以自动更新部署的
主要区别在于jsp的页面显示
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a href="${pageContext.request.contextPath}/success">success.jsp</a>
</body>
</html>
在其springmvc.xml中不用thymeleaf配置,只需要配置其InternalResourceViewResolver
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/templates/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
7. RESTFul
主要的链接可看我之前的文章
RESTFul从入门到精通超全解析(全)
8. HttpMessageConverter
HttpMessageConverter(报文信息转换器)
- 将请求报文转换为Java对象
- 将Java对象转换为响应报
文
HttpMessageConverter提供了两个注解和两个类型:
- @RequestBody,RequestEntity
- @ResponseBody,ResponseEntity
8.1 @RequestBody
@RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值
设置一个主页面的跳转信息
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
主页面的index的信息为
只有post才有请求头,所以要将method设置为post请求
通过在表格中加入一些信息,点击提交会跳转到某个页面
<form th:action="@{/testRequestBody}" method="post">
<input type="text" name="username">
<input type="text" name="password">
<input type="submit" value="测试@RequestBody">
</form>
这个页面的主要逻辑功能为
在终端中输出在表单中填写的具体信息
而且返回通过点击按钮,返回的是一个success页面信息
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
8.2 RequestEntity
RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()
获取请求头信息,通过getBody()
获取请求体信息
具体控制层面的代码信息为
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
//当前requestEnity表示整个请求报文的信息
System.out.println("请求头:"+requestEntity.getHeaders());
System.out.println("请求体:"+requestEntity.getBody());
return "success";
}
<form th:action="@{/testRequestEntity}" method="post">
<input type="text" name="username">
<input type="text" name="password">
<input type="submit" value="测试RequestEntity">
</form>
8.3 Response
8.3.1 通过原生的HttpServletResponse
@RequestMapping("/testResponse")
public void testResponse(HttpServletResponse response) throws IOException {
response.getWriter().print("hello,response");
}
其页面信息为
<a th:href="@{/testResponse}">通过servletAPI的response对象响应浏览器数据</a><br>
8.3.2 @ResponseBody
@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
当成是一个响应体返回数据而不是视图名称
控制层面的代码为
@RequestMapping(value = "/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "成功";
}
其页面信息为
<a th:href="@{/testResponseBody}">通过@ResponseBody响应浏览器数据</a><br>
8.3.3 Springmvc处理json
如果要返回一个java对象必须使用json格式来传输
因为是java对象所以会有一个对象类
类似与下面这种
响应报文和请求报文返回规定数据,所以返回的时候不能直接返回一个==对象==,所以需要转换一个json(数据交互方式)
加入json
需要添加适应的依赖包即可
添加在pom.xml文件下
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
- json有两种数据格式,一种是对象(键值对),一种数组
具体的逻辑思路大致如下
- 添加依赖包
- 在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:
MappingJackson2HttpMessageConverter
,可以将响应到浏览器的==Java对象转换为Json格式的字符串==
这个添加在springmvc.xml之下
<mvc:annotation-driven />
- 在处理器方法上使用@ResponseBody注解进行标识,将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
return new User(1001, "admin", "123456", 23, "男");
}
具体的页面显示信息如下
<a th:href="@{/testResponseUser}">通过@ResponseBody响应浏览器User对象</a><br>
8.3.4 Springmvc处理ajax
这部分的知识可看我之前的文章
Ajax从入门到精通(全)
通过点击事件联合vue界面
具体代码格式如下
<div id="app">
<a @click="testAxios" th:href="@{/testAxios}">SpringMVC处理ajax</a>
</div>
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript">
new Vue({
el:"#app",
methods:{
testAxios:function (event) {
axios({
method:"post",
url:event.target.href,
params:{
username:"admin",
password:"123456"
}
}).then(function (response) {
alert(response.data);
});
event.preventDefault();
}
}
});
</script>
控制层面的代码如下
@RequestMapping("/testAxios")
@ResponseBody
public String testAxios(String username, String password){
System.out.println(username+","+password);
return "hello,axios";
}
8.3.5 @RestController
@RestController
注解是springMVC提供的一个复合注解,标识在控制器的类上
就相当于为类添加了@Controller
注解,并且为其中的每个方法添加了@ResponseBody
注解
@RestController
=@Controller
+@ResponseBody
8.4 ResponseEntity
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
9. 文件上传和下载
此处涉及文件的输入输出流的一些具体知识可看我之前的文章
9.1 文件下载
在springmvc.xml文件中配置
<mvc:view-controller path="/file" view-name="file"></mvc:view-controller>
使用ResponseEntity实现下载文件的功能
@Controller
public class FileUpAndDownController {
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
System.out.println(realPath);
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
}
"Content-Disposition"
, 默认的下载功能"attachment
;表示附件的意思,filename=1.jpg"
下载完成之后的命名方式
具体的主要信息是响应头,响应体,响应状态码,分别一一对应这三个bytes, headers, statusCode
具体的页面信息为
<a th:href="@{/testDown}">下载1.jpg</a><br>
9.2 文件上传
添加文件上传的依赖包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
在springmvc中还要添加如下配置
一定要配置id属性
class是类路径名
<!--配置文件上传解析器,将上传的文件封装为MultipartFile-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
具体的代码格式为
@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
//获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
//获取上传的文件的后缀名
String suffixName = fileName.substring(fileName.lastIndexOf("."));
//将UUID作为文件名
String uuid = UUID.randomUUID().toString().replaceAll("-","");
//将uuid和后缀名拼接后的结果作为最终的文件名
fileName = uuid + suffixName;
//通过ServletContext获取服务器中photo目录的路径
ServletContext servletContext = session.getServletContext();
//放置到文件的路径名中
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
//判断photoPath所对应路径是否存在
if(!file.exists()){
//若不存在,则创建目录
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
//上传文件
photo.transferTo(new File(finalPath));
return "success";
}
具体的页面信息为
<form th:action="@{/testUp}" method="post" enctype="multipart/form-data">
头像:<input type="file" name="photo"><br>
<input type="submit" value="上传">
</form>
10. 拦截器
- SpringMVC中的拦截器用于拦截控制器方法的执行
- SpringMVC中的拦截器需要实现
HandlerInterceptor
- SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
主要的区别在于拦截器中
==第一种配置==
<!--配置拦截器-->
<mvc:interceptors>
<ref bean="firstInterceptor"></ref>
</mvc:interceptors>
==第二种配置==
<!--配置拦截器-->
<mvc:interceptors>
<ref bean="secondInterceptor"></ref>
</mvc:interceptors>
以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截
默认对所有的方式进行拦截
第三种配置是指定规则进行排除拦截
==第三种配置==
<!--配置拦截器-->
<mvc:interceptors>
<bean class="com.atguigu.mvc.interceptors.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/"/>
<ref bean="firstInterceptor"></ref>
</mvc:interceptor>
</mvc:interceptors>
以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过 mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
10.1 拦截器方法
主要的拦截器有三个抽象的方法
- 控制器之前
- 控制器之后
- 渲染试图完毕之后
preHandle
:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行
返回true为放行,即调用控制器方法;
返回false表示拦截,即不调用控制器方法
postHandle
:控制器方法执行之后执行postHandle()
afterComplation
:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
要想在拦截的时候输出,并且修改其配置,只有在第一个方法的返回值那里修改
@Component
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("FirstInterceptor-->preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("FirstInterceptor-->postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("FirstInterceptor-->afterCompletion");
}
}
- 若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行
而postHandle()和afterComplation()会按照配置的反序执行
- 若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行
postHandle()都不执行
返回false的拦截器之前的拦截器的afterComplation()会执行
11. 异常处理器
11.1 基于配置
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
HandlerExceptionResolver
接口的实现类有:DefaultHandlerExceptionResolver
和SimpleMappingExceptionResolver
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--设置将异常信息共享在请求域中的键-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
- 显示的错误信息是算数错误,所以返回的页面如果不带前后缀,则使用的是Thymeleaf的格式
- 要显示的错误信息,默认是放在了请求域中,所以通过键值对的ex来获取,在前端界面中获取ex的值
具体前端的页面信息为
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
出现错误
<p th:text="${ex}"></p>
</body>
</html>
11.2 基于注解
和上面一样
只需要将其配置去掉即可
在类中添加如下的配置
@ControllerAdvice
将当前类标识为异常处理的组件,@ControllerAdvice这个和@component都是差不多标识组件的意思@ExceptionHandler
用于设置所标识方法处理的异常- ex表示当前请求处理中出现的异常对象
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(value = {ArithmeticException.class, NullPointerException.class})
public String testException(Exception ex, Model model){
model.addAttribute("ex", ex);
return "error";
}
}
将其错误信息添加到请求域中并且将其输出
12. 注解配置SpringMVC
12.1 初始化类代替web.xml
在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer
接口的类,
如果找到的话就用它来配置Servlet容器。 Spring提供了这个接口的实现,名为SpringServletContainerInitializer
,这个类反过来又会查找实现WebApplicationInitializer
的类并将配
置的任务交给它们来完成。
Spring3.2引入了一个便利的WebApplicationInitializer
基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer
,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer
并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文
//web工程的初始化类,用来代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定spring的配置类
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 指定springMVC的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 指定DispatcherServlet的映射规则,即url-pattern
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 注册过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceResponseEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
==创建SpringConfig配置类,代替spring的配置文件==
@Configuration
public class SpringConfig {
}
12.2 WebConfig代替SpringMVC
/**
* 代替SpringMVC的配置文件:
* 1、扫描组件 2、视图解析器 3、view-controller 4、default-servlet-handler
* 5、mvc注解驱动 6、文件上传解析器 7、异常处理 8、拦截器
*/
//将当前类标识为一个配置类
@Configuration
//1、扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//5、mvc注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//4、default-servlet-handler
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//8、拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
TestInterceptor testInterceptor = new TestInterceptor();
registry.addInterceptor(testInterceptor).addPathPatterns("/**");
}
//3、view-controller
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
//6、文件上传解析器
@Bean
public MultipartResolver multipartResolver(){
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
return commonsMultipartResolver;
}
//7、异常处理
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException", "error");
exceptionResolver.setExceptionMappings(prop);
exceptionResolver.setExceptionAttribute("exception");
resolvers.add(exceptionResolver);
}
//配置生成模板解析器
@Bean
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
//生成模板引擎并为模板引擎注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
//生成视图解析器并未解析器注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}
12.3 测试类
@Controller
public class TestController {
@RequestMapping("/")
public String index(){
return "index";
}
}
13. SpringMVC执行流程
1) 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获
2) DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
a) 不存在
i. 再判断是否配置了mvc:default-servlet-handler
ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误
iii. 如果有配置,则访问目标资源(一般为静态资源,如JS,CSS,HTML),找不到客户端也会展示404错误
b) 存在则执行下面的流程
3) 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回
4) DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter
5) 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
6) 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求
在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
7) Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象
8) 此时将开始执行拦截器的postHandle(...)方法【逆向】
9) 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver
进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图
10) 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】
11) 将渲染结果返回给客户端