3.SpringMVC程序开发

发布于:2024-04-11 ⋅ 阅读:(138) ⋅ 点赞:(0)

文章目录

大家好,我是晓星航。今天为大家带来的是 SpringMVC程序开发 相关的讲解!😀

1.什么是 Spring MVC?

官⽅对于 Spring 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 ), but it is more commonly known as “Spring MVC”.

引⽤来⾃: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#spring-web

翻译为中⽂:

Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来自其源模块的名称(Spring-webmvc),但它通常被称为“Spring MVC”

从上述定义我们可以得出两个关键信息:

  1. Spring MVC 是⼀个 Web 框架。
  2. Spring MVC 是基于 Servlet API 构建的。

然⽽要真正的理解什么是 Spring MVC?我们⾸先要搞清楚什么是 MVC?

SpringBoot 是创建Spring MVC项目的一种方式而已。

当前阶段,MVC的概念又发生了一些变化,后端开发人员不涉及前端页面的开发,所以
也就没有view层

所以view 又有了一层解释
之前返回的是视图,现在返回的是视图所需要的数据

image-20240305180651717

1.1 MVC 定义

MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。

image-20240305180226830

  • **Model(模型)**是应⽤程序中⽤于处理应⽤程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
  • **View(视图)**是应⽤程序中处理数据显示的部分。通常视图是依据模型数据创建的。
  • **Controller(控制器)**是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据, 控制⽤户输⼊,并向模型发送数据。

1.2 MVC 和 Spring MVC 的关系

MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。

总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求。

2.为什么要学 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 框架,如下图所示:

image-20240304210441578

简单来说,咱们之所以要学习 Spring MVC 是因为它是⼀切项⽬的基础,我们以后创建的所有 Spring、Spring Boot 项⽬基本都是基于 Spring MVC 的。

3.怎么学 Spring MVC?

学习 Spring MVC 我们只需要掌握以下 3 个功能:

  1. **连接的功能:**将⽤户(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的 Spring 程序。
  2. **获取(请求)参数的功能:**⽤户访问的时候会带⼀些参数,在程序中要想办法获取到参数。
  3. **输出(响应)数据的功能:**执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤户。

对于 Spring MVC 来说,掌握了以上 3 个功能就相当于掌握了 Spring MVC。

请求,主要学习如何传递参数

1.传递单个参数
2.多个参数
3.对象
4.数组/集合…

3.1 Spring MVC 创建和连接

Spring MVC 项⽬创建和 Spring Boot 创建项⽬相同(Spring MVC 使⽤ Spring Boot 的⽅式创建), 在创建的时候选择 Spring Web 就相当于创建了 Spring MVC 的项⽬。

在 Spring MVC 中使⽤ @RequestMapping 来实现 URL 路由映射,也就是浏览器连接程序的作⽤。 接下来要实现的功能是访问地址:
http://127.0.0.1:8080/sayhello,能打印“hello,spring mvc”信息。

image-20240305180937233

路径也可以写多层

image-20240305181515674

此时我们访问sayhello就要先访问hello再访问sayhello文件夹

image-20240305181534591

使用postman进行访问

image-20240305181954940

@RequestMapping 支持get和post

3.1.1 创建 Spring MVC 项⽬

Spring MVC 可以基于 Spring Boot 创建,也就是创建⼀个 Spring Boot 项⽬,勾选上 Spring Web 模块即可,如下图所示:

image-20240304210441578

接下来,创建⼀个 UserController 类,实现⽤户到 Spring 程序的互联互通,具体实现代码如下:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller // 让 spring 框架启动时,加载
@ResponseBody // 返回⾮⻚⾯数据
@RequestMapping("/user") // 路由器规则注册
public class UserController {
 // 路由器规则注册
 @RequestMapping("/hi")
 public String sayHi(){
 return "<h1>Hi,Spring MVC.</h1>";
 }
}

这样实现之后,当访问地址:http://localhost:8080/user/hi 时就能打印“hello,spring mvc”的信息了。

3.1.2 @RequestMapping 注解介绍

@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路 由映射的。

路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类 的某个⽅法的过程就叫路由映射。

@RequestMapping 基础使⽤:

import com.example.demo.model.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@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 也可以直接修饰⽅法,代码实现如下:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody // 定义返回的数据格式为⾮视图(text/html)
public class UserController {
 @RequestMapping("/hi")
 public String sayHi(){
 return "<h1>Hi,Spring MVC.</h1>";
 }
}

⽅法地址:http://localhost:8080/hi

3.1.3 @RequestMapping 是 post 还是 get 请求?

使⽤ PostMan 测试⼀下,默认情况下使⽤注解 @RequestMapping 是否可以接收 GET 或 POST 请 求?

指定 GET/POST ⽅法类型

我们可以显示的指定 @RequestMapping 来接收 POST 的情况,如下所示:

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;
@Controller
@ResponseBody // 定义返回的数据格式为⾮⻚⾯
public class UserController {
 @RequestMapping(value = "/hi",method= RequestMethod.POST)
 public String sayHi(){
 return "<h1>Hi,Spring MVC.</h1>";
 }
}

只允许POST请求结果图:

image-20240305183230474 image-20240305183214798

如果我们更改代码指定为只允许GET请求:

image-20240305183130142 image-20240305183142403

3.1.4 @GetMapping 和 PostMapping

get 请求的 3 种写法:

// 写法1
@RequestMapping("/index")
// 写法2
@RequestMapping(value = "/index",method = RequestMethod.GET)
// 写法3
@GetMapping("/index")

post 请求的 2 种写法:

// 写法1
@RequestMapping(value = "/index",method = RequestMethod.POST)
// 写法2
@PostMapping("/index")

3.2 获取参数

3.2.1 传递单个参数

在 Spring MVC 中可以直接⽤⽅法中的参数来实现传参,⽐如以下代码:

@RequestMapping("/param")
@RestController
public class ParamController {
    public String m1(String name) {
        return "接收到的参数name:" + name;
    }
}

在 postman 中访问⽅法:

image-20240305184136414

需要名称一样

底层逻辑:从请求的参数中,获取参数名为name的值,并给name赋值

3.2.2传递多个参数

@RequestMapping("/param")
@RestController
public class ParamController {
    @RequestMapping("/m1")
    public String m1(String name) {
        return "接收到的参数name:" + name;
    }
    @RequestMapping("/m2")
    public String m2(String name,Integer age) {
        return "接收到的参数name:" + name + "age:" + age;
    }
}

在 postman 中访问⽅法:

image-20240305184744979

直接发送,参数的顺序可以调换

我们再看一个例子,这里我们将int的包装类型(Integer)换成int会发生什么呢?

image-20240305185206437

image-20240305185519211

可以看到,我们再运行启动这段代码时发什么错误,错误信息大致为age这个变量不是包装类,返回类型为null时会发生错误

如果使用基本类型,必须要传值,不传值会报错。

所以开发时,建议使用包装类,比如Integer,可以区分0和null。

3.2.3 传递对象

并且 Spring MVC 可以⾃动实现参数对象的赋值,⽐如 Person 对象:

import lombok.Data;
@Data
public class Person {
 private int id;
 private String name;
 private String password;
}

传递对象代码实现:

    @RequestMapping("/m4")
    public String m4(Person person) {
        return "接收到的参数name:" +person.toString();
    }

前端访问:

image-20240305190422245

开发中,接口的参数通常定义为对象。

3.2.4 表单参数传递/传递多个参数(⾮对象)

@RequestMapping("/m3")
public Object method_3(String name, String pwd) {
 System.out.println("name 参数:" + name);
 System.out.println("pwd 参数:" + pwd);
 return "/index.html";
}

重要说明:当有多个参数时,前后端进⾏参数匹配时,是以参数的名称进⾏匹配的,因此参数的位置 是不影响后端获取参数的结果。

3.2.5 后端参数重命名(后端参数映射)

某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,⽐如前端传递了⼀个 time 给后端,⽽后端⼜是有 createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现 这种情况,我们就可以使⽤ @RequestParam 来重命名前后端的参数值。

具体示例如下,后端实现代码:

    @RequestMapping("/m4")
    public String m4(@RequestParam("name") String username) {
        return "接收到的参数name:" + username;
    }

前端访问地址:

image-20240305191304374

我们这里结果发生错误是因为我们前端给的参数为name,而我们此时使用的参数时username因为会报错400

image-20240305191435217

在idea的报错信息亦是如此,name通过方法parameter命名为类型的值不存在

image-20240305193232978

如果此时我们修改一下,将参数name传过去,那么此时我们的username就可以接收到参数值了

3.2.6 设置参数必传@RequestParam

上⾯的列⼦,如果我们是前端传递⼀个⾮ time 的参数,就会出现程序报错的情况,如下图所示:

image-20240305191528935

这是因为后端已经声明了前端必须传递⼀个 time 的参数,但是前端没有给后端传递,我们查看 @RequestParam 注解的实现细节就可以发现端倪,注解实现如下:

image-20240305191740277

⾮必传参数设置

如果我们的实际业务前端的参数是⼀个⾮必传的参数,我们可以通过设置 @RequestParam 中的 required=false 来避免不传递时报错,具体实现如下:

@RequestMapping("/m4")
public Object method_4(@RequestParam(value = "time", required = false) Stri
ng createtime) {
 System.out.println("时间:" + createtime);
 return "/index.html";
}

postman查看结果:

image-20240305193057384

此时我们传输的结果就不会报错,而转为给一个默认值null

3.2.7 @RequestBody 接收JSON对象

image-20240305200748681

后端接收代码:

@RequestMapping(value = "/m5", method = RequestMethod.POST)
public Object method_5(@RequestBody Person person) {
 System.out.println("Person:" + person);
 return "redirect:/index.html";
}

尝试去除掉 @RequestBody 试试。

3.2.8 获取URL中参数@PathVariable

获取单个URL

@RequestMapping("/m9/{userId}")
public String m9(@PathVariable Integer userId) {
    return "usrId:" + userId;
}

注意此时参数要加 @PathVariable 我们才能成功获取到URL

前端⽅法地址:

image-20240306154235345

获取多个值:

@RequestMapping("/m9/{userId}/{name}")
public String m9(@PathVariable Integer userId,@PathVariable String name) {
    return "usrId:" + userId + ",name:" + name;
}

注意此时参数要加 @PathVariable 我们才能成功获取到URL

前端⽅法地址:

image-20240306154520433

重命名后:

image-20240306155026274

要求:主文件夹里的name和下面重命名前括号中的name要一致,@PathVariable 后面为重命名后的变量名

image-20240306155148781

3.2.9 上传⽂件@RequestPart

    @RequestMapping("/m10")
    public String m10(@RequestPart MultipartFile file) {
        System.out.println(file.getOriginalFilename());
        return "success";
    }

postman传输结果:

image-20240306155708432

idea中控制台也打印了刚才上传图片的信息:

image-20240306155817218

将上传图片保存到电脑本地:

image-20240306160309195

我们新增的代码便会将我们选中的文件上传并保存到我们本地的 D:/EDU/

image-20240306160424104

注意此时的EDU文件夹下之后两个文件

image-20240306160552523

我们选择cat.webp并通过postman发送给m10响应,然后找到EDU文件夹查看

image-20240306160527259

此时我们发现EDU文件夹下多了一个cat.webp文件,证明我们文件上传保存到本地已成功实现

获取项⽬⽬录的⼏种⽅式:

ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX).getPath();
new ClassPathResource("").getFile().getAbsolutePath();
ClassUtils.getDefaultClassLoader().getResource("").getPath();
ResourceUtils.getFile("classpath:static/").getPath();

练习:上传⼀个图⽚到本机的某个⽬录。

3.2.10 获取Cookie/Session/header

Cookie和Session的区别:

  • Cookie客户端保存用户信息的一种机制,Session服务器端保存用户信息的一种机制,
  • Cookie 和 Session之间主要是通过 Sessionld 关联起来的, SessionId 是 Cookie 和 ession 之间的桥梁
  • Cookie 和 Session 经常会在一起配合使用,但是不是必须配合.
    • 完全可以用 Cookie 来保存一些数据在客户端,这些数据不一定是用户身份信息,也不一定是Sessionld
    • Session 中的sessionId 也不需要非得通过 Cookie/Set-Cookie 传递比如通过URL传递

获取 Request 和 Response 对象

image-20240306162645533

@RequestMapping("/getCookie")
public String getCookie(HttpServletRequest request, HttpServletResponse response) {
    Cookie[] cookies = request.getCookies();
//        for (Cookie cookie : cookies) {
//            System.out.println(cookie.getName()+":"+cookie.getValue());
//        }
    if (cookies != null) {
        Arrays.stream(cookies).forEach(cookie -> {
            System.out.println(cookie.getName()+":"+cookie.getValue());
        });
    }
    return "获取cookie成功";
}
image-20240306164239172

获取session

何为session?

答:在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。

image-20240306164256278

第一种获取方法:

@RequestMapping("/getSession")
public String getSession(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session != null) {
        String username = (String)session.getAttribute("username");
        return "登录用户:"+username;
    }
    return "session 为空";
}

image-20240306164840699

第二种获取方法:

@RequestMapping("/getSession2")
public String getSession2(@SessionAttribute(required = false) String username) {
    return "username:"+username;
}

image-20240306165635022

image-20240306165739320

我们先setSession设置Session

image-20240306165818835 image-20240306165837280

再使用getSession2来获取设置完毕后的Session

第三种获取方法:

@RequestMapping("/getSession3")
public String getSession3(HttpSession session) {
    String username = (String)session.getAttribute("username");
    return "登录用户:"+username;
}

我们先setSession设置Session,再使用getSession3来获取设置完毕后的Session

image-20240306170232542

成功拿到我们的cookie,并从cookie中解析得到了我们设置好的用户名

传统获取 header/cookie

@RequestMapping("/param10")
@ResponseBody
public String param10(HttpServletResponse response, HttpServletRequest requ
est) {
 String name = request.getParameter("name");
 // 获取所有 cookie 信息
 Cookie[] cookies = request.getCookies();
 String userAgent = request.getHeader("User-Agent");
 return name + ":"+userAgent;
}

简洁的获取 Cookie—@CookieValue

@RequestMapping("/cookie")
@ResponseBody
public String cookie(@CookieValue("bite") String bite) {
 return "cookie:" + bite;
}

简洁获取 Header—@RequestHeader

@RequestMapping("/header")
@ResponseBody
public String header(@RequestHeader("User-Agent") String userAgent) {
 return "userAgent:"+userAgent;
}

获取Header

Header:标头 (header) 是服务器以HTTP协议传HTML资料到浏览器前所送出的字串,在标头与 HTML 文件之间尚需空一行分隔。

第一种获取方法:

@RequestMapping("/getHeader")
public String gerHeader(HttpServletRequest request) {
    String userAgent = request.getHeader("User-Agent");
    return "userAgent" + userAgent;
}

image-20240306170804760

第二种获取方法:

@RequestMapping("/getHeader2")
public String gerHeader2(@RequestHeader("User-Agent")String userAgent) {
    return "userAgent" + userAgent;
}

image-20240306171222500

3.2.11传递数组

@RequestMapping("/m5")
public String m5(String[] arrayParam) {
    return "接收到的参数arrayParam:" + Arrays.toString(arrayParam) + ",长度:" + arrayParam.length;
}

传递数组
当我们请求中,同一个参数有多个时,浏览器就会帮我们给封装成一个数组

image-20240305202309737

image-20240305202746965

image-20240305202315371

image-20240305202733167

3.2.12传递集合

更改一下代码,使用List来接收数组

@RequestMapping("/m6")
public String m6(List<String> listParam) {
    return "接收到的参数listParam:" + listParam + ",长度:" + listParam.size();
}
image-20240305203107880

发现错误500,表示服务端发生错误
5开头的通常值服务端发生错误
4开头的通常值客户端发生错误

如果看到5开头的错误,第一反应去看后端日志
后端日志从下往上看,一段一段的看
先看最后一段的首行

看完后端代码后发现缺少数组重命名:

image-20240305203353585

于是加上@RequestParam再次启动idea

image-20240305203459530

成功接收到我们的参数,以及长度

image-20240305203701621

image-20240305204638713

3.2.13传递JSON数据(重点)

3.2.13.1 JSON概念

JSON: JavaScript Object Notation [JavaScript 对象表示法]

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

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

类似于:

  • 国际通用语言-英语
  • 中国56个民族不同地区的通用语言-普通话

有自己的语法,其他语言也认识.

JSON与Javascript的关系

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

3.2.13.2 JSON语法

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

我们先来看一段JSON数据

{
	"squadname"; "Super hero squad",
    "homeTown":“Metro city”.
    "formed": 2016"secretBase":super tower”,
    "active": true.
    "meabers":[{
        "name":"Holecule Man",
        "age": 29,
        "secretidentity": "Dan Jukes”.
        powers"; ["Radiation resistance", "Turning tiny", "Radiation blast"]
	},{
        "name": "Madane uppercut”,
        age": 39
        "secretIdentity":"Jane Wilson",
        powers": ["Million tonne punch", "Damage resistance", "superhuman reflexes"]
	},{
        "name": "eternal flane",
        2ge”: 1088800
        secretIdentity":"Unknown”,
        "powers": ["Immortality,"Heat Imunity", "Inferno", "Teleportation","Interdine
    }]
}

也可以压缩表示:

{"squadhame":"Super hero squad","homeToun":"Metro City","formed":2016,"secretBase":"Super
tower","active";true,"members":[{"nane";"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":["Iortality","Heat Immunity,"Inferno”,"Teleportation”,"Interdimensional travel”]}]}

和上面描述的数据一样,只不过上面的进行了格式化,更易读

JSON的语法:

1.数据在键值对(Key/Value) 中

2.数据由逗号 。分隔

3.对象用 表示

4.数组用[] 表示

5.值可以为对象。也可以为数组,数组中可以包含多个对象

JSON的两种结构

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

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

image-20240306150834233

所以,以下都是合法的JSON数据

{"name":"admin","age":18}
["hello"3.1415"json"]
[{"name":"adnin","age":18},{"nane":"root","age":16},{"name":"3k=","age":20}]

可以使用在线JSON格式化工月来讲行校哈和书写: 在线JSON校验格式化工 (Be JSON)

3.2.13.3 JSON字符串和Java对象互转

JSON本质上是一个字符串,通过文本来存储和描述数据

Spring MVC框架也集成了JSON的转换工具,我们可以直接使用,来完成JSON字符串和Java对象的互转

本质上是jackson-databind提供的功能,Spring MVC框架中已经把该工具包引入了进来,咱们直接使用即可,如果脱离Spring MVC使用,需要引入相关依赖

<dependency>
<groupId>com.fasterxml.jackson.corec</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.5</version>
</dependency>

JSON的转换工具包有很多,jackson-databind只是其中的一种

public class Jsonutils [
    private static objectrapper 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()+",password:"+p.getPa
   }
}

使用ObjectMapper 对象提供的两个方法,可以完成对象和JSON字符中的互转

writeValueAsString: 把对象转为JSON字符串

readValue: 把字符申转为对象

JSON优点

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

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

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

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

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

基于以上特点,JSON在Web内用程序中被广泛使用,如前后端数据交互、API接口数据传输等

3.2.13.4 传递JSON对象

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

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

后端实现:

@RequestMapping("/m8")
public String m8(@RequestBody Person person) {
    return "接收到的数据person:" + person.toString();
}

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

image-20240306153338271

3.3 返回数据

通过上⾯的学习我们知道,默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的是视图 (xxx.html),⽽现在都是前后端分离的,后端只需要返给给前端数据即可,这个时候我们就需要使⽤ @ResponseBody 注解了。

@ResponseBody可以修饰 也可以修饰 方法

image-20240306183416616

3.3.1 返回静态⻚⾯

创建前端⻚⾯ index.html

image-20240306182912035
<!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>hello,spring mvc</title>
 <script src="index.js"></script>
</head>
<body>
 <h1>Hello,Spring MVC.</h1>
</body>
</html>

创建控制器 controller:

import com.example.demo.model.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/p")
public class PersonController {
 @RequestMapping("/index")
 public Object index(){
 // 执⾏业务...
 // 返回view -> index.html
 return "/index.html";
 }
}

3.3.2 返回 text/html

和返回数据一样,返回的数据中包含html标签的话,会直接被浏览器解析

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

image-20240306183534338

@Controller
public class ReturnController {
@ResponseBody
@RequestMapping("/returnData")
public String returnData() {
    return "返回视图需要的数据";
	}
}

image-20240306184115550

返回代码片段

@Controller
public class ReturnController {
    @ResponseBody
    @RequestMapping("/returnHtml")
    public String returnHtml() {
        return "<h1>返回HTML代码片段</h1>";
    }
}

image-20240306184224552

练习:实现计算器功能。

可使⽤ postman 传递参数,或使⽤ form 表单的⽅式提交参数。 前端⻚⾯:

<!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>
 <form action="http://localhost:8080/calc/sum">
 <h1>计算器</h1>
 数字1<input name="num1" type="text"><br>
 数字2<input name="num2" type="text"><br>
 <input type="submit" value=" 点击相加 ">
 </form>
</body>
</html>

controller 代码:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@ResponseBody
@Controller
@RequestMapping("/calc")
public class CalcController {
 @RequestMapping("/sum")
 public String sum(Integer num1,Integer num2){
 return String.format("<h1>计算的结果是:%d</h1><a href='javascript:h
istory.go(-1);'>返回</a>",num1+num2);
 }
}

3.3.3 返回 JSON 对象

@Controller
public class ReturnController {
    @ResponseBody
    @RequestMapping("/returnJson")
    public Person returnJson() {
        Person person = new Person();
        person.setId(1);
        person.setName("qiqi");
        person.setAge(7);
        return person;
    }
}

image-20240306184420054

image-20240306184559376

练习:实现登录功能,前端使⽤ ajax,后端返回 json 给前端

后端代码:

@RequestMapping(value = "/login")
 @ResponseBody
 public HashMap<String,Object> login(String username, String password){
 HashMap<String,Object> res = new HashMap<>();
 int succ = 200;
 if(username!=null && password!=null &&
 username.equals("admin") && password.equals("admin")){
 res.put("msg","登录成功");
 }else{
 res.put("msg","登录失败");
 }
 res.put("succ",succ);
 return res;
 }

前端代码:

<!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">
 <script src="js/jquery-1.9.1.min.js"></script>
 <title>Document</title>
 <script>
 function mysub() {
 var username = jQuery("#username").val();
 var password = jQuery("#password").val();
 jQuery.getJSON("/user/login",
 {
 "username":username,
"password":password
 },
 function (result) {
 if(result.succ==200){
 alert("返回结果:"+result.msg);
 }else{
 alert("操作失败,请重试。");
 }
 });
 }
 </script>
</head>
<body>
 <div style="text-align: center;">
 <h1>登录</h1>
 ⽤户:<input id="username">
 <br>
 密码:<input id="password" type="password">
 <br>
 <input type="button" value=" 提交 " onclick="mysub()" style="margin
-top: 20px;margin-left: 50px;">
 </div>
</body>
</html>

3.3.4设置状态码

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

@ResponseBody
@RequestMapping("/setStatus")
public String setStatus(HttpServletResponse response) {
    response.setStatus(401);//通常表示没有登录
    return "设置状态码";
}

image-20240307111859814

状态码不影响页面的展示

3.3.5设置Header

Header:标头 (header) 是服务器以HTTP协议传HTML资料到浏览器前所送出的字串,在标头与 HTML 文件之间尚需空一行分隔。

@ResponseBody
@RequestMapping(value = "/r1",produces = "application/json;charset=utf-8")
public String r1(HttpServletResponse response) {
    response.setStatus(401);//通常表示没有登录
    return "{'OK':1}";
}

image-20240307125100957

image-20240307125154560

image-20240308103710735

image-20240308103723351

3.3.6 请求转发或请求重定向

forward VS redirect

return 不但可以返回⼀个视图,还可以实现跳转,跳转的⽅式有两种:

  • forward 是请求转发;
  • redirect:请求重定向。

请求转发和重定向的使⽤对⽐:

// 请求重定向
@RequestMapping("/index")
public String index(){
 return "redirect:/index.html";
}
// 请求转发
@RequestMapping("/index2")
public String index2(){
 return "forward:/index.html";
}

举例说明 forward 和 redirect

forward(请求转发)和 redirect(请求重定向)的区别,举例来说,例如,你告诉你妈妈,你想吃辣 条,如果你妈妈,说好,我帮你去买,这就是 forward 请求转发;如果你妈妈让你⾃⼰去买,那么就是 请求 redirect 重定向。

“转发”和“重定向”理解:在中国官⽅发布的内容越少事也越⼤, “转发”和“重定向”也是⼀样:字越 少,责任越⼤ 。转发是服务器帮转的,⽽重定向是让浏览器重新请求另⼀个地址。

forward 和 redirect 具体区别如下:

  1. 请求重定向(redirect)将请求重新定位到资源;请求转发(forward)服务器端转发。
  2. 请求重定向地址发⽣变化,请求转发地址不发⽣变化。
  3. 请求重定向与直接访问新地址效果⼀直,不存在原来的外部资源不能访问;请求转发服务器端转发 有可能造成原外部资源不能访问。

请求转发 forward 导致问题演示

请求转发如果资源和转发的⻚⾯不在⼀个⽬录下,会导致外部资源不可访问,演示示例如下。 controller 示例代码:

@Controller
@RequestMapping("/user")
public class UserController {
 @RequestMapping(value = "/index")
 public String sayHi(){
 return "forward:/index.html";
 }
}

程序的⽬录如下:

image-20240305081813055

程序的执⾏结果如下:

image-20240305081824740

image-20240305081832943

尝试将转发 foward 换成重定向 redirect,如下代码所示:

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;
@Controller
@RequestMapping("/user")
public class UserController {
 @RequestMapping(value = "/index")
 public String sayHi(){
 return "redirect:/index.html";
 }
}

⻚⾯就可以正常获取到外部资源 js 了。

3.3.7 @ResponseBody 说明

@ResponseBody 返回的值如果是字符会转换成 text/html,如果返回的是对象会转换成 application/json 返回给前端。

@ResponseBody 可以⽤来修饰⽅法或者是修饰类,修饰类表示类中的所有⽅法都会返回 html 或者 json,⽽不是视图。

3.3.8 组合注解:@RestController

@RestController = @Controller + @ResponseBody

3.3.9加法计算器

3.3.9.1加法计算器前端代码
<!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.3.9.2约定前后端交互接口

接口定义:

请求路径:calc/sum

请求方式:GET/POST

接口描述:计算两个整数相加

请求参数:

image-20240308105328389

实例:num1=5&num2=3

响应数据:

Content-Type: text/html

响应内容:计算机结算结果:8

服务器给浏览器返回计算的结果

3.3.9.3后端代码

后端:

package org.example.demo.controller;

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 "计算机结果为:" + sum;
    }
}
3.3.9.4启动idea 测试 加法计算器项目 结果

image-20240308112121702

image-20240308112403555

image-20240308112411006

3.3.9.5开发中程序报错,如何定位问题

1.先定位前端还是后端

通过日志

1)前端:F12 看控制台

2)后端:接口,控制台日志

3.3.10用户登录

3.3.10.1用户登录前端代码

index.html

<!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({
            url: "/user/getUserInfo",
            type: "get",
            success:function(username){
                $("#loginUser").text(username);
            }
        });
    </script>
</body>

</html>

login.html

<!DOCTYPE html>
<html lang="en">

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

<body>
  <h1>用户登录</h1>
  用户名:<input name="userName" type="text" id="userName"><br>
  密码:<input name="password" 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>
    function login() {
      console.log("登录...");
      $.ajax({
        url:"/user/login",
        type:"post",
        data:{
          "username": $("#userName").val(),
          "password": $("#password").val()
        },
        success:function(result){
          if(result) {
            location.href = "/index.html";
            // location.assign();
          } else {
            alert("密码错误");
          }
        }
      });
    }

  </script>
</body>

</html>
3.3.10.2约定前后端交互接口

image-20240308114938514

image-20240308114943836

3.3.10.3用户登录后端代码
package org.example.demo.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created with IntelliJ IDEA
 * Description
 * User: 晓星航
 * Date: 2024 -03 -08
 * Time: 11:50
 */
@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping("/login")
    public Boolean login(String userName, String password, HttpSession session) {
        //校验参数合法性
//        if (userName == null || userName.length() == 0 || password == null || password.length() == 0) {
//            return false;
//        }
        //若username或password中任一一个为空就会返回false,加上!后返回true
        if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
            return false;
        }
        //进行用户名和密码校验
        if ("admin".equals(userName) && "admin".equals(password)) {
            //设置Session
            session.setAttribute("username","admin");
            return true;
        }
        return false;
    }
    @RequestMapping("/getUserInfo")
    public String getUserInfo(HttpServletRequest request) {
        //从Session中获取登录用户
        HttpSession session = request.getSession(false);
        String username = null;
        if (session != null) {
            username = (String) session.getAttribute("username");
        }
        return username;
    }
}

image-20240314181957475

3.3.10.4启动idea 测试 用户登录界面 结果

image-20240314190608181

3.3.12留言板

3.3.12.1留言板前端代码

messageWall.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>留言板</title>
    <style>
        .container {
            width: 350px;
            height: 300px;
            margin: 0 auto;
            /* border: 1px black solid; */
            text-align: center;
        }

        .grey {
            color: grey;
        }

        .container .row {
            width: 350px;
            height: 40px;

            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .container .row input {
            width: 260px;
            height: 30px;
        }

        #submit {
            width: 350px;
            height: 40px;
            background-color: orange;
            color: white;
            border: none;
            margin: 10px;
            border-radius: 5px;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>留言板</h1>
        <p class="grey">输入后点击提交, 会将信息显示下方空白处</p>
        <div class="row">
            <span>谁:</span> <input type="text" name="" id="from">
        </div>
        <div class="row">
            <span>对谁:</span> <input type="text" name="" id="to">
        </div>
        <div class="row">
            <span>说什么:</span> <input type="text" name="" id="say">
        </div>
        <input type="button" value="提交" id="submit" onclick="submit()">
        <!-- <div>A 对 B 说: hello</div> -->
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script>
        //页面加载时,请求后端,获取留言列表
        $.ajax({
           url: "/message/getMessageInfo",
           type: "get",
           success:function (message){
               for (var m of message) {
                   //2. 构造节点
                   var divE = "<div>"+m.from +"对" + m.to + "说:" + m.message+"</div>";
                   //3. 把节点添加到页面上
                   $(".container").append(divE);
               }
           }
        });
        function submit(){
            //1. 获取留言的内容
            var from = $('#from').val();
            var to = $('#to').val();
            var say = $('#say').val();
            if (from== '' || to == '' || say == '') {
                return;
            }
            //提交留言
            $.ajax({
                url: "/message/publish",
                type:"post",
                data:{
                    "from":from,
                    "to":to,
                    "message":say
                },
                success:function (result) {
                    if (result){
                        //添加成功
                        //2. 构造节点
                        var divE = "<div>"+from +"对" + to + "说:" + say+"</div>";
                        //3. 把节点添加到页面上
                        $(".container").append(divE);

                        //4. 清空输入框的值
                        $('#from').val("");
                        $('#to').val("");
                        $('#say').val("");
                    } else {
                        //添加失败 alert - 弹出一个警告框
                        alert("留言发布失败")
                    }
                }//如果成功的话执行一个回调函数
            });

            
        }
        
    </script>
</body>

</html>
3.3.12.2约定前后端交互接口

image-20240314194754203

image-20240314194800378

3.3.12.3留言板后端代码

MessageInfo.java (接口)

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.util.Date;

@Data
public class MessageInfo {
    private String from;
    private String to;
    private String message;
    private Date CreteTime;

}

MessageController.java

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA
 * Description
 * User: 晓星航
 * Date: 2024 -03 -14
 * Time: 19:48
 */
@RestController
@RequestMapping("/message")
public class MessageController {
    private List<MessageInfo> messageInfos = new ArrayList<>();
    @RequestMapping("/publish")
    public Boolean publishMessage(MessageInfo messageInfo) {
        //进行参数校验 StringUtils.hasLength()如果为空返回false,加上!后 如果为空会整体返回true
        if (!StringUtils.hasLength(messageInfo.getFrom())
                || !StringUtils.hasLength(messageInfo.getTo())
                || !StringUtils.hasLength(messageInfo.getMessage())) {
            return false;
        }
        //添加留言
        messageInfos.add(messageInfo);
        return true;
    }
    @RequestMapping("/getMessageInfo")
    public List<MessageInfo> getMessageInfo() {

        return messageInfos;
    }
}
3.3.12.4引入lombok包 - 使用 @Data 自动创建接口中成员变量的set和get方法

引入lombok包的意义是可以避免在每一个接口中都需要写get和set方法,引入完毕后我们就可以直接使用方法来使用接口中的成员变量了。

image-20240314200416087

引入之后,使用maven刷新一下,我们引入的包就下载完毕了

image-20240314200619718

然后在我们的自定义接口类中加上 @Data ,此时这个注解会帮我们自动编写get和set方法

image-20240314200839737

如果我们只想部分方法可以使用get和set方法

image-20240314201048195

此时我们可以在我们想使用的成员变量上加上 @Getter@Setter 注解来只针对我们部分成员变量进行get方法和set方法的创建

3.3.12.5使用spring自带的框架导入(导入lombok)

首先我们要安装一个新的插件

image-20240314201623182

在pom.xml文件中右键,点击Generate

image-20240314201743631

选择第一个Edit Starters

image-20240314201828401

选择OK

image-20240314201905509

找到Lombok,点击添加并OK确认即可完成添加Lombok包的功能

image-20240314202012092

3.3.12.5.1 lombok的api方法总结

image-20240314202304159

image-20240314202536180

@Data

image-20240314202352864

@ToString

image-20240314202422977

3.3.12.6启动idea 测试留言板项目结果

image-20240314205735080

如果代码出问题了

image-20240314204514372

3.3.13图书管理系统

3.3.13.1创建新项目

image-20240315105533453

删除没用的代码

image-20240315110122575

3.3.13.2用户登录前端代码

login.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/login.css">
    <script type="text/javascript" src="js/jquery.min.js"></script>
</head>

<body>
    <div class="container-login">
        <div class="container-pic">
            <img src="pic/computer.png" width="350px">
        </div>
        <div class="login-dialog">
            <h3>登陆</h3>
            <div class="row">
                <span>用户名</span>
                <input type="text" name="userName" id="userName" class="form-control">
            </div>
            <div class="row">
                <span>密码</span>
                <input type="password" name="password" id="password" class="form-control">
            </div>
            <div class="row">
                <button type="button" class="btn btn-info btn-lg" onclick="login()">登录</button>
            </div>
        </div>
    </div>
    <script src="js/jquery.min.js"></script>
    <script>
        function login() {
            $.ajax({
               url:"/user/login",
               type:"post",
               data:{
                   "userName":$("#userName").val(),
                   "password":$("#password").val()
               },
                success:function (result) {
                   if (result) {
                       location.href = "book_list.html";
                   } else {
                       alert("用户名或密码错误");
                   }
                }
            });
        }


    </script>
</body>

</html>

book_list.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图书列表展示</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">

    <link rel="stylesheet" href="css/list.css">
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/bootstrap.min.js"></script>
    <script src="js/jq-paginator.js"></script>

</head>

<body>
    <div class="bookContainer">
        <h2>图书列表展示</h2>
        <div class="navbar-justify-between">
            <div>
                <button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加图书</button>
                <button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量删除</button>
            </div>
        </div>

        <table>
            <thead>
                <tr>
                    <td>选择</td>
                    <td class="width100">图书ID</td>
                    <td>书名</td>
                    <td>作者</td>
                    <td>数量</td>
                    <td>定价</td>
                    <td>出版社</td>
                    <td>状态</td>
                    <td class="width200">操作</td>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td><input type="checkbox" name="selectBook" value="1" id="selectBook" class="book-select"></td>
                    <td>4</td>
                    <td>大秦帝国第四册</td>
                    <td>我是作者</td>
                    <td>23</td>
                    <td>33.00</td>
                    <td>北京出版社</td>
                    <td>可借阅</td>
                    <td>
                        <div class="op">
                            <a href="book_update.html?bookId=4">修改</a>
                            <a href="javascript:void(0)" onclick="deleteBook(4)">删除</a>
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>

        <div class="demo">
            <ul id="pageContainer" class="pagination justify-content-center"></ul>
        </div>
        <script>

            getBookList();
            function getBookList() {
                $.ajax({
                    type:"get",
                    url:"/book/getBookList",
                    success:function (books) {
                        var finalHtml = "";
                        for (var book of books) {
                            //根据每一条记录去拼接html,也就是一个tr
                            finalHtml += '<tr>';
                            finalHtml += '<td><input type="checkbox" name="selectBook" value="'+book.id+'" id="selectBook" class="book-select"></td>';
                            finalHtml += '<td>'+book.id+'</td>';
                            finalHtml += '<td>'+book.bookName+'</td>';
                            finalHtml += '<td>'+book.author+'</td>';
                            finalHtml += '<td>'+book.count+'</td>';
                            finalHtml += '<td>'+book.price+'</td>';
                            finalHtml += '<td>'+book.publish+'</td>';
                            finalHtml += '<td>'+book.statusCN+'</td>';
                            finalHtml += '<td><div class="op">';
                            finalHtml += '<a href="book_update.html?bookId='+book.id+'">修改</a>';
                            finalHtml += '<a href="javascript:void(0)" οnclick="deleteBook('+book.id+')">删除</a>';
                            finalHtml += '</div></td></tr>';
                        }
                        book.statusCN = undefined;
                        console.log(finalHtml);
                        $("tbody").html(finalHtml);
                    }
                });
            }
            //翻页信息
            $("#pageContainer").jqPaginator({
                totalCounts: 100, //总记录数
                pageSize: 10,    //每页的个数
                visiblePages: 5, //可视页数
                currentPage: 1,  //当前页码
                first: '<li class="page-item"><a class="page-link">首页</a></li>',
                prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',
                next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',
                last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',
                page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}<\/a><\/li>',
                //页面初始化和页码点击时都会执行
                onPageChange: function (page, type) {
                    console.log("第" + page + "页, 类型:" + type);
                }
            });
            function deleteBook(id) {
                var isDelete = confirm("确认删除?");
                if (isDelete) {
                    //删除图书
                    alert("删除成功");
                }
            }
            function batchDelete() {
                var isDelete = confirm("确认批量删除?");
                if (isDelete) {
                    //获取复选框的id
                    var ids = [];
                    $("input:checkbox[name='selectBook']:checked").each(function () {
                        ids.push($(this).val());
                    });
                    console.log(ids);
                    alert("批量删除成功");
                }
            }



        </script>
    </div>
</body>

</html>
3.3.13.3约定前后端交互接口

image-20240315112613658

image-20240315112622911

字段说明:

image-20240315114123728

验证后端接口

image-20240315145718610

image-20240315145755220

3.3.13.4图书管理系统后端代码

BookInfo.java:

package org.example.book;

import lombok.Data;

import java.math.BigDecimal;

/**
 * Created with IntelliJ IDEA
 * Description
 * User: 晓星航
 * Date: 2024 -03 -15
 * Time: 11:30
 */
@Data
public class BookInfo {
    private Integer id;
    private String bookName;
    private String author;
    private Integer count;
    private BigDecimal price; //定价使用BigDecimal使得精度更准确
    private String publish;
    private Integer status;//1-可借阅,2-不可借阅
    private String statusCN;
}

UserController.java:

package org.example.book;

import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created with IntelliJ IDEA
 * Description
 * User: 晓星航
 * Date: 2024 -03 -15
 * Time: 11:27
 */
@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping("/login")
    public Boolean login(String userName, String password, HttpSession session){
        //校验参数
        if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){
            return false;
        }
        //验证账号密码是否正确
        //这种写法导致的问题 如果userName 为null,则会报空指针异常
//        if (userName.equals("admin") && password.equals("admin")) {
//
//        }
        if ("admin".equals(userName) && "admin".equals(password)) {
            //账号密码正确
            //存Session session.setAttribute(“username”,username); 是将username保存在session中
            session.setAttribute("userName",userName);
            return true;
        }
        return false;
    }
}

BookController.java:

package org.example.book;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Created with IntelliJ IDEA
 * Description
 * User: 晓星航
 * Date: 2024 -03 -15
 * Time: 11:27
 */
@RequestMapping("/book")
@RestController
public class BookController {
    @RequestMapping("/getBookList")
    public List<BookInfo> getBookList() {
        //1.获取图书数据

        //2.对图书数据进行处理

        //3.返回数据
        //mock 表示虚拟的,假数据
        List<BookInfo> bookInfos = mockData();
        for (BookInfo bookInfo : bookInfos) {
            if (bookInfo.getStatus() == 1) {
                bookInfo.setStatusCN("可借阅");
            } else {
                bookInfo.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }

    private List<BookInfo> mockData() {
        List<BookInfo> bookInfos = new ArrayList<>(15);
        for (int i = 0; i < 15; i++) {
            BookInfo bookInfo = new BookInfo();
            bookInfo.setId(i);
            bookInfo.setBookName("图书"+i);
            bookInfo.setAuthor("作者"+i);
            bookInfo.setCount(new Random().nextInt(200));
            bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
            bookInfo.setPublish("出版社"+i);
            bookInfo.setStatus(i%5==0?2:1);
            bookInfos.add(bookInfo);
        }
        return bookInfos;
    }
}
3.3.13.5启动idea 测试图书管理系统项目结果

登录页面:

image-20240315162605544

列表页:

image-20240315162614363

3.3.13.6应用分层 - 三层架构

image-20240315162716722

应用分层的好处

  • 降低层与层之间的依赖,结构更加的明确,利于各层逻辑的复用
  • 开发人员可以只关注整个结构中的其中某一层,极大地降低了维护成本和维护时间
  • 可以很容易的用新的实现来普换原有层次的实现 工
  • 有利于标准化

文件夹分层

image-20240315163140084

代码分层

image-20240315163206922

3.3.14注解总结(重点)

1.@RestController — 修饰的类下的方法,全部返回数据

@RestController = @Controller + @ResponseBody

image-20240306181857489

@RestController无法返回指定页面,
@Controller可以返回指定页面。

​ @RestController可以直接返回数据,
而@Controller需要@ResponseBody的辅助才能返回数据。

image-20240306175236526

image-20240306174943487

每一个spring项目开头必带的注释

2.@RequestMapping — 请求映射

image-20240306174825182

此时这里的我们要访问m1下的内容,我们需要输入: http://127.0.0.1:8080/param/m1

image-20240306174754821

image-20240306174708517

3.@RequestParam — 传递集合

image-20240306174037244

image-20240306173044150

4.@RequestBody — 传递JSON

image-20240306174052634

image-20240306173142203

5.@PathVariable — 映射 URL 绑定的占位符

image-20240306174425455

image-20240306174409095

6.@RequestPart — 上传文件

image-20240306174115407

image-20240306173301161

7.@RequestAttribute — 获取HTTP请求中的属性值

image-20240306173339668

image-20240306173358182

8.@CookieValue — 用来获取Cookie中的值

image-20240306173612613

image-20240306173619165

9.@SessionAttribute — 将Session中的数据映射到控制器处理方法的参数中

image-20240306174215506

image-20240306173732038

image-20240306173739187

10.@RequestHeader — 将请求参数区域的数据映射到控制层方法的参数上

image-20240306174202964

image-20240306173846124

11.@Controller — 返回指定页面(静态页面)

image-20240306181628338

3.3.15 查看更多注解

官⽅ API:https://docs.spring.io/springframework/docs/current/reference/html/web.html#mvc-ann-requestmapping

4.企业规范

  1. 类名使用大驼峰风格,但以下情形例外: DO/BO/DTO/VO/AO

  2. 方法名、参数名、成员变量、局部变量统一使用小驼峰风格

  3. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词

常见命名命名风格介绍

大驼峰: 所有单词首字母都需要大写,又叫帕斯卡命名法,比如: UserController

小驼峰: 除了第一个单词,其他单词首字母大写,比如: userController

蛇形: 用下划线()作用单词间的分隔符,一般小写,又叫下划线命名法,比如: user_control

串形: 用短横线(-)作用单词间的分隔符,又叫脊柱命名法,比如: user-controller

类名使用大驼峰

变量名使用小驼峰

数据库,字段命名用蛇形

5.总结

  1. 学习Spring MVC,其实就是学习各种Web开发需要用的到注解

    1. @RequestMapping: 路由映射
    2. @RequestParam: 后端参数重命名
    3. @RequestBody: 接收JSON类型的参数
    4. @PathVariable: 接收路径参数
    5. @RequestPart: 上传文件
    6. @ResponseBody: 返回数据
    7. @CookieValue: 从Cookie中获取值
    8. @SessionAttribute: 从Session中获取值
    9. @RequestHeader: 从Header中获取值
    10. @Controller: 定义一个控制器, Spring 框架启动时加载,把这个对象交给Spring管理.默认返回视图.
    11. @RestController: @ResponseBody + @Controller 返回数据
  2. Cookie 和Session都是会话机制,Cookie是客户端机制,Session是服务端机制.二者通过SessionId来关联,Spring MVC内置HttpServletRequest,HttpServletResponse两个对象,需要使用时,直接在方法中添加对应参数即可,Cookie和Session可以从HttpServletRequest中来获取,也可以直接使用HttpServletResponse设置Http响
    状态码

  3. Java EE学习阶段会涉及较多工具,插件的学习,来帮助我们提高开发效率,比如Postman,lombok,EditStarter

pps-1712723849768)]

3.3.15 查看更多注解

官⽅ API:https://docs.spring.io/springframework/docs/current/reference/html/web.html#mvc-ann-requestmapping

4.企业规范

  1. 类名使用大驼峰风格,但以下情形例外: DO/BO/DTO/VO/AO

  2. 方法名、参数名、成员变量、局部变量统一使用小驼峰风格

  3. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词

常见命名命名风格介绍

大驼峰: 所有单词首字母都需要大写,又叫帕斯卡命名法,比如: UserController

小驼峰: 除了第一个单词,其他单词首字母大写,比如: userController

蛇形: 用下划线()作用单词间的分隔符,一般小写,又叫下划线命名法,比如: user_control

串形: 用短横线(-)作用单词间的分隔符,又叫脊柱命名法,比如: user-controller

类名使用大驼峰

变量名使用小驼峰

数据库,字段命名用蛇形

5.总结

  1. 学习Spring MVC,其实就是学习各种Web开发需要用的到注解

    1. @RequestMapping: 路由映射
    2. @RequestParam: 后端参数重命名
    3. @RequestBody: 接收JSON类型的参数
    4. @PathVariable: 接收路径参数
    5. @RequestPart: 上传文件
    6. @ResponseBody: 返回数据
    7. @CookieValue: 从Cookie中获取值
    8. @SessionAttribute: 从Session中获取值
    9. @RequestHeader: 从Header中获取值
    10. @Controller: 定义一个控制器, Spring 框架启动时加载,把这个对象交给Spring管理.默认返回视图.
    11. @RestController: @ResponseBody + @Controller 返回数据
  2. Cookie 和Session都是会话机制,Cookie是客户端机制,Session是服务端机制.二者通过SessionId来关联,Spring MVC内置HttpServletRequest,HttpServletResponse两个对象,需要使用时,直接在方法中添加对应参数即可,Cookie和Session可以从HttpServletRequest中来获取,也可以直接使用HttpServletResponse设置Http响
    状态码

  3. Java EE学习阶段会涉及较多工具,插件的学习,来帮助我们提高开发效率,比如Postman,lombok,EditStarter

感谢各位读者的阅读,本文章有任何错误都可以在评论区发表你们的意见,我会对文章进行改正的。如果本文章对你有帮助请动一动你们敏捷的小手点一点赞,你的每一次鼓励都是作者创作的动力哦!😘