1.什么是 Spring Web MVC?
Spring Web MVC is the original web framework built on the Servlet API and has been includedin the Spring Framework from the very beginning. The formal name, "Spring Web MVC",comes from the name of its source module (spring-webmvc)引⽤来⾃: https://docs.spring.io/spring-framework/reference/web/webmvc.html
什么是Servlet呢?Servlet 是⼀种实现动态⻚⾯的技术. 准确来讲Servlet是⼀套 Java Web 开发的规范,或者说是⼀套Java Web 开发的技术标准. 只有规范并不能做任何事情,必须要有⼈去实现它. 所谓实现 Servlet 规范,就是真正编写代码去实现 Servlet 规范提到的各种功能,包括类、⽅法、属性等.Servlet 规范是开放的,除了 Sun 公司,其它公司也可以实现 Servlet 规范,⽬前常⻅的实现了Servlet 规范的产品包括 Tomcat、Weblogic、Jetty、Jboss、WebSphere 等,它们都被称为"Servlet 容器". Servlet 容器⽤来管理程序员编写的 Servlet 类.
1.1 MVC定义
⽐如去饭店吃饭客⼾进店之后, 服务员来接待客⼾点餐, 客⼾点完餐之后, 把客⼾菜单交给前厅, 前厅根据客⼾菜单 给后厨下达命令. 后厨负责做饭, 做完之后, 再根据菜单告诉服务员, 这是X号餐桌客⼈的饭.在这个过程中服务员就是View(视图), 负责接待客⼾, 帮助客⼾点餐, 以及给顾客端饭前厅就是Controller(控制器), 根据⽤⼾的点餐情况, 来选择给哪个后厨下达命令.后厨就是Model(模型), 根据前厅的要求来完成客⼾的⽤餐需求⽐如去公司⾯试我们到了公司之后, HR会给我们安排会议室, 根据候选⼈去通知不同的部⻔来安排⾯试, ⾯试结束 后, 由HR来告诉⾯试结果在这个过程中HR就是View(视图), 负责接待候选⼈, 并告知候选⼈⾯试结果不同的部⻔, 就是Controller(控制器), HR根据候选⼈来选择对应的部⻔来进⾏⾯试⾯试官, 就是Model层, 来处理⾯试这个事情.
1.2 什么是Spring MVC ?
⽐如: 厨房可以⽤来做饭, 但真实实现做饭功能的是⽕以及各种做饭相关的⻝材和⼯具.厨房就好⽐是SpringBoot, 厨房可以装柜⼦, 实现收纳功能, 装燃⽓灶等, 实现做饭功能.做饭这个事, 就是MVC, 在⼏千年前, 有⽕有⻝材就可以实现做饭
不过核⼼没变⽐如上⾯的例⼦中, 去饭店吃饭. ⼀些饭店是前厅来负责接待客⼾, 帮助客⼾点餐, 也就是Controller来 负责接收⽤⼾的请求.去公司⾯试, 直接由⾯试官来接待候选⼈, 省去了HR中间的交接过程.
2.学习Spring MVC
⽐如⽤⼾去银⾏存款1. 建⽴连接: 去柜台2. 请求: 带着银⾏卡, ⾝份证去存款3. 响应: 银⾏返回⼀张存折.
对于 Spring MVC 来说,掌握了以上 3 个功能就相当于掌握了 Spring MVC.
2.1 项⽬准备
Spring MVC 使⽤ Spring Boot 的⽅式创建Spring MVC更早期的实现⽅式, 课程中不再讲解
创建项⽬时, 勾选上 Spring Web 模块即可,如下图所⽰:

2.2 建⽴连接
package com.example.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MVC_07_22_1 {
@RequestMapping("/sayhi")
public String methed(){
return "hello 各位";
}
}
2.2.1 @RequestMapping 注解介绍
2.2.2 @RequestMapping 使⽤
@RequestMapping标识⼀个类:设置映射请求的请求路径的初始信息@RequestMapping标识⼀个⽅法:设置映射请求请求路径的具体信
@RequestMapping("/user") @RestController public class UserController { @RequestMapping("/sayHi") public String sayHi(){ return "hello,Spring MVC"; } }
@RequestMapping("user")
@RestController
public class UserController {
@RequestMapping("sayHi")
public String sayHi(){
return "hello,Spring MVC";
}
}
@RequestMapping("/user/m1")
@RestController
public class UserController {
@RequestMapping("/say/hi")
public String sayHi(){
return "hello,Spring MVC";
}
}
2.2.3 @RequestMapping 是 GET 还是 POST 请求?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/user/sayHi" method="post">
<input type="submit" value="提交">
</form>
</body>
</html>
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@RestController
public class UserController {
@RequestMapping(value = "/getRequest",method= RequestMethod.POST)
public String sayHi(){
return "get request...";
}
}
2.3 Postman介绍
从上面的案例中,也发现了个新的问题,就是我们测试后端方法时,还需要去写前端代码.这对我们来说,是一件麻烦又痛苦的事情.
随着互联网的发展,也随着项目难度的增加,企业也按照开发的功能,把人员拆分成了不同的团队.界面显示交给"前端开发工程师",业务逻辑的实现交给"后端开发工程师"后端开发工程师,不要求也不需要掌握前端技能了.那后端开发工程师,如何测试自己的程序呢?--使用专业的接口测试工具咱们课堂中使用的是Postman,接下来我们来学习Postman的使用.
2.3.1 下载安装Postman
2.3.2 创建请求

界面介绍

2.3.3 传参介绍
1. 传参介绍,也就是通过字符串来传参.
HTTP时,我们通过URL来访问互联网上某一个资源.

其中,字符串就是请求的参数.
2.form-data(完整表示为:multipart/form-data)
表单提交的数据,在 form 标签中加上 enctyped="multipart/form-data"
通常用于提交图片/文件.对应 Content-Type: multipart/form-data
3.x-www-form-urlencoded
form表单,对应 Content-Type: application/x-www-from-urlencoded

4.raw
可以上传任意格式的文本,可以上传test,json,xml,html等

2.4 请求
访问不同的路径,就是发送不同的请求,,在发送请求时,可能会带有一些参数,所以学习Spring的请求,主要是学习如何传递参数到后端以及后端如何接收.
传递参数,我们主要以浏览器和Postman形式:
后端开发人员无需过度关注如何传递参数,了解即可,实际开发中以Postman测试为主.比如餐厅的厨师,不关注用户是在店里下单,还是外卖平台下单,或者小程序下单,只需要知道如何接收订单,根据订单做出对应的菜看就可以了.
2.4.1 传递一个参数
接受单个参数,在Spring MVC中直接用方法中的参数就可以,比如:
@RequestMapping("/m1")
public String m1(String name){
return "接收参数name ="+name;
}
我们使用Postman发送URL
http://127.0.0.1:8080/m1?name=spring

可以看到:Spring MVC会根据方法的参数名,找到相应的参数,赋值给方法.
如果参数不一致是拿不到结果的.
比如

注意:
使用基本类型来接收参数时,参数必须传(除boolean类型),否则会报500错误.
类型不匹配时,会报400错误.


我们可以通过日志来查看错误.

对于包装类型,如果不传对应的参数,Spring对应接收到的参数是null
所以企业开发中,对于参数可能传空的情况,都使用包装类型,这样就不会报错.
2.4.2 传递多个参数
如何接收多个参数?
@RequestMapping("/m3")
public Object m3(String name,int age){
return name+age;
}
http://127.0.0.1:8080/m3?name=spring&age=11

2.4.3 传递对象
package com.example.demo;
public class Person {
String name;
int age;
String sex;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString(){
return name+"/"+age+"/"+sex;
}
}
@RequestMapping("m4")
public Person m4(Person p){
return p;
}
2.4.4 后端参数重命名(后端参数映射)
@RequestMapping("m5")
public String m5(@RequestPart("na") String name){
return name;
}
http://127.0.0.1:8080/m5?na=spring
如果我们传不是修改的名字

http://127.0.0.1:8080/m5?name=spring
就会响应失败.

日志提示:应该写成na.
@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"
}
@RequestMapping("/m5")
public Object m5(@RequestParam(value = "na",required = false) String name){
return name;
}
2.4.5 传递数组
Spring MVC 可以⾃动绑定数组参数的赋值
@RequestMapping("/m5")
public String method5(String[] arrayParam) {
return Arrays.toString(arrayParam);
}
@RequestMapping("/m6") public String m6(String[] str){ return Arrays.toString(str); }http://127.0.0.1:8080/m6?str=aaa&str=bbb



如果2c没有c只有2

2.4.6 传递集合
默认情况下,请求中参数名相同的多个值,是封装到数组. 如果要封装到集合,要使⽤@RequestParam 绑定参数关系
@RequestMapping("m7")
public List<String> m7(@RequestParam List<String> list){
return list;
}
2.4.7 传递JSON数据
类似于:• 国际通⽤语⾔-英语• 中国56个⺠族不同地区的通⽤语⾔-普通话有⾃⼰的语法, 其他语⾔也认识
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"
}, {
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": ["Million tonne punch", "Damage resistance", "Superhuman r
}, {
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": ["Immortality", "Heat Immunity", "Inferno", "Teleportation
}]
}
也可以压缩表⽰:
{"squadName":"Super hero squad","homeTown":"Metro
City","formed":2016,"secretBase":"Super tower","active":true,"members":
[{"name":"Molecule Man","age":29,"secretIdentity":"Dan Jukes","powers":
["Radiation resistance","Turning tiny","Radiation blast"]},{"name":"Madame
Uppercut","age":39,"secretIdentity":"Jane Wilson","powers":["Million tonne
punch","Damage resistance","Superhuman reflexes"]},{"name":"Eternal
Flame","age":1000000,"secretIdentity":"Unknown","powers":["Immortality","Heat
Immunity","Inferno","Teleportation","Interdimensional travel"]}]}
{ "name" : "admin" , "age" : 18 }[ "hello" , 3.1415 , "json" ][{ "name" : "admin" , "age" : 18 },{ "name" : "root" , "age" : 16 },{ "name" : " 张三 " , "age" : 20 }]
本质上是jackson-databind提供的功能, Spring MVC框架中已经把该⼯具包引⼊了进来, 咱们直接使 ⽤即可, 如果脱离Spring MVC使⽤, 需要引⼊相关依赖.< dependency >< groupId >com.fasterxml.jackson.core</ groupId >< artifactId >jackson-databind</ artifactId >< version >2.13.5</ version ></ dependency >JSON的转换⼯具包有很多, jackson-databind只是其中的⼀种
public class JSONUtils {
private static ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
Person person = new Person();
person.setId(5);
person.setName("zhangsan");
person.setPassword("123456");
//对象转为JSON字符串
String jsonStr = objectMapper.writeValueAsString(person);
System.out.println("JSON字符串为:"+jsonStr);
//JSON字符串转为对象
Person p = objectMapper.readValue(jsonStr,Person.class);
System.out.println("转换的对象id:"+p.getId()+",name:"+p.getName()+",passwo
}
}
RequestBody: 请求正⽂,意思是这个注解作⽤在请求正⽂的数据绑定,请求参数必须在写在请求正⽂中
后端实现:
@RequestMapping("m8")
public String m8( Person person){
return person.toString();
}

接收不到.
2.4.8 获取URL中参数@PathVariable
默认传递参数写在URL上,SpringMVC就可以获取到
@RequestMapping("m9/{id}/{name}")
public String m9(@PathVariable Integer id,@PathVariable("name")String usename){
return id+usename;
}


2.4.9 上传⽂件@RequestPart
后端代码实现:
2.4.10 获取Cookie/Session
"⽆状态" 的含义指的是:默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信, 和下次通信之间没有直接的联系
例如登陆⽹站成功后, 第⼆次访问的时候服务器就能知道该请求是否是已经登陆过了.
⽐如去医院挂号1. 看病之前先挂号. 挂号时候需要提供⾝份证号, 同时得到了⼀张 "就诊卡", 这个就诊卡就相当于患者的 "令牌".2. 后续去各个科室进⾏检查, 诊断, 开药等操作, 都不必再出⽰⾝份证了, 只要凭就诊卡即可识别出当前患者的⾝份.3. 看完病了之后, 不想要就诊卡了, 就可以注销这个卡. 此时患者的⾝份和就诊卡的关联就销毁了. (类似于⽹站的注销操作)4. ⼜来看病, 可以办⼀张新的就诊卡, 此时就得到了⼀个新的 "令牌"
理解Session
⽐如我们打客服电话每次打客服电话, 是⼀个会话. 挂断电话, 会话就结束了下次再打客服电话, ⼜是⼀个新的会话.如果我们⻓时间不说话, 没有新的请求, 会话也会结束.
SessionId 是由服务器⽣成的⼀个 "唯⼀性字符串", 从 Session 机制的⻆度来看, 这个唯⼀性字符串称 为 "SessionId". 但是站在整个登录流程中看待, 也可以把这个唯⼀性字符串称为 "token".上述例⼦中的令牌ID, 就可以看做是SessionId, 只不过令牌除了ID之外, 还会带⼀些其他信息, ⽐如时间, 签名等.

1. 当⽤⼾登陆的时候, 服务器在 Session 中新增⼀个新记录, 并把 sessionId返回给客⼾端. (通过 HTTP 响应中的 Set-Cookie 字段返回).2. 客⼾端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId. (通过 HTTP 请求中的 Cookie 字段带上).3. 服务器收到请求之后, 根据请求中的 sessionId在 Session 信息中获取到对应的⽤⼾信息, 再进⾏后 续操作.找不到则重新创建Session, 并把SessionID返回
Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失.
Cookie 和 Session 的区别
@RequestMapping("m11")
public String m11(HttpServletRequest request, HttpServletResponse response){
Cookie[] cookies = request.getCookies();
StringBuilder builder = new StringBuilder();
if(cookies != null){
for(Cookie ck:cookies){
builder.append(ck.getName()+":"+ck.getValue());
}
}
return "Cookie信息:"+builder;
}
Spring MVC是基于 Servlet API 构建的原始 Web 框架, 也是在Servlet的基础上实现的HttpServletRequest , HttpServletResponse 是Servlet提供的两个类, 是SpringMVC⽅法的内置对象. 需要时直接在⽅法中添加声明即可.HttpServletRequest 对象代表客⼾端的请求, 当客⼾端通过HTTP协议访问服务器时,HTTP请 求头中的所有信息都封装在这个对象中,通过这个对象提供的⽅法,可以获得客⼾端请求的所有信 息.HttpServletResponse 对象代表服务器的响应. HTTP响应的信息都在这个对象中, ⽐如向客⼾端发送的数据, 响应头, 状态码等. 通过这个对象提供的⽅法, 可以获得服务器响应的所有内容Spring MVC在这两个对象的基础上进⾏了封装, 给我们提供更加简单的使⽤⽅法.
从这个例⼦中, 也可以看出Cookie是可以伪造的, 也就是不安全的, 所以使⽤Cookie时, 后端需要进⾏ Cookie校验.
从这个例⼦中, 也可以看出Cookie是可以伪造的, 也就是不安全的, 所以使⽤Cookie时, 后端需要进⾏ Cookie校验.
@RequestMapping("m12")
public String m12(@CookieValue("bite") String b){
return "bite"+b;
}
@RequestMapping("m13")
public String m13(HttpServletRequest request){
HttpSession session = request.getSession();
if(session != null){
session.setAttribute("usename","java");
}
return "session 存储成功";
}
HttpSession getSession ( boolean create);HttpSession getSession ();
@RequestMapping("m14")
public String m14(HttpServletRequest request){
HttpSession session = request.getSession(false);
String usename = null;
if (session != null && session.getAttribute("usename") != null) {
usename = (String) session.getAttribute("usename");
}
return "usename+"+usename;
}
@RequestMapping("m16")
public String m16(@SessionAttribute(value = "usename",required = false)String usename){
return "usename:"+usename;
}
@RequestMapping("m17")
public String m17(HttpSession session){
String usename = (String)session.getAttribute("usename");
return "usename:"+usename;
}
2.4.11 获取Header
@RequestMapping("m15")
public String m15(HttpServletRequest request,HttpServletResponse response){
String userAgent = request.getHeader("User-Agent");
return userAgent+":";
}
@RequestMapping("m18")
public String m18(@RequestHeader("user-agent")String useer){
return useer;
}
2.5 响应
2.5.1 返回静态⻚⾯
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>我是静态页面</h1>
</body>
</html>

结果却发现,页面没有返回.而且我们这个访问方法也有问题.我们这样是直接通过浏览器访问这个,并不是通过 返回 得到的.
下面我们写后端代码,通过后端代码(服务器)返回我们之前写的.就是我们想要的页面了.
public String m19(){
return "/index.html";
}

但是这样也不是页面呀.
怎么回事呢??
我们把
就可以了.


2.5.2 返回数据@ResponseBody
我们上⾯讲到, @ResponseBody 表⽰返回数据
2.5.3 返回HTML代码⽚段
@RequestMapping("m20")
@ResponseBody
public String m20(){
return "<h1>Hello,HTML~</h1>";
}
2.5.5 设置状态码
@RequestMapping("m21")
public String m21(HttpServletResponse response){
response.setStatus(401);
return "设置状态码成功";
}
状态码不影响⻚⾯的展⽰
String[] consumes() default {};String[] produces() default {};
@RequestMapping(value = "m22",produces = "application/json")
public String m22(){
return "success";
}
@RequestMapping("m23")
public String m23(HttpServletResponse response){
response.setHeader("MyHead","MyHeadvalue");
return "设置成功";
}
3
3.1.1 约定前后端交互接⼝
请求路径: calc/sum请求⽅式: GET/POST接⼝描述:计算两个整数相加
请求参数:
参数名 类型 是否必须 备注num1 Integer 是 参与计算的第⼀个数num2 Integer 是 参与计算的第一个数
⽰例: num1=5&num2=3
Content-Type: text/html响应内容 : 计算机计算结果 : 8
3.1.2 服务器代码
@RequestMapping("m24")
public String m24(Integer num1,Integer num2){
Integer sum = num1+num2;
return "<h1>计算机计算结果: "+sum+"</h1>";
}
3.2.1 约定前后端交互接⼝
请求路径: /user/login请求⽅式: POST接⼝描述:校验账号密码是否正确
请求参数:
参数名 类型 是否必须 备注userName String 是 校验的账号password String 是 校验的密码
@RequestMapping("/login")
public boolean login(String userName, String password, HttpSession session) {
//账号或密码为空
if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return false;
}
//校验账号密码是否正确
//理论上应该从数据库中获取账号密码, 校验是否正确, 但当前还没讲数据库的操作, 先把账
if (!"zhangsan".equals(userName) || !"123456".equals(password)) {
return false;
}
//密码验证成功, 把⽤⼾名存储在Session中
session.setAttribute("userName", userName);
return true;
}
@RequestMapping("/getLoginUser")
public String getLoginUser(HttpSession session){
//从Session中获取⽤⼾登录信息
String userName = (String)session.getAttribute("userName");
//如果⽤⼾已经登录, 则直接返回⽤⼾登录
if (StringUtils.hasLength(userName)){
return userName;
}
return "";
}
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></scr
<script>
$.ajax({
type: "get",
url: "/user/getLoginUser",
success: function (result) {
$("#loginUser").text(result);
}
});
</script>
3.2.2 更快捷的引入依赖
4.应⽤分层
⼆者其实是从不同⻆度对软件⼯程进⾏了抽象.MVC模式强调数据和视图分离, 将数据展⽰和数据处理分开, 通过控制器对两者进⾏组合.三层架构强调不同维度数据处理的⾼内聚和低耦合, 将交互界⾯, 业务处理和数据库操作的逻辑分开.⻆度不同也就谈不上互相替代了,在⽇常的开发中可以经常看到两种共存的情况,⽐如我们设计模型层的时候往往也会拆分出业务逻辑层(Service层)和数据访问层(Dao层)。但是⼆者的⽬的是相同的, 都是"解耦,分层,代码复⽤"
软件设计原则:⾼内聚低耦合.⾼内聚指的是:⼀个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联 系程度越⾼,则内聚性越⾼,即 "⾼内聚"。低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。修改⼀处代码, 其他模块的代码 改动越少越好.
⾼内聚低耦合⽭盾吗?不⽭盾, ⾼内聚指的是⼀个模块中各个元素之间的联系的紧密程度, 低耦合指的是各个模块之间的紧 密程度.这就好⽐⼀个企业, 包含很多部⻔, 各个部⻔之间的关联关系要尽可能的⼩, ⼀个部⻔发⽣问题, 要尽 可能对降低对其他部⻔的影响, 就是耦合. 但是部⻔内部员⼯关系要尽量紧密, 遇到问题⼀起解决.
⼩驼峰: 除了第⼀个单词,其他单词⾸字⺟⼤写,⽐如: userController
5.总结
1. 学习Spring MVC, 其实就是学习各种Web开发需要⽤的到注解
2.
Cookie 和Session都是会话机制, Cookie是客⼾端机制, Session是服务端机制. ⼆者通过SessionId



8614

被折叠的 条评论
为什么被折叠?



