Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。它
的正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc),但它通常被称为“Spring
MVC”
从上述定义我们可以得出两个关键信息:
MVC是 Model View Controller 的缩写,它是软件工程中的一种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
下图是比较早期的MVC图,前后端未分离时期的。
● Model (模型) 是应⽤程序中⽤于处理应⽤程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
● View (视图) 是应⽤程序中处理数据显示的部分。通常视图是依据模型数据创建的
● Controller (控制器) 是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据,控制⽤户输⼊,并向模型发送数据
MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框
架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求。
现在绝⼤部分的 Java 项⽬都是基于 Spring(或 Spring Boot)的,⽽ Spring 的核⼼就是 Spring
MVC。也就是说 Spring MVC 是 Spring 框架的核⼼模块,⽽ Spring Boot 是 Spring 的脚⼿架,因此我
们可以推断出,现在市⾯上绝⼤部分的 Java 项⽬约等于 Spring MVC 项⽬,这是我们要学 Spring
MVC 的原因。
在创建 Spring Boot 项⽬时,我们勾选的 Spring Web 框架其实就是 Spring MVC 框架,如下图所示:
简单来说,咱们之所以要学习 Spring MVC 是因为它是⼀切项⽬的基础,我们以后创建的所有 Spring、
Spring Boot 项⽬基本都是基于 Spring MVC 的
Spring MVC 项⽬创建和 Spring Boot 创建项⽬相同(Spring MVC 使⽤ Spring Boot 的⽅式创建),
在创建的时候选择 Spring Web 就相当于创建了 Spring MVC 的项目。
@Controller
@RequestMapping("/demo")
public class UserController {
@RequestMapping("/hello")
@ResponseBody//返回非页面数据
public String hello(String name) {
return "hello "+name;
}
}
URL的路径要和注解的路由名称对应
@RequestMapping
的作用就是用来注册一个 url 路由,通过这个路由,前端的请求就可以实现和后端程序的交互了,而 @RequestMapping
这个注解既能修饰方法,又能修饰类
默认情况下使用@RequestMapping
会返回一个 html 页面,当加上 @ResponseBody
注解之后就能返回非页面的数据了
当使用 @ResponseBody
修饰一个类的时候,它表示此类中的所有方法都会返回一个非页面的数据
@Controller
@RequestMapping("/demo")
@ResponseBody// 使用 @ResponseBody 修饰一个类的时候,它表示此类中的所有方法都会返回一个非页面的数据
public class UserController {
@RequestMapping("/hello")
public String hello(String name) {
return "hello "+name;
}
}
1. @RequestMapping
在只设置路由地址的情况下,那么此方法是 既支持 GET 请求 又支持 POST请求类型的
2. 当给 @RequestMapping 设置了 method 属性之后,那么它只能支持设置的请求类型,其它类型会报 405错误
RequestMethod 是一个枚举类型
@RequestMapping(value = "get", method = RequestMethod.GET)
public String demo(String str) {
return "测试Get请求";
}
等同于上面的那种写法,但这两个注解只能修饰方法
@PostMapping("/testPost")
public String postType() {
return "test post";
}
@GetMapping("/getType")
public String getType() {
return "test get";
}
@RequestMapping
:支持任意一种请求类型(get/post …)(既能修饰方法又能修饰类)@GetMapping
:只支持 get 方式的请求(只能修饰方法)@PostMapping
:支持post 方式的请求(只能修饰方法)前端传递的参数 key 必须要和方法的参数名一致,否则不能正常的获取前端参数
比如前端必须传过来的 名为 name 的 key 才能获取值
@RequestMapping("/hello")
public String hello(String name) {
return "hello "+name;
}
@PostMapping("/all")
public Object getAll(Integer id,String name) {
return "学号:"+id+",姓名:"+name;
}
注意: 当有多个参数时,前后端进⾏参数匹配时,是以参数的名称进⾏匹配的,因此参数的位置
是不影响后端获取参数的结果
User对象
@Data//使用lombok的组合注解添加Get、Set、toString等方法
public class User {
private int id;
private String name;
private int age;
private String sex;
}
获取对象方法
@GetMapping("/getUser")
public User getUser(User user) {
return user;
}
测试结果
在某些特殊情况下,前端传递的参数 key 和我们后端接收的key可以不一致,比如前端传递了一个 time给后端,而后端是用 newTime接收的,而此时我们有大量代码都用到了 newTime不好修改,前端也下班了,出现这种情况就可以用 @RequestParam
来重命名前后端的参数值
代码
@GetMapping("/getTime")
public String getTime(@RequestParam("time") String myTime) {
return "当前时间"+myTime+":"+System.currentTimeMillis();
}
@RequestParam
既能重命名请求参数,又能保证此参数为必传参数
如果 required 设置为 true ,前端如果不传此参数就会报错(默认为true)
@GetMapping("/getTime")
public String getTime(@RequestParam(value = "time",required = false) String myTime) {
return "当前时间"+myTime+":"+System.currentTimeMillis();
}
当我们想设置此参数可传可不传就通过 @RequestParam 注解设置 required 为false
在参数的前面加上 @RequestBody
才能接收 JSON格式的数据
@GetMapping("/getUser")
public User getUser(@RequestBody User user) {
return user;
}
@GetMapping("/path/{name}/{password}")//这里要和下方变量名对应
public String getUrlParameter(@PathVariable String name, @PathVariable String password) {
return "username: "+name+",密码:"+password;
}
注意事项:@GetMapping("/path/{name}/{password}")
中的 {password}
参数不能省略 ,且名字要和方法的参数名对应
通过@RequestPart
注解和MultipartFile
类型可以实现文件上传
代码示例
// 获取配置文件中的文件保存路径
@Value("${uploadPath}")
private String path;
@RequestMapping("/upload")
public String uploadFile(@RequestPart("myFile") MultipartFile file) throws IOException {
// 1.通过uuid生成动态的文件名
String fileName = UUID.randomUUID()+
file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
// 2.保存文件
file.transferTo(new File(path+fileName));
return "文件上传成功!"+fileName;
}
获取Cookie
@RequestMapping("/cookie")
public Cookie[] getCookie(HttpServletRequest request, HttpServletResponse response) {
// 1.获取所有cookie
Cookie[] cookies = request.getCookies();
return cookies;
}
浏览器伪造cookie
// required如果为false说明Cookie可以为空
@RequestMapping("/getCookie")
public String getCookie(@CookieValue(value = "name",required = false) String userName) {
return "userName:"+userName;
}
获取 Header
@RequestMapping("/getHead")
public String getHead(HttpServletRequest request) {
String head = request.getHeader("User-Agent");
return head;
}
更简洁的获取 Header
@RequestMapping("/getHead2")
public String getHead2(@RequestHeader("User-Agent") String head) {
return "User-Agent: "+head;
}
Session 存储和获取
Session 存储和 Servlet 类似,是使⽤ HttpServletRequest 中获取的,如下代码所示:
存储和获取Session
@RequestMapping("/getSession")
public String getSession(HttpServletRequest request) {
HttpSession session = request.getSession(false);
String status = "未登录";
if (session != null) {
status = (String) session.getAttribute("userName");
}
return status;
}
@RequestMapping("/setSession")
public String setSession(HttpServletRequest request) {
// 设置为true如果不存在就会创建一个 session会话
HttpSession session = request.getSession(true);
if (session != null) {
session.setAttribute("userName","admin");
}
return "登录成功";
}
获取 Session 更简洁的⽅式
required
表示可以不存在不会报错
@RequestMapping("/getSession2")
public String getSession2(@SessionAttribute(value = "userName", required = false) String userName) {
return userName;
}
Spring MVC 会根据返回类型自动对你返回回的类型进行适配,设置 Content-Type
注意:text/html 和JSON都是一个非页面的数据
@ResponseBody // 返回一份非页面的数据
@RequestMapping("/getText")
public String getText(String name) {
return "hello "
+name+"";
}
@ResponseBody
@RequestMapping("/getJson")
public Object getJson() {
Map<String,String> map = new HashMap<>();
map.put("id","001");
map.put("userName","张三");
map.put("password","654321");
return map;
}
当返回的是一个前端页面时,就不能用 @ResponseBody 注解修饰了
@RequestMapping("/getHtml")
public Object getHtml() {
return "/index.html";
}
return不但可以放回一个视图,还可以实现跳转,跳转的方式有两种
@Controller
@RequestMapping("/demo")
public class UserController {
@RequestMapping("/index")
public Object index() {
return "forward:/index.html";
}
@RequestMapping("/getIndex")
public Object getIndex() {
return "redirect:/index.html";
}
}
使用请求转发
通过上面请求转发的访问资源路径,并不是在根路径,所以会出现资源访问不到的情况
如果是在类上没有加 @RequestMapping 注解,就不会出现资源访问不到的情况
@Controller
public class UserController {
@RequestMapping("/index")
public Object index() {
return "forward:/index.html";
}
@RequestMapping("/getIndex")
public Object getIndex() {
return "redirect:/index.html";
}
}
页面转发
请求转发和重定向的区别
1.请求转发 URL地址不变,因为是服务器端进行转发和响应的;请求重定向 URL 地址会发生改变,因为服务器端直接将请求重定向到具体的地址上
2.使用请求转发有可能外部资源是会丢失,访问不到的;而请求重定向是直接重定向到 URL 地址了,所以请求重定向和直接访问目标地址的效果是一模一样的,所以不会存在外部资源丢失的情况