(1)什么是SpringMVC?
spring官方的描述是这样的:
SpringMVC, 也就是Spring web MVC, 是基于Servlet API构建的原始web框架, 从一开始就包含在spring框架中, spring web mvc 时候它的正式名称, 原子模块spring-webmvc, 但是通常叫它Spring MVC
从上面官方给出的定义不难看出:
- spring MVC 是一个web框架
- spring MVC是基于servlet api构建的
- spring mvc是包含在spring框架中的
(2)什么是MVC?
MVC 是 Model View Controller, 的缩写, 它是软件工程中的一种软件架构模式, 他把软件系统分为模型, 视图和控制三个基本部分:
- Model(模型) : 是应用程序中用于处理应用程序数据逻辑部分的模型, 通常模型是对象负载在数据库中存取数据
- View(视图) : 视图是应用程序中处理并且显示数据的部分, 通常Controller从Model拿到的数据都是比较原始的, 用户直接看也看不懂, 这个时候就需要视图将其转换为用户所能看懂的页面展示出来, 通常视图是依据模型数据创建的
- Controller(控制器) : 是应用程序中处理用户交互的部分, 通常控制器负责, 从视图读取数据, 控制用户输入, 并向模型发送数据
(3) SpringMVC和spring的关联
MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web框架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求, 并做出相应的反应.
为什么要学 Spring MVC?
现在绝大部分的Java项目都是基于Spring或者是spring boot的. 而spring的核心就是spring mvc, 也就是说spring mvc是spring框架的核心模块, 而spring boot是spring 的脚手架, 因此, 市面上绝大部分java项目其实可以看做是spring mvc项目.
在创建spring boot的时候, 勾选的spring web框架其实就是spring mvc, 如图:
(4)spring mvc 的创建和连接
创建mvc项目
点击next.
选择依赖:
- Spring Web, 也就是spring mvc框架
- lombok. 一种日志依赖
选择好之后会显示:
创建好之后如下:
其中.mvn和mvnw.cmd, mvnw, HELP.md4个文件和文件夹是可以删除的.
连接用户
下面展示使用mvc向用户连接:
首先创建UserController类, 实现spring和用户的互通, 如下:
@Controller // 让spring启动时, 注入容器 @ResponseBody // 返回非页面数据 @RequestMapping("/user") // 路由器规则 public class UserController { // 路由器规则注册 @RequestMapping("/hi") public String sayHi() { return "<h1>hi, Spring MVC</h1>"; } }
启动项目
访问 http://localhost:8080/user/hi
-- 输出
(5)注解@RequestMapping
基本使用
@RequestMapping 是spring web应用最长被用到的注解之一, 他是用来注册接口的路由映射的.
什么是路由映射? 也就是说, 当一个用户访问一个url的时候, 将该用户的请求对应到程序中某个类的某个方法的过程就叫做路由映射.
基本使用:
@Controller @RequestMapping("/p") public class PersonController { @RequestMapping("/index") public Object index(Person person){ // 获取参数 System.out.println(person.getName() +":"+ person.getPassword()); // 执⾏业务... return "/index.html"; } }
@RequestMapping 即可以修饰类也可以修饰方法, 当修饰类和方法时, 访问的地址是类 + 方法
@RequestMapping 也可以直接用来修饰方法, 具体实现如下:
@Controller @ResponseBody // 定义返回的数据格式为⾮视图(text/html) public class UserController { @RequestMapping("/hi") public String sayHi(){ return "<h1>Hi,Spring MVC.</h1>"; } }
直接访问: http://localhost:8080/hi
输出: Hi,Spring MVC.
@RequestMapping 是post还是get?
使用postMan 来测试一下默认情况下使用注解@RequestMapping 是否可以接收get或者post
我们可以在方法参数里面加入method = 来指定是post还是get:
先显式指定为post:
@Controller // 让spring启动时, 注入容器 @ResponseBody // 返回非页面数据 @RequestMapping("/user") // 路由器规则 public class UserController { // 路由器规则注册 @RequestMapping(value = "/hi", method = RequestMethod.POST) public String sayHi() { return "<h1>hi, Spring MVC</h1>"; } }
使用postman发送post请求:
发送后输出:
如果发送get请求:
返回状态码为405的错误信息.
相同的如果设置了method = RequestMethod.GET, 那么发送get请求就可以正常接收, 否则就收到错误提示.
那么如果不显式指定method呢?? 我们删除这个method字段, 继续尝试, 结果如下:
这里的GET, POST, PUT, PATCH等都可以正确接收请求
@GetMapping和@PostMapping
如果我们想要改变接收请求的类型, 那么就可以显式指定, 例如
@RequestMapping(value = "/hi", method = RequestMethod.GET) // 或者 // @RequestMapping(value = "/hi", method = RequestMethod.POST)
当然, 也可以直接写作@RequestMapp("/hi"), 他同样可以接受get请求
同时也可以使用另外一个注解: @GetMapping("/hi")
同理Post也有类似的操作:
// 写法1 @RequestMapping(value = "/index",method = RequestMethod.POST) // 写法2 @PostMapping("/index")
获取参数
传递单个参数
可以直接使用方法的参数进行传参, 比如下面代码:
@Controller // 让spring启动时, 注入容器 @ResponseBody // 返回非页面数据 @RequestMapping("/user") // 路由器规则 public class UserController { @RequestMapping("/para1") public Object method1(String name) { System.out.println("参数name: " + name); return "/index.html"; } }
在postMan中发送请求访问这个方法映射:
这个请求后面跟着一个查询字符串: name=zhangsan
postman返回如下:
控制台输出:
传递对象
当然, 如果仅仅只是传递一个参数, 肯定是不够用的, mvc同样可以传递对象参数. 例如我们穿件一个Person类:
@Data public class Person { private int age; private String name; private String password; }
注意此处的@Data是lombok的功能:他相当于在编译的时候提供了Person这个类的get和set, 还有重写了toString方法.
传递对象代码实现:
@Controller @RequestMapping("/person") @ResponseBody public class PersonController { @RequestMapping("/do") public Object method(Person ara) { System.out.println("Person 对象中的name:" + ara.getName()); System.out.println("Person 对象中的password:" + ara.getPassword()); return "/index.html"; } }
在postman中测试, 输入:
http://127.0.0.1:8080/person/do?name=zhangsan&age=18&password=123
控制台输出:
表单传递
表单传递, 或者是传递多个参数:
@RequestMapping("/form") public Object method2(String name, String password) { System.out.println("name: " + name); System.out.println("password: " + password); return "/index.html"; }
访问地址:
http://127.0.0.1:8080/person/form?name=zhangsan&password=123
控制台输出:
需要注意的是, 当有多个参数的时候, 前后端进行参数匹配的时候, 是以参数的名称进行匹配的, 因此参数的位置是不影响后端获取参数的结果.
后端参数重命名
某些特殊情况下, 前端传递的参数key和我们后端接收的可以不一致, 比如前端传递了一个time给后端, 而后端又有createtime字段来接收的, 这样就会出现参数对应不上, 接收不到的情况, 如果出现这种情况, 我们就可以使用@RequestParam来重命名前后端的参数值.
@RequestMapping("/requestpara") public Object method3(@RequestParam("time") String createTime) { System.out.println("时间:"+ createTime); return "/index.html"; }
访问:
控制台输出:
--
当你将参数改为createTime的时候:
使用@RequestParam之后就不能使用原来的字符串变量名.
这是因为后端已经声明了前端必须传递一个time参数, 但是前端没有给后端传递, 我们查看@RequestParam注解的实现细节就可以发现:
// @RequestParam 源码 @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestParam { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; boolean required() default true; String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n"; }
这里的required是必须的意思, 默认值为true, 因此不传递此参数, 就会报400的错误.
非必传设置
如果我们后端接收的重命名后的参数, 却又是个非必传参数, 但是它被注解了@RequestParam参数, 这个时候该怎么办?
我们可以通过设置@RequestParam的参数required来实现:
@RequestMapping("/requestpara") public Object method3(@RequestParam(value = "time", required = false) String createTime) { System.out.println("时间:"+ createTime); return "/index.html"; }
这个时候, 即使不传递time这个参数, 也不会出错.
(6)@RequestBody
@RequestBody是用来接收前端传递给后端的json字符串中的数据的, 所以只能发送post请求, Get请求方法无请求体, 所以在使用@RequestBody接收数据的时候, 前端不能使用GET方式, 提交数据, 而是使用post方式进行提交数据
在后端的同一个接收方法中,@RequestBody和@RequestParam可以同时使用, @RequestBody最多只能有一个, 但是@RequestParam可有朵儿
再看向前端, JQuery中, &.ajax默认发送的参数类型为application/x-www-form-urlencoded, 而@RequestBody处理的参数类型为json, 或者是xml, 通过contentType来制定
传递之前, json对象要使用json.stringify()方法, 这个方法将一个js值, 对象, 或者数组, 转化成一个json字符串.
@RequestMapping("/reqBody") public Object method4(@RequestBody Person person) { System.out.println("Person:" + person); return "/index.html"; }
访问:
输出:
如果去掉@RequestBody:
那么前端请求中的数据, 就无法自动写入参数中.
(7)获取URL中的参数(@PathVariable)
后端代码实现:
@RequestMapping("/path/{name}/{password}") public Object method5(@PathVariable String name, @PathVariable String password) { System.out.println("name:" + name); System.out.println("password:" + password); return "/index.html"; }
postman访问:
输出:
(8)@RequestPart
@RequestMapping("/part") public String getpart(@RequestPart("myfile")MultipartFile file) throws IOException { // 生成唯一一个id , 使用UUID, UUID = MAC + 随机种子 + 加密算法 String name = UUID.randomUUID().toString().replace("-",""); // 获取文件后缀名: String filename = file.getOriginalFilename(); String fileLastName = filename.substring(filename.lastIndexOf('.')); String path = "D:\\image\\" + name + fileLastName; // 保存文件 file.transferTo(new File(path)); return path; }
上传文件:
选择文件类型为File然后上传文件
(9)@ResponseBody 说明
@ResponseBody 返回值如果是字符就会转换成text/html, 如果返回的是对象, 就会转换成application/json 返回给前端.
@ResponseBody 可以用来修饰方法或者是修饰类, 修饰类表示类中的所有方法都会返回html, 或者json, 而不是视图.
@RestController 是 @Controller + @ResponseBody的组合.