springboot构建RESTful 风格应用

news2024/7/3 8:42:59

Spring Boot 构建 RESTful 风格应用

1.Web开发的两种模式:

前后端不分离:

以前没有移动互联网时,我们做的大部分应用都是前后端不分的,比如jsp,或者thymeleaf等后端分离模板,在这种架构的应用中,数据基本上都是在后端渲染好返回给前端展示的,也就是后端需要控制前端的展示,前端与后端的耦合度很高。
这种应用模式比较适合纯网页应用,但是当后端对接App时,App可能并不需要后端返回一个HTML网页,而仅仅是数据本身,所以后端原本返回网页的接口不再适用于前端App应用,为了对接App后端还需再开发一套接口。这样前后端不分离就有局限性了。

前后端分离:

在前后端分离的应用模式中,后端仅返回前端所需的数据,不再渲染HTML页面,不再控制前端的效果。至于前端用户看到什么效果,从后端请求的数据如何加载到前端中,都由前端自己决定,网页有网页的处理方式,App有App的处理方式,但无论哪种前端,所需的数据基本相同,后端仅需开发一套逻辑对外提供数据即可。
在前后端分离的应用模式中 ,前端与后端的耦合度相对较低。
我们通常将后端开发的每个视图都称为一个接口,或者API,前端通过访问接口来对数据进行增删改查。

前面我们讲解了前后端不分离不分离模式,现在来讲解一下前后端分离模式怎么实现.

2.什么是API?

API:全称是 Application Programming Interface,应用程序编程接口。API是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。简单的说API 是一套协议,规定了我们与外界的沟通方式:如何发送请求和接收响应。
比如我们平时在QQ上可以看到天气信息,而这些天气信息不是腾讯公司用卫星监测到的,而是去调用气象局的天气信息的。但腾讯公司不需要也不能访问气象局的数据库和源码,而是通过调用气象局提供的一个公共函数来实现,我们只需要知道这个函数需要传递什么参数,以及返回什么样的数据就行,函数的内部结构我们并不需要知道。这个函数就是API。

3.什么是RESTful

为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,
而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。所以出现了接口服务架构,目前市面上大部分公司开发人员使用的接口服务架构主要有:restful、rpc。

REST:那Rest是什么呢,它是一种架构风格,就像气象局建立API时要遵守的一种规则,可以是Rest也可以是其它规则。这种规则是为web应用服务的,也就是用URL定位资源,用HTTP动词(GET,POST,DELETE,PUT)描述操作,用HTTP Status Code返回结果状态的这种client和server的交互方式。实现看Url就知道要什么,看http 方法就知道干什么,看http status code就知道结果如何。

RESTFul:RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中。RESTFul就是为了实现REST这种交互方式而制定的一套约束条件和规则,符合这些约束条件和原则的应用程序或设计就是RESTful。也就是REST本身不实用,实用的是如何设计 RESTful API(REST风格的网络接口)。这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。

4.RESTful设计规范

那么RESTFul有哪些设计规范呢?

1、协议

API与用户的通信协议,使用HTTPs协议或者HTTP协议,统一确定用一种。

2、域名

应该尽量将API部署在专用域名之下,如https://xxx.xxx.com;

如果多个项目创建API,把项目名称带上 如[https://项目名.XXX.com

3、版本

应该将API的版本号放入URL。

http://www.example.com/app/1.0/foo

http://www.example.com/app/1.1/foo

http://www.example.com/app/2.0/foo


另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github就采用了这种做法。

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL。版本号可以在HTTP请求头信息的Accept字段中进行区分


Accept: vnd.example-com.foo+json; version=1.0

Accept: vnd.example-com.foo+json; version=1.1

4、路径

路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource)

(1) 资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。

举例来说,以下是不好的例子:
/selectGoods
/listOrders
/retreiveClientByOrder?orderId=1

对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

GET  /goods :将返回所有商品清单
POST /goods :将商品新建到集合
GET  /goods/4 :将获取商品 4
PATCH(或)PUT /goods/4 :将更新商品 4


(2) API中的名词应该使用复数。无论子资源或者所有资源。

举例来说,获取产品的API可以这样定义

获取单个产品:http://127.0.0.1:8080/AppName/goods/1
获取所有产品: http://127.0.0.1:8080/AppName/goods

5、HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- DELETE(DELETE):从服务器删除资源。

还有三个不常用的HTTP动词。

- PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
- HEAD:获取资源的元数据。
- OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

GET /goods:列出所有商品清单
POST /goods:新建一个商品(上传文件)
GET /goods/ID:获取某个指定商品的信息
PUT /goods/ID:更新某个指定商品的信息(提供该商品的全部信息)
PATCH /goods/ID:更新某个指定商品的信息(提供该商品的部分信息)
DELETE /goods/ID:删除某个商品
GET /goods/ID/attributes:列出某个指定商品的所有属性信息
DELETE /goods/ID/attributes/ID:删除某个指定商品的指定属性

6、过滤信息

如果记录数量很多,服务器不可能都将它们返回给用户,API会提供参数,过滤返回结果,用于补充规范一些通用字段,常见的参数有:
1. ?limit=20:指定返回记录的数量为20;
2. ?offset=8:指定返回记录的开始位置为8;
3. ?page=1&per_page=50:指定第1页,以及每页的记录数为50;
4. ?sortby=name&order=asc:指定返回结果按照name属性进行升序排序;
5. ?attr_id=2:指定筛选条件。

7、状态码

服务器会向用户返回状态码和提示信息,以下是常用的一些状态码,可以根据实际业务添加对应的状态码,需和http状态码对应:
1. 200 OK - [GET]:服务器成功返回用户请求的数据;
2. 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功;
3. 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务);
4. 204 NO CONTENT - [DELETE]:用户删除数据成功;
5. 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作;
6. 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误);
7. 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的;
8. 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作;
9. 406 Not Acceptable - [GET]:用户请求的格式不可得;
10. 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的;
11. 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误;
12. 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

8、错误处理

如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

比如Google 的出错信息:

{
  "error": {
    "errors": [
      {
        "domain": "global",
        "reason": "insufficientFilePermissions",
        "message": "The user does not have sufficient permissions for file {fileId}."
      }
    ],
    "code": 403,
    "message": "The user does not have sufficient permissions for file {fileId}."
  }
}

9、返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

GET /collection:返回资源对象的列表(数组)

GET /collection/ID:返回单个资源对象(json)

POST /collection:返回新生成的资源对象(json)

PUT /collection/ID:返回完整的资源对象(json)

DELETE /collection/ID:返回一个空文档(空字符串)

比如:
code:200,
msg:查询成功
data:[{},{},{}]

10、超媒体(Hypermedia API)

RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。

比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}
从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

{
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3"
}
上面代码表示,服务器给出了提示信息,以及文档的网址。

11、其它

服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

注意:上面的规范是一些约定的,并不是强制,可以不遵守,也可以只遵守几条

5.RESTFUl风格的 GET、POST、PUT、DELETE前端请求和服务器端接收示例

5.1 GET请求

后端:

 @ResponseBody
 @GetMapping("/users/{id}")
 public User getUserById(@PathVariable("id") Integer id){
        return userService.getUserById(id);
 }

前端:

localhost:8080/users/1

5.2 POST请求

后端:

@ResponseBody
@PostMapping("/users")
public int addUser(User user){ 

   return  userService.addUser(user);   
}

前端:

<form action="http://localhost:8080/users" method="post">
	
	<input type="text" name="uname" />
    
    <input type="text" name="age" />

	<input type="submit" value="提交"/>
    
</form>

5.3 PUT请求

后端:

@ResponseBody
@PutMapping("/users")
public Users updateUser(User user){

        return userService.updateUser(user);
    }

前端:

<form action="http://localhost:8080/users" method="post">

	<input type="hidden" name="id" />

	<input type="text" name="uname" />
    
    <input type="text" name="age" />

	<input type="hidden" name="_method" value="PUT"/>  <!-- 在表单中添加 _method 提交put 请求-->

	<input type="submit" value="提交"/>

</form>

5.4 DELETE请求

后端:

@DeleteMapping("/users/{id}")
@ResponseBody
public String deleteUser(@PathVariable("id") Integer id){
        return userService.deleteUserById(id);
    }

前端:

<form action="http://localhost:8083/user/1" method="post">

	<input type="hidden" name="_method" value="DELETE"/>

	<input type="submit" value="提交"/>

</form>

异步请求,对于PUT和DELETE请求,使用post方法提交,在发送的数据中加上_method=PUT/DELETE

6.RESTFUL风格中设计统一响应体

6.1为什么要用统一响应体

什么是统一响应体呢?在前后端分离架构下,后端主要是一个RESTful API的数据接口。接口中有时返回数据,有时又没有,还有的会出错,也就是结果不一致。只用http状态码表达不够。

那么可以通过修改响应返回的JSON数据,让其带上一些固有的字段,例如以下这样的:

{
    "code": 600,
    "msg": "success",
    "data": {
        "id": 1,
        "uname": "daimenglaoshi"
        "qq":2398779723
    }
}

6.2 怎么实现

1.创建一个响应结果类

package com.test.restful.util;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseResult<T> {

    public int code; //返回状态码

    private String msg; //返回描述信息

    private T data; //返回内容体

   
}

2.创建一个生成响应结果类的类

package com.test.restful.util;


public class Response {

    private final static String SUCCESS = "success";

    private final static String FAIL = "fail";

    //不同的响应情景
    public static <T> ResponseResult<T> makeOKRsp() {
        return new ResponseResult<T>(200,SUCCESS,null);
    }

    public static <T> ResponseResult<T> makeOKRsp(String message) {
        return new ResponseResult<T>(200,message,null);
    }

    public static <T> ResponseResult<T> makeOKRsp(T data) {
        return new ResponseResult<T>(200,SUCCESS,data);
    }

    public static <T> ResponseResult<T> makeErrRsp(String message) {
        return new ResponseResult<T>(500,message,null);
    }

    public static <T> ResponseResult<T> makeErrRsp() {
        return new ResponseResult<T>(500,FAIL,null);
    }

    public static <T> ResponseResult<T> makeRsp(int code, String msg, T data) {
        return new ResponseResult<T>(code,msg,data);
    }
}

3.控制层调用

@GetMapping("/users")
public ResponseResult<List<User>> findUsers()
{
    List<User> userList= userService.findUsers();

    return Response.makeOKRsp(userList);
}

4.Postman测试

在这里插入图片描述

5.封装状态码

我们也可以将状态码封装到枚举类中

比如:

package com.test.restful.util;


public enum  CodeStatus {

    // 成功
    SUCCESS(200),

    // 错误的请求
    FAIL(400),

    // 访问被拒绝,比如未认证(签名错误)
    UNAUTHORIZED(401),

    // 接口不存在
    NOT_FOUND(404),

    // 服务器内部错误
    INTERNAL_SERVER_ERROR(500);

    //自定义  状态码
    NOT_ALLOWRD_REG(1001);

    public int code;

    CodeStatus(int code) {
        this.code = code;
    }

}

那么我们在Response类中使用状态码时可以替换成CodeStatus来访问,视频中会有详细讲解。

7.全局异常处理

​ 在使用统一响应结果的时候,还会遇到一种情况,就是程序的报错是由于运行时出异常导致的,有些异常是我们可以提前预知在业务中抛出,有些则是无法提前预知的。不管能否预知,我们都需要对异常处理,如果我们可以定义一个统一的全局异常处理,在Controller捕获所有异常,并且做适当处理,也做成统一响应体返回就好了。

7.1设计思路

  1. 自定义一个异常类(如:NotAllowedRegException),捕获针对项目或业务的某个异常;
  2. 使用@ExceptionHandler注解处理自定义异常和通用异常,并指明异常的处理类型;
  3. 使用@ControllerAdvice接收所有的控制层方法抛出的异常

7.2 代码实现

创建NotAllowedRegException类

package com.test.restful.exception;

import lombok.Data;


@Data
public class NotAllowedRegException extends Exception {

      private int code; //这里的状态码  在 StatusCode枚举类设置好

      private String message="异常:该用户不允许注册";

     public NotAllowedRegException(String msg) {
        super(msg);
     }
    public NotAllowedRegException() {

    }
}

创建统一异常处理类GlobalExceptionHandler:

package com.test.restful.controller;

import com.test.restful.exception.NotAllowedRegException;
import com.test.restful.util.Response;
import com.test.restful.util.ResponseResult;
import com.test.restful.util.StatusCode;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


/*
@RestControllerAdvice=@RestController+ @ControllerAdvice     @ControllerAdvice字面上意思是“控制器通知”,作用是接收所有的控制层方法抛出的异常
如果你只想对一部分控制器添加通知,比如某个包下的控制器,可以写@RestControllerAdvice("包名")
*/
@RestControllerAdvice
public class GlobalExceptionHandler {

    //@ExceptionHandler 注解用来指明异常的处理类型,即如果这里指定为 NullpointerException,则数组越界异常就不会进到这个方法中来。
    @ExceptionHandler(Exception.class)
    public ResponseResult handlerException(Exception e) {

        // 自定义异常
        if (e instanceof NotAllowedRegException) {
            return Response.createFailResp(StatusCode.NOT_ALLOWRD_REG.code,((NotAllowedRegException) e).getMessage());
        }else {
            // 其他异常,当我们定义了多个异常时,这里可以增加判断和记录
            return Response.createFailResp(StatusCode.SERVER_ERROR.code,e.getMessage());
        }
    }


}

创建控制层方法测试:

package com.test.restful.controller;

import com.test.restful.exception.NotAllowedRegException;
import com.test.restful.pojo.User;
import com.test.restful.util.Response;
import com.test.restful.util.ResponseResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/test")
public class User3Controller {

    @PostMapping("/users")
    public ResponseResult addUser(User user) throws NotAllowedRegException {
           if(user.getUname().equals("daimenglaoshi"))
              throw new NotAllowedRegException();

           return Response.createOkResp();
    }

}

测试结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66919.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQLMAP _DNS注入配置方法

网上针对sqlmap进行dns注入的相关文章太少&#xff0c;只是简单介绍了下–dns-domain参数&#xff0c;相关的实战文章要么就模糊或者一笔带过&#xff0c;。然后参考网上的方法重新整理了一遍&#xff0c;简单理解。 需要准备的东西&#xff0c;sqlmap、windows盲注一个、两个…

档案信息化咨询方法论实践要点有哪些?

咨询工作中&#xff0c;有些咨询师也许会有这样的疑问&#xff1a;“我业务经验挺丰富的&#xff0c;但做咨询工作怎么这么吃力呢&#xff1f;”这可能就是因为缺乏方法论的有效指导。咨询方法论是咨询工作中用来分析和解决某类特定问题的工具、方法、流程、模型和评价准则等。…

Ansible之playbook详解和应用实例

目录 一、playbook简介 1.什么是playbook 2.playbook组成 二、应用实例 1.使用playbook安装启用httpd服务 2.使用playbook安装启用nginx服务 三、ansible-playbook其他用法 1.检查yaml文件的语法是否正确 2.检查tasks任务 3.检查指定的主机 4.指定从某个task开始运行…

红队学习隧道必须了解的知识

端口映射和端口转发 端口映射 端口映射就是将外网的主机的一个端口映射到内网主机的一个端口&#xff0c;提供相应的服务。当用户访问外网IP的这个端口时&#xff0c;服务器自动将请求映射到对应局域网内部的机器上 端口转发 端口转发就是将发往外网指定端口的通信完全转发给…

windows下安装hbase

windows下安转hbase 安装流程 解压提供的压缩包 该压缩包是经过我修改后的&#xff0c;已经创建data、tmp、zookeeper目录和替换相关配置文件&#xff0c;目的是简化安装流程 压缩包 https://share.weiyun.com/SLTS9woO 环境变量配置 在系统变量中添加HBASE_HOME环境变量&…

Java ConcurrentHashMap 高并发安全实现原理解析

三、C13Map的字段定义 C13Map的字段定义 //最大容量 private static final int MAXIMUM_CAPACITY 1 << 30; //默认初始容量 private static final int DEFAULT_CAPACITY 16; //数组的最大容量,防止抛出OOM static final int MAX_ARRAY_SIZE Integer.MAX_VALUE -…

使用pyautogui进行PC用户界面自动化测试

目录 1.pyautogui简介及安装 2.pyautogui常见用法 2.1返回所用显示器的分辨率 2.2键盘输入函数 2.3常用函数typewrite() 2.4键盘事件 2.5函数hotkey() 2.6保存屏幕截图 3.常见用法代码汇总 1.pyautogui简介及安装 Pyautogui是一个纯Python的图形化自动化工具&#x…

代码审计-4 代码执行漏洞

代码执行漏洞 代码执行漏洞利用 ZZZPHP1.6 远程代码执行漏洞分析 漏洞点函数 此处如果能控制$ifstr就可以进行闭合&#xff0c;执行恶意代码 parserIfLabel函数将传入的参数进行正则匹配&#xff0c;当匹配通过时继续往下走 下面并没有对恶意代码内容进行过滤 跟踪parserIfLab…

游戏开发37课 狙击枪 视野问题

首先说一下视野的思路&#xff1a;我们可视化的视野全部都是以扇形显示的&#xff0c;同时为了后期的方便调整我们的视野和距离都必须是动态的。那么我们是不是可以使用度数来控制视野范围&#xff0c;那么我们就需要画出一个扇形。那么我们可以先画出来一个圆 然后在这个圆上面…

Linux安装 vmware workstation

官网下载地址 vmware workstation&#xff1a; Download VMware Workstation Pro 也可以下载提供的安装包。 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;au74 一、Ubuntu 安装 安装构建依赖项&#xff0c;打开您的系统终端并运行以下命令&#xff1a; s…

Linux操作系统中的yum命令

Linux操作系统中的yum命令是大家经常会用到的命令&#xff0c;有着非常重要的作用&#xff0c;但很多朋友依然不太清楚yum命令作用是什么&#xff1f;yum命令有哪些语法和常用命令&#xff1f;接下来我们一起来看看详细的内容介绍。 yum命令全称为Yellow dog Updater, Modified…

ChatGPT生成量化交易策略,真好玩

OK&#xff0c;还有没有更好玩的对 量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 『正文』 ˇ 最近比较火的OpenAI-ChatGPT&#xff0c;太有意思了。尝试让它写了几个策略&#xff0c;您别说&#xff0c;还真是有模有样。我们来看看吧。 模型一&a…

哈希(开散列、闭散列)-位图-布隆过滤器-哈希切分

文章目录1、哈希概念2、哈希表/散列表&#xff08;1&#xff09;哈希函数的设计&#xff1a;&#xff08;2&#xff09;&#xff08;最常用&#xff09;除留余数法&#xff1a;&#xff08;3&#xff09;如何解决哈希冲突&#xff1f;更加合理的设计哈希函数闭散列&#xff08;…

Java多线程之线程同步机制(锁,线程池等等)

Java多线程之线程同步机制一、概念1、并发2、起因3、缺点二、三大不安全案例1、样例一&#xff08;模拟买票场景&#xff09;2、样例二&#xff08;模拟取钱场景&#xff09;3、样例三&#xff08;模拟集合&#xff09;三、同步方法及同步块1、同步方法2、同步块四、JUC安全类型…

java多线程这一篇就差不多了

java多线程这一篇就差不多了 什么是多线程&#xff1f; 一般被问你对多线程了解多少的时候&#xff0c;你可能不仅仅只需要知道线程怎么创建&#xff0c;你可能需要了解线程的几种创建方式&#xff0c;线程的生命周期&#xff0c;线程池相关&#xff0c;并发安全&#xff0c;…

ADSP-21489的图形化编程详解(4:左右声道音量调节和多通道的输入输出详解)

左右声道音量调节 在直通的前提下&#xff0c;我们加入一个调音量的算法模块&#xff0c;来实现调节输出音量大小的功能。首先拖出来一个音量调节算法模块&#xff1a; 我们这里都是双通道&#xff0c;所以需要对这个音量调节模块进行配置&#xff1a; 连好程序&#xff0c;下…

acwing基础课——Dijkstra

由数据范围反推算法复杂度以及算法内容 - AcWing 常用代码模板3——搜索与图论 - AcWing 基本思想&#xff1a; 迪杰斯特拉&#xff08;dijkstra&#xff09;算法是单源最短路径问题的求解方法,它是一个按路径长度递增的次序产生最短路径的算法。单源最短路径就在给出一个固定…

Sqoop数据导出第2关:HDFS数据导出至Mysql内

为了完成本关任务,你需要掌握: 1、数据库( MySQL )建表。 2、HDFS 数据导出至 MySQL 中。 数据库( MySQL )建表 因为这边 Sqoop 不能够帮关系型数据库创建表,所以需要我们自己来创建表。 用命令进入 MySQL 客户端。 mysql -uroot -p123123 -h127.0.0.1 创建数据库…

备忘录模式(Memento)

参考&#xff1a; [备忘录设计模式 (refactoringguru.cn)](https://refactoringguru.cn/design-patterns/mediator) 文章目录一、什么是备忘录模式&#xff1f;二、实现三、优缺点优点缺点四、适用环境一、什么是备忘录模式&#xff1f; 在软件构建过程中&#xff0c;某些对象…

3.ORM实践

文章目录3.1 介绍Spring Data JPAJPA&#xff08;Java Persistence API&#xff09;标准HibernateSpring DataSpring Data JPA引入依赖3.2 定义JPA的实体对象常用JPA注解实体主键映射关系常用lombok注解3.3 SpringBucks线上咖啡馆实战项目&#xff08;1&#xff09;项目目标&am…