【SpringBoot】从零开始全面解析SpringMVC (二)

本篇博客给大家带来的是SpringBoot的知识点, 本篇是SpringBoot入门, 介绍SpringMVC相关知识.
🐎文章专栏: JavaEE进阶
🚀若有问题 评论区见
👉gitee链接: 薯条不要番茄酱
❤ 欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

王子,公主请阅🚀

  • 要开心
  • [1. 关于SpringMVC 请求传递信息](#1. 关于SpringMVC 请求传递信息)
    • [1.1 传递 JSON 数据](#1.1 传递 JSON 数据)
    • [1.2 获取URL中参数(@PathVariable)](#1.2 获取URL中参数(@PathVariable))
    • [1.3 上传文件 (@RequestPart)](#1.3 上传文件 (@RequestPart))
    • [1.4 Cookie 和 Seesion](#1.4 Cookie 和 Seesion)
      • [1.4.1 回顾 Cookie, 理解 Session](#1.4.1 回顾 Cookie, 理解 Session)
      • [1.4.2 获取Cookie 和 Session](#1.4.2 获取Cookie 和 Session)
    • [1.5 获取Header](#1.5 获取Header)
  • [2. 关于SpringMVC 响应传递信息](#2. 关于SpringMVC 响应传递信息)
    • [2.1 返回静态页面](#2.1 返回静态页面)
    • [2.2 返回数据@ResponseBody](#2.2 返回数据@ResponseBody)
    • [2.3 返回HTML代码片段](#2.3 返回HTML代码片段)
    • [2.4 返回JSON数据](#2.4 返回JSON数据)
    • [2.5 设置状态码](#2.5 设置状态码)
    • [2.6 设置Header(了解即可)](#2.6 设置Header(了解即可))
  • [3. 综合练习](#3. 综合练习)
    • [3.1 加法计算器](#3.1 加法计算器)
      • [3.1.1 准备工作](#3.1.1 准备工作)
      • [3.1.2 约定前后端交互接口](#3.1.2 约定前后端交互接口)
      • [3.1.3 服务器代码](#3.1.3 服务器代码)
      • [3.1.4 前端页面代码](#3.1.4 前端页面代码)
      • [3.1.5 运行测试](#3.1.5 运行测试)
    • [3.2 用户登录](#3.2 用户登录)
      • [3.2.1 准备工作](#3.2.1 准备工作)
      • [3.2.2 约定前后端交互接口](#3.2.2 约定前后端交互接口)
      • [3.2.3 实现服务器端代码](#3.2.3 实现服务器端代码)
      • [3.2.4 前端页面代码](#3.2.4 前端页面代码)

要开心

要快乐

顺便进步

1. 关于SpringMVC 请求传递信息

1.1 传递 JSON 数据

Ⅰ JSON概念

JSON:JavaScript Object Notation 【JavaScript 对象表示法】

JSON是一种轻量级的数据交互格式. 它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。-- 来自百度百科

简单来说:JSON就是一种数据格式, 有自己的格式和语法, 使用文本表示一个对象或数组的信息,
因此JSON本质是字符串. 主要负责在不同的语言中数据传递和交换.

类似于:
① 国际通用语言 - 英语.
② 中国56个民族不同地区的通用语言 - 普通话.

Ⅱ JSON 与 Javascript 的关系

没有关系, 只是语法相似, js开发者能更快的上手而已, 但是他的语法本身比较简单, 所以也很好学.

Ⅲ JSON语法

JSON 是一个字符串,其格式非常类似于 JavaScript 对象字面量的格式.

java 复制代码
{
 "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
 }]
}

Ⅳ JSON的语法:

  1. 数据在 键值对(Key/Value) 中.
  2. 数据由逗号 ' , ' 分隔.
  3. 对象用 {} 表示.
  4. 数组用 [] 表示.
  5. 值可以为对象, 也可以为数组, 数组中可以包含多个对象.

Ⅴ JSON的两种结构

  1. 对象: 大括号 {} 保存的对象是一个无序的 键值对 集合. 一个对象以左括号 { 开始, 右括号 } 结束。每个"键"后跟⼀个冒号 : 键值对使用逗号 , 分隔开。

  2. 数组: 中括号 [] 保存的数组是值(value)的有序集合. 一个数组以左中括号 [ 开始, 右中括
    号 ] 结束,值之间使用逗号 , 分隔。

可以使用在线JSON格式化工具来进行校验和书写: JSON校验工具 .

Ⅵ JSON优点

  1. 简单易用: 语法简单,易于理解和编写,可以快速地进行数据交换.

  2. 跨平台支持: JSON可以被多种编程语言解析和生成, 可以在不同的平台和语言之间进行数据交换和传输.

  3. 轻量级: 相较于XML格式, JSON数据格式更加轻量级, 传输数据时占用带宽较小, 可以提高数据传输速度.

  4. 易于扩展: JSON的数据结构灵活,支持嵌套对象和数组等复杂的数据结构,便于扩展和使用.

  5. 安全性: JSON数据格式是一种纯文本格式,不包含可执行代码, 不会执行恶意代码,因此具有较高的安全性.

Ⅶ 传递 JSON 数据

接收 JSON 对象,需要使用 @RequestBody 注解

RequestBody: 请求正文,意思是这个注解作用在请求正文的数据绑定,请求参数必须在写在请求正文中.

使用 Postman 来发送 json 请求参数:

如果去掉@RequestBody 注解的话会怎么样? 👇

可以看到, 后端未能成功给 Student对象赋值.

1.2 获取URL中参数(@PathVariable)

path variable: 路径变量.
和字面表达的意思一样, 这个注解主要作用在请求URL路径上的数据绑定.

后端实现代码:

java 复制代码
 @RequestMapping("/r9/{articleId}")
    public String r9(@PathVariable("articleId") Integer articleId) {
        return "接到参数,articleId:"+articleId;
    }

使用Postman 发送请求:

可以看到, 后端正确获取到了URL中的参数.

1.3 上传文件 (@RequestPart)

直接看后端代码实现:

java 复制代码
    @RequestMapping("/r10")
    public String r10(@RequestPart("file")MultipartFile imgFile) {
        String originalFilename = imgFile.getOriginalFilename();
        return "接到文件,文件名称:"+originalFilename;
    }

使用 Postman 发送请求:

文件上传成功.

1.4.1 回顾 Cookie, 理解 Session

Ⅰ 回顾 Cookie

HTTP 协议 自身 是属于 "无状态" 协议.
"无状态" 的含义指的是:默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系.

实际开发中, 我们很多时候是需要知道请求之间的关联关系的.
例如登陆网站成功后, 第二次访问的时候服务器就能知道该请求是否是已经登陆过了.

上图过程中的令牌通常就存储在Cookie 子段当中. 此时在服务器这边就需要记录"令牌"信息, 以及令牌对应的用户信息, 这个就是 session 机制所做的工作.

比如去医院挂号 :

  1. 看病之前先挂号. 挂号时候需要提供身份证号, 同时得到了一张 "就诊卡", 这个就诊卡就相当于患者的 "令牌".
  2. 后续去各个科室进行检查, 诊断, 开药等操作, 都不必再出示身份证了, 只要凭就诊卡即可识别出当前患者的身份.
  3. 看完病了之后, 不想要就诊卡了, 就可以注销这个卡. 此时患者的身份和就诊卡的关联就销毁了. (类似于网站的注销操作).
  4. 又来看病, 可以办一张新的就诊卡, 此时就得到了一个新的 "令牌".

Ⅱ 理解 Session (会话)

服务器同一时刻收到的请求是很多的. 服务器需要清楚的区分每个请求是从属于哪个用户, 也就是属于哪个会话, 就需要在服务器这边记录每个会话以及与用户的信息的对应关系. Session是服务器为了保存用户信息而创建的一个特殊的对象.

Session 的本质就是一个 "哈希表", 存储了一些键值对结构. Key 就是SessionID, Value 就是用户信息.
SessionId 是由服务器生成的一个 "唯一性字符串", 这就是Session能够区分同一请求不同用户信息的根本原因.

Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失.

Ⅲ Cookie 和 Session 的区别

① Cookie 是客户端保存用户信息的一种机制. Session 是服务器端保存用户信息的一种机制.
② Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId 是 Cookie 和 Session 之间的桥梁.
③ Cookie 和 Session 经常会在一起配合使用. 但 不是必须配合.

  1. 完全可以用 Cookie 来保存一些数据在客户端. 这些数据不一定是用户身份信息, 也不一定是 SessionId.
  2. Session 中的 sessionId 也不需要非得通过 Cookie/Set-Cookie 传递, 比如通过URL传递.

1.4.2 获取Cookie 和 Session

Ⅰ 获取Cookie

代码如下:

java 复制代码
    @RequestMapping("/r11")
    public String r11(HttpServletRequest request, HttpServletResponse response) {
        //获取所有 cookie信息
        Cookie[] cookies = request.getCookies();
        //打印 cookie信息
        if(cookies != null) {
            Arrays.stream(cookies).forEach(ck -> System.out.println(ck.getName()+":"+ck.getValue()));
        }
        return "获取cookies成功";
    }

此时没有设置Cookie, 通过浏览器访问: http://127.0.0.1:8080/req/r11, 得到 Cookie为 null . 按下图提示设置 Cookie 👇

设置完成之后再次访问就可以在IDEA 上看到 刚刚设置的Cookie. 👇

Ⅱ 简洁获取Cookie的方式, 使用@CookieValue注解

java 复制代码
    @RequestMapping("/r12")
    public String r12(String ck) {
        return "cookie: " + ck;
    }

Ⅲ 获取 Session 的方式

Session是服务器端的机制, 我们需要先存储, 才能再获取.
Session 也是基于HttpServletRequest 来存储和获取的.

java 复制代码
//设置Session
    @RequestMapping("/setSess")
    public String r13(HttpServletRequest request) {
        //获取 Session 对象
        HttpSession session = request.getSession();
        if(session != null) {
            session.setAttribute("username","大帅锅");
        }
        return "session 存储成功";
    }

    //获取Session
    @RequestMapping("/getSess")
    public String r14(HttpServletRequest request) {
        //如果 session 不存在, 不会⾃动创建
        HttpSession session = request.getSession(false);
        String username = null;
        if(session != null && session.getAttribute("username") != null) {
            username = (String)session.getAttribute("username");
        }
        return "username: " + username;
    }

先设置Session

再获取 Session

上述获取Session 的方式有点麻烦, 有没有跟简单点的?

其实就是对 HttpServletRequest获取session的方法进行封装就能通过 HttpSession 直接获取.

java 复制代码
    @RequestMapping("/getSess2")
    public String r15(HttpSession session) {
        String name = (String)session.getAttribute("username");
        return "从session中获取username: " + name;
    }

还是一样的, 先设置 Session 再获取 Session,下面只给出获取的截图👇:

为了图方便, 能不能把上述获取Session的方式进一步封装成注解? 实际上也确实有这种方式:

java 复制代码
    @RequestMapping("/getSess3")
    public String r16(@SessionAttribute("username") String name) {
        return "从Session中获取name: "+name;
    }

通过注解 @SessionAttribute就可以获取 Session 中的任意信息.

1.5 获取Header

Ⅰ 传统获取 header

从 HttpServletRequest 中获取

java 复制代码
    //获取header.
    @RequestMapping("/r8")
    public String r8(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        return "userAgent: "+userAgent;
    }

使用浏览器访问后,得到如下:👇

通过Fiddler观察, 获取的 User-Agent 是否正确。

Ⅱ 使用@RequestHeader注解 简洁获取Header

java 复制代码
    @RequestMapping("/r9")
    public String r9(@RequestHeader("User-Agent") String userAgent) {
        return userAgent;
    }

2. 关于SpringMVC 响应传递信息

2.1 返回静态页面

创建前端页面 index.html(注意是在resources/static 目录下)。

前端页面代码如下:

java 复制代码
<!doctype html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>用户登录首页</title>
</head>

<body>
	我是Index页面。
</body>

</html>

后端代码:

java 复制代码
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/res")
@RestController
public class ResponseController {
    @RequestMapping("/index")
    public String index() {
        return "/index.html";
    }
}

使用浏览器访问 http://127.0.0.1:8080/res/index

发现, 页面未正确返回, http 响应把 "/index.html" 当做了 http响应正文的数据。
那Spring MVC如何才能识别出来 index.html 是一个静态页面, 并进行返回呢?

把 @RestController 改为 @Controller

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("/res")
@Controller
public class ResponseController {
    @RequestMapping("/index")
    public String index() {
        return "/index.html";
    }
}

再次访问 http://127.0.0.1:8080/res/index

发现页面正确展示了。

@RestController 和 @Controller 有着什么样的关联和区别呢? 我们来看@RestController 的源码 👇

java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

不难发现有如下关系:
@RestController = @Controller + @ResponseBody。

@RestController 返回的是数据 。 @Controller 返回的是视图(页面)。可以推出@ResponseBody返回的是数据。

2.2 返回数据@ResponseBody

上面讲到, @ResponseBody 表示返回数据。我们来验证下:

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@RequestMapping("/res")
@Controller
@ResponseBody
public class ResponseController {
    @RequestMapping("/index")
    public String index() {
        return "/index.html";
    }
}

加上 @ResponseBody 该方法就会把"/index.html" 当做一个数据返回给前端。

@ResponseBody 既是类注解, 又是方法注解。
如果作用在类上, 表示该类的所有方法, 返回的都是数据, 如果作用在方法上, 表示该方法返回的是数据。

2.3 返回HTML代码片段

后端返回数据时, 如果数据中有HTML代码, 也会被浏览器解析。

java 复制代码
    @RequestMapping("/returnHtml")
    @ResponseBody
    public String html() {
        return "<h1>Hello,HTML</h1>";
    }

响应结果👇:


通过Fiddler抓包,观察响应结果:

响应中的 Content-Type 常见取值有以下几种:

①text/html : body 数据格式是 HTML。
② text/css : body 数据格式是 CSS。
③ application/javascript : body 数据格式是 JavaScript。
④ application/json : body 数据格式是 JSON。

2.4 返回JSON数据

Spring MVC 也可以返回JSON

java 复制代码
    @RequestMapping("/returnJson")
    @ResponseBody
    public HashMap<String,String> returnJson() {
        HashMap<String,String> map = new HashMap<>();
        map.put("糯香:","柠檬茶");
        map.put("牢大: ","坠机");
        map.put("ikun炒粉: ","放鸡精");
        return map;
    }

运行程序,浏览器响应结果如下:

通过 Fiddler 观察响应结果, Content-Type 为 application/json.

2.5 设置状态码

Spring MVC会根据我们方法的返回结果自动设置响应状态码, 程序员也可以手动指定状态码.
通过Spring MVC的内置对象HttpServletResponse 提供的方法来进行设置.

java 复制代码
    @RequestMapping("/setStatus")
    @ResponseBody
    public String setStatus(HttpServletResponse response) {
        response.setStatus(403);
        return "设置状态码成功";
    }

通过Fiddler 抓包来观察设置的结果:

2.6 设置Header(了解即可)

Http 响应报头也会向客户端传递一些附加信息, 比如服务程序的名称,请求的资源已移动到新地址等, 如:
Content-Type, Local等.

3. 综合练习

结合上述内容, 我们可以做一些小案例: 重在理解前后端交互的实现.

3.1 加法计算器

需求: 输入两个整数, 点击 "点击相加" 按钮 就显示计算结果.

3.1.1 准备工作

gitee链接: 薯条不要番茄酱
从我的码云上直接下载spring-mvc文件夹, 并把 calc.html 复制到static 目录下👇

3.1.2 约定前后端交互接口

接口设计:

  1. 接收什么是看后端完成这个功能需要什么.
  2. 返回什么是看前端页面展示需要什么.

对于此练习:
路径: calc/sum
参数: num1 和 num2
返回值: sum

3.1.3 服务器代码

java 复制代码
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/calc")
@RestController
public class CalcController {
    @RequestMapping("/sum")
    public String sum(Integer num1,Integer num2) {
        Integer sum = num1+num2;
        return "<h1>计算结果: "+sum+"</h1>";
    }
}

3.1.4 前端页面代码

java 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
     <form action="calc/sum" method="post">
        <h1>计算器</h1>
        数字1:<input name="num1" type="text"><br>
        数字2:<input name="num2" type="text"><br>
        <input type="submit" value=" 点击相加 ">
    </form>
</body>

</html>

3.1.5 运行测试

注意点: 前端接收的参数为num1 和 num2, 那么后端方法参数也必须是 num1 和 num2.(顺序无所谓,名字必须和前端保持一致

3.2 用户登录

需求: 用户输入账号和密码, 后端进行校验密码是否正确.

  1. 如果不正确, 前端进行用户告知.
  2. 如果正确, 跳转到首页. 首页显示当前登录用户.
  3. 后续再访问首页, 可以获取到登录用户信息.

3.2.1 准备工作

gitee链接: 薯条不要番茄酱

还是一样的前端页面代码直接在我的码云上拿就可以了.

3.2.2 约定前后端交互接口

用户登录:
url: /user/login
参数: userName,password
返回结果: true / false

获取当前登录用户:
url: /user/index
参数: 无
返回结果: userName

3.2.3 实现服务器端代码

java 复制代码
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;

@RequestMapping("/user")
@RestController
public class LoginController {
    @RequestMapping("/login")
    public Boolean login(String userName, String password, HttpSession session) {
        //校验用户名和密码是否为空
//        if(userName == null || userName.length() == 0 || password == null || password.length() == 0) {
//            return false;
//        }

        //上面代码可以改写成 ↓, 可以点过去看 hasLength 的源码,其实和上面的代码是类似的
        if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
            return false;
        }

        //注意接下来的写法, 由于已经对 userName进行判断处理, 所以userName不会为null.
        //还没学 mybatis, 所以只能先把用户名和密码写死了, 后面学了再改.
        if("admin".equals(userName) && "admin".equals(password)) {
            //从需求上看: 如果成功登录就需要 获取用户名,
            // 所以这里得先设置 session, 将用户名放到session中, 后面得方法通过session 来获取.
            session.setAttribute("userName",userName);
            return true;
        }
        return false;
    }
    @RequestMapping("/index")
    public String getUserName(@SessionAttribute("userName") String userName) {
        return userName;
    }
}

3.2.4 前端页面代码

login.html :

java 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>登录页面</title>
</head>

<body>
  <h1>用户登录</h1>
  用户名:<input name="userName1" type="text" id="userName"><br>
  密码:<input name="password1" type="password" id="password"><br>
  <input type="button" value="登录" onclick="login()">
  
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
  <script>
  //下列代码需要自己写,ajax必须要会:
    function login() {
      $.ajax({
        url:"/user/login",
        type:"post",
        data: {
          userName: $("#userName").val(),
          password: $("#password").val()
        },
        //http响应成功
        success:function(result) {
          if(result==true) {
            //页面跳转
            location.href = "index.html";
            //也可以写成
            // location.assign("index.html");
          }else {
            alert("密码错误");
          }
        }
        });
      }

  </script>
</body>

</html>

index.html :

java 复制代码
<!doctype html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>用户登录首页</title>
</head>

<body>
    登录人: <span id="loginUser"></span>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script>
     //下列代码需要自己写,ajax必须要会:
        $.ajax({
            url:"/user/index",
            type:"get",
            success:function(loginName) {
                $("#loginUser").text(loginName);
            }
        });
    </script>
</body>

</html>

本篇博客到这里就结束啦, 感谢观看 ❤❤❤
🐎期待与你的下一次相遇😊😊😊

相关推荐
书源丶10 分钟前
三十六、File 类与 IO 流基础——文件操作的「第一步」
java
刀法如飞15 分钟前
Go数组去重的20种实现方式,AI时代解决问题的不同思路
后端·算法·go
AI人工智能+电脑小能手43 分钟前
【大白话说Java面试题】【Java基础篇】第30题:JDK动态代理和CGLIB动态代理有什么区别
java·开发语言·后端·面试·代理模式
swipe1 小时前
别再把 AI 聊天做成纯文本:从 agui 这个前后端项目,拆解“可感知工具调用”的流式 AI UI
后端·langchain·llm
GetcharZp1 小时前
GitHub 爆火!纯 Go 编写的文件同步神器 Syncthing,凭什么成为程序员的标配?
后端
hERS EOUS1 小时前
SpringBoot 使用 spring.profiles.active 来区分不同环境配置
spring boot·后端·spring
DFT计算杂谈1 小时前
wannier90 参数详解大全
java·前端·css·html·css3
LucianaiB1 小时前
我用飞书多维表做了一个 AI 活动推荐智能体:每天自动催我别错过截止日期!
后端
marsh02061 小时前
43 openclaw熔断与降级:保障系统在异常情况下的可用性
java·运维·网络·ai·编程·技术
张健11564096481 小时前
临界区和同一线程上锁
java·开发语言·jvm