1 SpringMVC简介
Spring MVC是Spring Web层的一个模块。Spring MVC属于SpringFramework的后续产品,已经融合在Spring Web Flow里面。Spring框架提供了构建Web应用程序的全功能MVC模块。使用Spring可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等。SpringMVC处于控制层(Web层),该知识点在《Spring Framework学习笔记-IOC》1.2章节有所涉及。
2 使用该模块的事前准备和配置
2.1 导入jar包
需要导入的jar包有:spring-core-container5、spring-aop、spring-webmvc、spring-web
(SpringBoot项目在pom中加入依赖即可),而现在无需手动导入这些jar包,它们都被SpringBoot封装成了一个jar包即spring-boot-starter-web
,只需要通过maven进行应用即可导入。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.2 配置web.xml(SpringBoot项目不用)
SpringBoot项目是不用配置这个的,如果是上古项目,纯使用Spring的项目,需要在web.xml配置前端控制器DispathcherServlet,在其中指定springmvc配置文件的路径。以前在学习Java Servlet时配置过web.xml文件,点击这里查看对web.xml中Servlet的配置
2.3 创建springmvc配置文件
在src目录下创建springmvc配置文件:springmvc.xml并配置扫描包、内部资源视图解析器(SpringBoot项目基于注解配置不用配置这个)。如果使用SpringBoot项目可以使用thymeleaf模板引擎来省去繁琐的配置。SpringBoot项目配置视图解析器时可以在Spring的配置文件application.properties
或者application.yml
中配置,如果使用thymeleaf模板引擎只需要在spring.thymeleaf
节点下进行相关配置。
3 演示请求
3.1 导入Thymeleaf依赖
在pom.xml中添加Thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3.2 新建页面
在resources目录下建立template文件夹并在其中新建html页面,里边随便写点东西。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>啦啦啦</title>
</head>
<body>
<h1>这是一个主页</h1>
</body>
</html>
3.3 配置Thymeleaf
在application.properties配置文件中对Thymeleaf进行配置
spring.thymeleaf.prefix=classpath:/template/
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.enabled=true
spring.thymeleaf.mode=HTML5
spring.thymeleaf.content-type=text/html
3.4 编写控制层控制器
package cc.fireflyhut.selftech.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller // 标注当前bean为控制层
@RequestMapping("/index") // 请求路径
public class ViewController {
@RequestMapping("/1") // 请求路径
public String index() {
return "index";
}
}
3.5 启动并访问
访问路径http://127.0.0.1:8080/index/1
4 视图解析器
4.1 视图解析器的概述
SpringMVC中的视图解析器的主要作用就是将逻辑视图转换成用户可以看到的物理视图。
当用户对SpringMVC应用程序发起请求时,这些请求都会被SpringMVC的DispatcherServlet处理,通过处理器找到最为合适的HandlerMapping定义的请求映射中最为合适的映射,然后通过HandlerMapping找到相对应的Handler,然后再通过相对应的HandlerAdapter处理该Handler。返回结果是一个ModelAndView对象,当该ModelAndView对象中不包含真正的视图,而是一个逻辑视图路径的时候,ViewResolver就会把该逻辑视图路径解析为真正的View视图对象,然后通过View的渲染,将最终结果返回给用户。
SpringMVC中处理视图最重要的两个接口就是ViewResolver和View。ViewResolver的作用是将逻辑视图解析成物理视图,View的主要作用是调用其render()方法将物理视图进行渲染。
一般来说,对于SpringMVC控制器中的方法,无论是返回String、View还是ModelAndView,SpringMVC在内部都会将返回结果封装成ModelAndView对象,然后返回给用户。
4.2 SpringMVC视图解析器解析流程
- 将SpringMVC控制器中的返回结果封装成一个ModelAndView对象。
- 通过SpringMVC中的视图解析器,使用ViewResolver对控制器返回的ModelAndView对象进行解析,将逻辑视图转换成物理视图。
- 调用View中的render()方法对物理视图进行渲染。
4.1~4.2引用自该文章
4.3 响应数据和页面的几种方法
4.3.1 通过ModelAndView响应
- 在application.properties中配置好thymeleaf(也可以用yaml文件)
spring.thymeleaf.prefix=classpath:/template/
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.enabled=true
spring.thymeleaf.mode=HTML5
spring.thymeleaf.content-type=text/html
- 在Controller中新建一个方法,该方法返回类型为ModelAndView
package cc.fireflyhut.selftech.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/index")
public class ViewController {
/**
* 通过返回ModelAndView返回页面和数据
* */
@RequestMapping("/interesting")
public ModelAndView interesting() {
ModelAndView mv = new ModelAndView("index"); // 通过有参构造器创建对象,传入页面的名字
mv.addObject("msg", "通过返回ModelAndView来响应页面和数据");
return mv;
}
}
- 新建一个HTML页面命名为index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>啦啦啦</title>
</head>
<body>
<h1>这是一个主页</h1>
<h2>这里会从后端传来什么值呢?</h2>
<h3 th:text="${msg}"></h3>
</body>
</html>
- 访问该地址得到页面
通过浏览器访问:http://127.0.0.1:8080/index/interesting
4.3.2 通过String响应
- 修改上一步的Controller向其中新增一个方法
package cc.fireflyhut.selftech.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/index")
public class ViewController {
/**
* 通过返回String返回页面和数据
* */
@RequestMapping("/something")
public String something(Model model) { // 在参数列表接收Model对象
// 通过调用Model对象的addAttribute方法向页面传值
model.addAttribute("msg", "通过返回String来响应页面和数据");
return "index";
}
/**
* 通过返回ModelAndView返回页面和数据
* */
@RequestMapping("/interesting")
public ModelAndView interesting() {
ModelAndView mv = new ModelAndView("index"); // 通过有参构造器创建对象,传入页面的名字
mv.addObject("msg", "通过返回ModelAndView来响应页面和数据");
return mv;
}
}
- 再次用不同的路径访问浏览器
通过浏览器访问:http://127.0.0.1:8080/index/something
4.4 请求转发与重定向
4.4.1 SpringMVC的请求转发
要求Controller方法返回String类型,并且在返回的路径前加上forward:
关键字,后面接上要转发的路径,例如forward:/index/forwardTarget
,请注意:这个路径一定要有对应的Controller方法,不然会404,因为请求转发也要经过视图解析器。
- 编写一个Controller
package cc.fireflyhut.selftech.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class ViewController {
/**
* 被请求转发的目标路径
* */
@RequestMapping("/forwardTarget")
public String forwardTarget() {
return "forward";
}
/**
* 请求转发
* */
@RequestMapping("/forward")
public String forwardTest() {
System.out.println("请求转发测试");
return "forward:/index/forwardTarget";
}
}
- 编写一个HTML页面命名为forward.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>请求转发页面测试</title>
</head>
<body>
<h1>这是一个请求转发页面测试页面</h1>
</body>
</html>
- 浏览器访问
http://127.0.0.1:8080/index/forward
4.4.2 SpringMVC的重定向
要求Controller方法返回String类型,并且在返回的路径前加上redirect:
关键字,后面接上要转发的路径,例如redirect:/index/redirectTarget
,请注意:这个路径一定要有对应的Controller方法,不然会404,因为重定向也要经过视图解析器。
- 在上一步的Controller中新加两个方法
package cc.fireflyhut.selftech.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class ViewController {
/**
* 请求转发
* */
@RequestMapping("/forward")
public String forwardTest() {
System.out.println("请求转发测试");
return "forward:/index/forwardTarget";
}
/**
* 被请求转发的目标路径
* */
@RequestMapping("/forwardTarget")
public String forwardTarget() {
return "forward";
}
// 下方为新加方法
/**
* 重定向
* */
@RequestMapping("/redirect")
public String redirectTest() {
return "redirect:/index/redirectTarget";
}
/**
* 被重定向的目标路径
* */
@RequestMapping("/redirectTarget")
public String redirectTarget() {
return "redirect";
}
}
- 新建一个HTML页面命名为redirect.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>重定向测试页面</title>
</head>
<body>
<h1>重定向测试页面</h1>
</body>
</html>
- 访问
http://127.0.0.1:8080/index/redirect
神奇的发现浏览器地址栏自动变为了http://127.0.0.1:8080/index/redirectTarget
5 SpringMVC常见注解
5.1 RequestMapping注解
-
作用
标记在类和方法上,指定访问路径以及访问方法。
它有作用类似的注解,例如PostMapping注解、GetMapping注解等,它们其实就是省略了自己填写method属性的过程,例如@RequestMapping(path = "/test", method = RequestMethod.GET)
和@GetMapping("/test")
是等价的。 -
作用域
类和方法 -
属性(常用的)
3.1. value或path:访问资源的路径。类型为String,并支持Ant-Style通配符。
3.2. method:请求方法。类型为RequestMethod数组,RequestMethod为枚举类。
3.3. params:请求参数的限制。类型为String数组,例如:{"username=zhangsan"}
代表请求参数中必须携带参数名为username且值为zhangsan的参数,或{"password!=lisi"}
代表为请求参数中必须携带参数名为password且值不能为lisi的参数,又或{"!type"}
和{"age"}
表示参数中不可以有名为type的参数、参数中必须有名为age的参数,值为何无所谓。3.4. headers:请求头。类型为String数组。
3.5. consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html,application/x-www-form-urlencoded等等。3.6. produces:它的作用是指定返回值类型。它不但可以设置返回值类型,还可以设定返回值的字符编码。例子如下:
@RequestMapping(path = "/justTest",
produces = "application/json;charset=utf-8",
consumes = "application/x-www-form-urlencoded",
method = {RequestMethod.POST})
@ResponseBody
public BaseResponse justTest(BaseRequest baseRequest) {
// ...
}
5.2 RequestParam注解
-
作用
标记在Controller方法的参数前,用于指定该参数接收请求参数。当请求参数的名字和Controller方法参数名字一致时,RequestParam注解可以省略。 -
作用域
参数 -
属性(常用的)
3.1. value和name:指定请求参数名字,类型为String。
3.2. required:是否必填,类型为boolean。
3.3. defaultValue:如果required值为false,请求没有携带指定参数,那么该参数的值为defaultValue所指定的默认值。
5.3 PathVariable注解
- 作用
标记在Controller方法的参数前,用于指定该参数就收URL路径中的变量值。当RequestMapping注解这样写时:@RequestMapping(path = "/archives/{archivesId}")
,path属性值的大括号中的量即为变量,在PathVariable中传入该变量名称可以用方法参数接收该变量值。例子如下:
@RequestMapping(path = "/archives/{archivesId}")
public String findArchives(Model model, @PathVariable("archivesId") String archivesId){
// ...
}
-
作用域
参数 -
属性(常用的)
3.1. value和name:指定请求参数名字,类型为String。
3.2. required:是否必填,类型为boolean。
6 自定义SpringMVC类型转换器
6.1 背景需求
当我们编写的Controller方法参数的dto类中字段无法被SpringMVC合理的转换并赋值,我们只能自定义类型转换器。
6.2 自定义类转换器演示
该演示是使用Idea以及SpringBoot项目,项目依赖管理工具为Maven。
- 新建一个类并实现Converter<S, T>接口
package cc.fireflyhut.selftech.controller.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class StringToDateConverter implements Converter<String, Date> {
/**
* 实现接口的方法,该方法用于将字符串转换成Date类型
* */
@Override
public Date convert(String stringDate) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(stringDate);
} catch (ParseException e) {
e.printStackTrace();
}
return date; // 如果转换失败返回null
}
}
- 请求和响应两个dto类
package cc.fireflyhut.selftech.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BaseRequest {
private String rpid;
private Date reqTime;
}
package cc.fireflyhut.selftech.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseResponse {
private String message;
private String retCode;
}
- Controller层方法
package cc.fireflyhut.selftech.controller;
import cc.fireflyhut.selftech.dto.BaseRequest;
import cc.fireflyhut.selftech.dto.BaseResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController // 该注解等价于@Controller+@ResponseBody
@RequestMapping("/api")
public class CommonController {
@PostMapping(path = "/converterTest", produces = "application/json;charset=utf-8", consumes = "application/x-www-form-urlencoded")
public BaseResponse justFillSomePara(BaseRequest baseRequest) {
System.out.println(baseRequest.toString()); // 打印请求参数
if (baseRequest.getReqTime() == null || baseRequest.getRpid() == null) {
return new BaseResponse("failed", "-1000");
}
return new BaseResponse("succcess", "0000");
}
}
- 通过postman以预期正确的格式请求并查看返回结果和控制台打印结果
postman请求结果:
控制台打印结果:
- 通过postman以预期错误的格式请求并查看返回结果和控制台打印结果
postman请求结果:
控制台打印结果,可以发现因为转换错误抛了异常: