Spring MVC(正式名称:Spring Web MVC) 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。
- Spring MVC 是⼀个 Web 框架
- Spring MVC 是基于 Servlet API 构建的
MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
MVC 执行流程:
- 用户的请求首先到 Controller
- Controller 将请求转发给 Model
- Model 处理业务并将数据结果返回给 Controller
- Controller 将处理的数据发给 View
- View 将数据转换成页面发送给用户
MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web框架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求.
在创建 Spring Boot 项⽬时,我们勾选的 Spring Web 框架其实就是 Spring MVC 框架。
- 连接的功能:将⽤户(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的 Spring 程序。
- 获取参数的功能:⽤户访问的时候会带⼀些参数,在程序中要想办法获取到参数。
- 输出数据的功能:执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤户
首先创建一个 TestController 类,来实现用户与 Spring 程序的交互:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller //让该类随着 Spring 框架启动而加载
@ResponseBody //返回非页面数据(这个注解在返回数据中讲解)
//@RestController 相当于@Controller + @ResponseBody
@RequestMapping("/test") //路由器规则注册(一级路由)
public class TestController {
@RequestMapping("/hi") //路由器规则注册(二级路由)
public String sayHi() {
System.out.println("hi Spring MVC");
return " 你好 Spring MVC "
;
}
}
通过浏览器地址访问,来与程序交互:
可以看到,通过我们访问地址 http://localhost:8080/test/hi 就可以执行sayHi 方法,并返回字符串到页面上了。
这里注意:
spring mvc 项目默认扫描路径是启动类所在的包下所有的子包,也就是说:我们新建的的类要想放入 IoC 中,就得在该包下创建类。(默认启动类是在 demo 包下)
@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路由映射的。
路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类的某个⽅法的过程就叫路由映射。
@RequestMapping 即可修饰类,也可以修饰⽅法,当修饰类和⽅法时,访问的地址是类 + ⽅法。
@RequestMapping 也可以直接修饰⽅法,代码实现如下:
默认情况下,@RequestMapping 是支持 post 和 get 请求的,我们用 Postman 来验证一下:
get 请求:
post 请求:
在有些情况下,我们可能要该注解只支持其中一种请求,那要怎么实现呢?
@RestController
@RequestMapping("/test")
public class TestController {
//方式一
//下面 value 也可以改为 path
@RequestMapping(value = "/hi",method = RequestMethod.GET)
public String sayHi() {
System.out.println("hi Spring MVC");
return " 你好 Spring MVC "
;
}
//方式二
@GetMapping("/hhh")
public String hhh() {
return "hello world";
}
}
注意,浏览器通过 url 来访问地址, 默认是 get 请求。
再通过 Postman 来构造一下 post 请求:
2. 只支持 post 请求的注解方式
@RestController
@RequestMapping("/test2")
public class Test2Controller {
//方式一
@RequestMapping(path = "/hi",method = RequestMethod.POST)
public String sayHi() {
return "你好";
}
//方式二
@PostMapping("/hhh")
public String hhh() {
return "你好,世界!";
}
}
这里通过浏览器直接访问就报错了:
通过 Postman 构建 post 请求:
学习 servlet 时获取参数的写法:
@RestController
@RequestMapping("/user")
public class UserController {
//传统写法获取请求中的参数
@RequestMapping("/getname") //这里不建议使用大小写, 可以用下划线来区分
public String getName(HttpServletRequest request) {
return "Name : " + request.getParameter("name");
}
}
通过注解获取:
@RestController
@RequestMapping("/user")
public class UserController {
//直接获取 url 中的参数
//当该路由被触发后,执行到方法时
//就会对 name 进行匹配,直接对 name 进行赋值
@RequestMapping("/getname2")
public String getName2(String name) {
return "Name : " + name;
}
}
其实获取多个参数和获取单个参数差不多:
@RestController
@RequestMapping("/user")
public class UserController {
//在传参时,注意参数的命名与要获取的参数名一致
@RequestMapping("/getname3")
public String getName3(String name, Integer age) {
return "Name : " + name + "age : " + age;
}
}
顾名思义,就是将参数当做一个对象的部分属性来接收,在接收时我们新建一个 model 层,来存放所需要的对象:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/add")
public User add(User user) {
return user; //将得到的对象返回回去
}
}
@Data 注解是个组合注解,它等于:@Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor + @NoArgsConstructor
添加了它就不需要我们自己写 Getter 和 Setter 方法了,减少重复工作。
当后端进行接收数据时,就会将参数和 user 里的属性进行对比,发现属性名称与参数的 key 相同就会进行赋值。
其实表单传参和 url 传参区别就是传递参数的位置不一样,对于后端来说都一样,我们可以用 Postman 来构造请求:
有时候前端传递的 key 你觉得不合理,想改个顺眼的名字,就可以对传递过来的参数重命名,当然前端的参数是不变的。(注意: 对象不能重命名)
@RestController
@RequestMapping("/user")
public class UserController {
//将前端参数 y 改为 name,并由 name 接收
@RequestMapping("/name")
public String name(@RequestParam("y") String name) {
return name;
}
}
在 @RequestParam 中,参数默认是必传的:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/name")
public String name(@RequestParam(value = "y",required = false) String name) {
return name;
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/get_json")
public User getJson(@RequestBody User user) {
return user;
}
}
@RestController
@RequestMapping("/user")
public class UserController {
//{aid} 中的 aid 是用来接收参数的
@RequestMapping("/get_url/{aid}")
//下面的 "aid" 是将参数名为 aid 的参数赋值给后面的 aid
//这里两个 aid 名字相同,可以省略参数名("aid")不写
public Integer getUrl(@PathVariable("aid") Integer aid) {
return aid;
}
}
当然了, 还可以传递多个参数 :
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/get_url2/{aid}/{name}")
public String getUrl2(@PathVariable() Integer aid, @PathVariable String name) {
return "aid: " + aid + " name: " + name;
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/upload")
//myfile 是接收的参数名, 赋值给 file
public String upload(@RequestPart("myfile") MultipartFile file) throws IOException {
String path = "E:\\image\\img.png";
//保存文件
file.transferTo(new File(path));
return path;
}
}
上面的代码写法是有问题的, 如果有很多用户都要保存文件, 那文件名就不能写死了, 必须保证每次保存的文件名都不一样, 可以使用 UUID :
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/upload")
public String upload(@RequestPart("myfile") MultipartFile file) throws IOException {
//得到 UUID 并去掉 "-"
String name = UUID.randomUUID().toString().replace("-","");
//file.getOriginalFilename() 得到文件名
//file.getOriginalFilename().lastIndexOf(".") 得到最后一个"."的下标
//整个就是 name 拼接上 .后缀名
name += file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
String path = "E:\\image\\" + name;
//保存文件
file.transferTo(new File(path));
return path;
}
}
多次提交得到的文件 :
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/get_cookie")
public String getCookie(@CookieValue(value = "myCookie", required = false) String ck) {
return ck;
}
}
没有输出 :
通过前端构建一个 key 为 myCookie 的 cookie :
要想获取 Session 首先要有 Session, 我们可以上传一个 Session :
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/set_session")
public String setSession(HttpServletRequest request) {
HttpSession session = request.getSession();
if(session != null) {
session.setAttribute("SESSION_KEY","张三");
return "session set success";
}
return "session set fail";
}
@RequestMapping("/get_session")
public String getSession(@SessionAttribute(required = false, value = "SESSION_KEY") String name) {
return name;
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/get_header")
public String getHeader(@RequestHeader("User-Agent") String userAgent) {
return "UserAgent : " + userAgent;
}
}
默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的都是 html 格式,如果需要返回非 html 格式数据, 就得使用 @ResponseBody 注解了, 我们之前一直使用的 @RestController 便是 @ResponseBody + @Controller 注解.
验证返回数据的默认格式 :
@Controller
public class Test {
@RequestMapping("/b")
public String t() {
return "hello.html";
}
}
因为没有这个前端页面, 所以返回出错 :
在静态文件中加入 hello.html 文件 :
使用 @ResponseBody 返回字符串 :
@Controller
public class Test {
@ResponseBody
@RequestMapping("/a")
public String t2() {
return "hello.html";
}
}