SpringMVC-RESTful架构风格

news2024/11/24 20:49:57

目录

RESTful架构风格

1、RESTful概述

2、RESTful的六大原则

3、RESTful的实现

4、HiddenHttpMethodFilter

RESTful风格的CRUD

1、环境搭建

2、功能需求

3、功能:访问首页

4、功能:查询所有数据

5、功能:删除一条数据

6、功能:添加一条数据

SpringMVC处理静态资源

1、tomcat自己的web.xml

2、关于DefaultServlet


RESTful架构风格

1、RESTful概述

什么是REST

REST:Representational State Transfer,表述层资源状态转移。(表现层:视图+控制器)

它是在2000年,由Http协议的主要编写者Roy Fielding提出的,他觉得所有人都在胡乱使用Http,违背了他的设计思想。他就站出来,规定了一个web应用的,功能强、性能好、适宜通信的架构。这就是REST,它指的是一组架构约束条件和原则

如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构风格

REST本身并没有创造新的技术、组件或服务,简单来说,REST的目的就是设立规定,促使人们更好地使用Http协议。理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。

不遵守REST规范,也能编写出web应用,这就类似于不使用设计模式也能开发程序一样。但基本都在使用。

资源

项目最终会部署到服务器上,被访问。

服务器中,万物皆资源。每个资源都是服务器上的一个可命名的抽象概念。

与面向对象的设计理念类似,资源是以名词为核心来组织的,而不是方法

资源可以是一个文件,一张数据库表,或是更抽象的概念,比如一个视图,一个参数之类。

一个资源可以由一个或多个URI来标识。

URI

URI既是资源的名称,也是资源在web上的地址。客户端通过URI与资源进行交互。

REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露。 URI 的设计只要负责把资源通过合理方式暴露出来就可以了。对资源的操作与它无关,操作是通过 HTTP动词来体现,所以REST 通过 URI 暴露资源时,会强调不要在 URI 中出现动词。

资源的状态表述

资源的表述,指某个资源在某个特定时刻的状态的描述。这种状态,可以在客户端-服务器之间交换。

资源的表述可以有多种格式,例如HTML、XML、JSON、纯文本、图片、音视频等。

统一资源在请求-响应方向的表述,通常使用不同的格式

资源状态表述的转移

状态转移,指在客户端-服务器端之间转移 资源状态的表述。

通过转义和操作资源的表述,来实现间接操作资源的目的。

2、RESTful的六大原则

Roy Fielding在提出RESTful时,也阐述了REST架构的6大原则。他希望所有web应用都符合这六个特征,相当于是web应用的设计原则

如果某个服务违反了其他任意一项准则,严格意义上不能称之为RESTful风格。

1、C-S架构

数据的存储在Server端,Client端只需使用就行。两端彻底分离的好处使client端代码的可移植性变强,Server端的拓展性变强。两端单独开发,互不干扰。

2、无状态

http请求本身就是无状态的,基于C-S架构,客户端的每一次请求都必须带有充分的信息,才能够让服务端识别。

服务端能够根据请求的各种参数,无需保存客户端的状态,将响应正确返回给客户端。

无状态的好处:无状态的特征大大提高的服务端的健壮性和可拓展性。

无状态的坏处:每次请求必须携带身份状态信息,造成传输数据的冗余。但影响很细微,可以忽略

3、统一资源接口

这个才是REST架构的核心,统一的接口对于RESTful服务非常重要。客户端只需要关注实现接口就可以,接口的可读性加强,使用人员方便调用。

4、一致的数据格式

5、系统分层

客户端通常无法表明自己是直接还是间接与端服务器进行连接,分层时同样要考虑安全策略。

6、可缓存

管理得当的缓存会部分地或完全地除去客户端和服务端之间的交互,进一步改善性能和延展性。

3、RESTful的实现

REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。 它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源

操作传统方式REST风格
查询操作getUserById?id=1user/1 --> get请求方式
保存操作saveUseruser --> post请求方式
删除操作deleteUser?id=1user/1 --> delete请求方式
更新操作updateUseruser --> put请求方式

4、HiddenHttpMethodFilter

由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?

SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求

HiddenHttpMethodFilter如何使用?

HiddenHttpMethodFilter 处理put和delete请求的条件:

  • 当前请求的请求方式必须为post
  • 当前请求必须传输请求参数_method

满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式

在web.xml中注册HiddenHttpMethodFilter

<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

注意,在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter

即先注册字符编码过滤器,再注册Http隐藏方法过滤器

HiddenHttpMethodFilter需要获取请求方式,也属于请求参数,那就必须在设置好字符编码之后再去获取。

在前端页面中,使用隐藏域传递请求参数_method

<form th:action="@{/user}" method="post">修改用户信息
    <input type="hidden" name="_method" value="put"/>
    用户名:<input type="test" name="username"/>
    密码:<input type="password" name="password"/>
    <input type="submit" value="提交">
</form><br>

此时,点击提交按钮,发出的就是一个put类型的请求

RESTful风格的CRUD

1、环境搭建

创建实体类

public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    //1 male, 0 female
    private Integer gender;
    ...
}

创建DAO,使用map集合模拟数据库资源

@Repository
public class EmployeeDAO {

    private static Map<Integer, Employee> employees = null;

    static {
        employees = new HashMap<Integer, Employee>();
        employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
        employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
        employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
        employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
        employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
    }

    private static Integer initId = 1006;

    public void save(Employee employee) {
        if (employee.getId() == null) {
            employee.setId(initId++);
        }
        employees.put(employee.getId(), employee);
    }

    public Collection<Employee> getAll() {
        return employees.values();
    }

    public Employee get(Integer id) {
        return employees.get(id);
    }

    public void delete(Integer id) {
        employees.remove(id);
    }
}

@Repository 持久层组件标记

创建Controlle

public class EmployeeController {

    private EmployeeDAO employeeDAO;

    
}

创建私有成员变量DAO,后续使用依赖注入

2、功能需求

功能URL 地址请求方式
访问首页/GET
查询全部数据/employeeGET
删除/employee/2DELETE
跳转到添加数据页面/toAddGET
执行保存/employeePOST
跳转到更新数据页面/employee/2GET
执行更新/employeePUT

使用RESTful风格,对相同资源的不同操作,请求地址相同,不同请求方式对应不同的操作。

3、功能:访问首页

由于只是页面跳转的功能,可以使用view-controller标签配置。

<mvc:view-controller path="/" view-name="index"/>

<mvc:annotation-driven />

首页的前端页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h3>RESTful风格的员工CRUD系统</h3>
        <a th:href="@{/employee}">查看所有员工信息</a>

    </body>
</html>

4、功能:查询所有数据

分析

不使用ajax异步访问,就只能先获取数据,再转发到其他页面来展示。

传参流程

首页点击超链接,被Spring前端控制器拦截,匹配到控制器方法;

控制器方法中,执行DAO的相应方法,将结果存入request域对象中,并转发到结果页面;

结果页面上,使用视图模板引擎获取域对象中的相关数据,并展示。

控制器方法

@GetMapping("/employee")
public String getEmployeeList(Model model){
    Collection<Employee> employeeList = employeeDAO.getAll();
    model.addAttribute("employeeList", employeeList);
    return "employee_list";
}

前端页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>员工信息</title>
    </head>
    <body>

        <table border="1" cellspacing="0" cellpadding="0" style="text-align: center">
            <tr>
                <th colspan="5">全部员工信息</th>
            </tr>
            <tr>
                <th>id</th>
                <th>lastName</th>
                <th>email</th>
                <th>gender</th>
                <th>options</th>
            </tr>
            <tr th:each="employee : ${employeeList}">
                <td th:text="${employee.id}"></td>
                <td th:text="${employee.lastName}"></td>
                <td th:text="${employee.email}"></td>
                <td th:text="${employee.gender}"></td>
                <td>
                    <a href="">Delete</a>
                    <a href="">Update</a>
                </td>
            </tr>
        </table>

    </body>
</html>

效果

5、功能:删除一条数据

分析

  1. 删除操作,需要使用DELETE请求方式,则需要在Delete超链接上,绑定一个post表单的提交事件,该表单使用隐藏域,设置_method为DELETE

  2. 点击删除超链接,需要传递该行的员工id,动态获取数据并拼接。拼接有两种方法

 <!--方法一-->
 <a th:href="@{/employee/}+${employee.id}">Delete</a>
 																					
 <!--方法二-->
 <a th:href="@{'/employee/'+${employee.id}}">Delete</a>

步骤

  1. 创建发送delete请求方式的form表单
  2. 给delete超链接绑定事件
  3. 引入vue.js
  4. 给delete超链接绑定单击事件
  5. 通过vue处理单击事件
  6. 编写控制器方法,获取前端请求携带的id信息,调用DAO,根据id删除该数据

创建发送delete请求方式的form表单

<form id="deleteForm" method="post">
    <input type="hidden" name="_method" value="delete"/>
</form>

引入vue.js

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>

需要在webapp下创建static/js目录,放入vue.js

给delete超链接绑定单击事件

<a @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">Delete</a>

这里做了两件事情:

  1. 给超链接绑定了单击事件,方法名称为deleteEmployee
  2. 拼接了请求路径,用RESTful风格传参,请求路径包含该行的id值

通过vue处理单击事件

<script type="text/javascript">
    var vue = new Vue({
        el:"#dataTable",
        methods:{
            deleteEmployee:function (event){
                //获取deleteForm这个表单元素
                var deleteForm = document.getElementById("deleteForm");
                //获取当前触发事件的href属性,即employee/1001
                deleteForm.action = event.target.href;
                //提交表单
                deleteForm.submit();
                //取消默认行为,阻止submit跳转页面
                event.preventDefault();
            }
        }
    });
</script>

注意

如果不做配置,vue.js无法被获取到,单击事件也就不起作用。

因为SpringMVC默认无法访问静态资源。vue.js会被前端控制器 / 拦截到,显然没有它对应的控制器方法,返回了404

需要在SpringMVC配置文件中,引入默认的servlet,开放对静态资源的访问

<mvc:default-servlet-handler/>

静态资源在访问时,会先被前端控制器进行处理,如果前端控制器找不到对应的请求映射,就会交给默认的servlet来处理。如果默认的servlet找到了相对应的资源,就访问资源。如果找不到,就返回404。

控制器方法

@DeleteMapping("/employee/{id}")
public String delEmployee(@PathVariable Integer id){
    employeeDAO.delete(id);
    return "redirect:/employee";
}

6、功能:添加一条数据

在前端页面,设置添加员工的入口

<th>options(<a th:href="@{/toAdd}">添加员工</a>)</th>

效果

点击“添加员工”,跳转到添加员工的页面。用view-controller实现

<mvc:view-controller path="/toAdd" view-name="employee_add"/>

 编写添加页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>添加员工信息</title>
    </head>
    <body>
        <form th:action="@{/employee}" method="post">
            lastName:<input type="text" name="lastName"/><br>
            email:<input type="text" name="email"/><br>
            gender:<input type="radio" name="gender" value="1"/>男
            <input type="radio" name="gender" value="0"/>女<br>
            <input type="submit" value="提交"/>
            <input type="reset" value="重置"/>
        </form>
    </body>
</html>

样式

控制器方法

@GetMapping("/employee/{id}")
public String getEmployeeById(@PathVariable Integer id, Model model){
    //根据id获取员工对象
    Employee employee = employeeDAO.get(id);
    //将员工对象存入request域中,用于数据回显
    model.addAttribute("employee", employee);
    return "employee_update";
}

@PutMapping("/employee")
public String updateEmployee(Employee employee){
    employeeDAO.save(employee);
    return "redirect:/employee";
}

 修改的前端页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>更新员工数据</title>
    </head>
    <body>

        <form th:action="@{/employee}" method="post">
            <input type="hidden" name="_method" value="put"/>
            <input type="hidden" name="id" th:value="${employee.id}"/>
            lastName:<input type="text" name="lastName" th:value="${employee.lastName}"/><br>
            email:<input type="text" name="email" th:value="${employee.lastName}"/><br>
            gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}"/>男
            <input type="radio" name="gender" value="0" th:field="${employee.gender}"/>女<br>
            <input type="submit" value="提交"/>
            <input type="reset" value="重置"/>
        </form>

    </body>
</html>
  1. 注意这里实现勾选框 回显的方式

th:field="${employee.gender}"可用于单选框或复选框的回显

若单选框的value和employee.gender的值一致,则添加checked="checked"属性

     2. 注意,需要使用隐藏域传递id,达到覆盖原有数据的目的

SpringMVC处理静态资源

传统的web项目中,负责处理静态资源的是“默认的Servlet”

1、tomcat自己的web.xml

存放位置

tomcat目录下的conf目录内。

tomcat的web.xml,和工程中web.xml的关系

tomcat的web.xml文件,是全局配置,作用于部署在tomcat的所有web应用。

web应用中的web.xml,只针对于自身。

如果当前应用内的web.xml配置,与tomcat的web.xml配置产生了冲突,则以web应用的配置为准。

2、关于DefaultServlet

tomcat的web.xml中,注册了DefaultServlet

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

DefaultServlet的url-pattern是 / 说明访问所有资源(除了jsp)时,都会进入DefaultServlet。

在SpringMVC中,DefaultServlet和DispacherServlet产生了冲突。冲突时,以当前工程中的配置为准,也就是DispacherServlet。

就是说,所有的请求都会被DispacherServlet处理。

但DispacherServlet处理请求的方式是,将请求地址与控制器方法的映射路径匹配,直到找到相对应的请求映射。但这种方式无法访问静态资源。例如需要访问vue.js资源,而此路径并没有相对应的控制器方法,所以会报404,找不到该资源。

而DefaultServlet可以找到vue.js这个请求的资源。

所以SpringMVC默认不能访问静态资源,必须手动开启对静态资源的访问,在核心配置中进行如下的配置:

<!--开启对静态资源的访问-->
<mvc:default-servlet-handler/>

但此时,所有的请求都将被DefaultServlet处理,则只能访问到静态资源,请求映射将全部失效

所以,还需要配置开启注解驱动的标签

<!--开启mvc注解驱动-->
<mvc:annotation-driven>

同时配置这两个标签,就能实现,请求先被DispacherServlet处理,如果找不到,再被DefaultServlet处理

关于控制台信息

如果不添加日志功能,只有DispacherServlet的处理结果会被输出,而DefaultServlet的结果不会显示。

 

 

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

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

相关文章

Web服务

安装 WEB 服务&#xff1b; ~ 服务以用户 webuser 系统用户运行&#xff1b; ~ 限制 web 服务只能使用系统 500M 物理内存&#xff1b; ~ 全站点启用 TLS 访问&#xff0c;使用本机上的“CSK Global Root CA”颁 发机构颁发&#xff0c;网站证书信息如下&#xff1a; C…

财报解读:涅槃重生之后,新东方还想再造一个“文旅甄选”?

新东方逐渐走出了“微笑曲线”。 图源&#xff1a;新东方2023财年Q3财报 2023年4月19日&#xff0c;新东方披露了2023财年Q3财报&#xff08;截至2023年2月28日止&#xff09;&#xff0c;营收7.5亿美元&#xff0c;同比增长22.8%&#xff1b;归母净利润为8165万美元&#xff…

2023年4月份上新的视频领域分割模型设计系列论文(附下载链接)

来源&#xff1a;投稿 作者&#xff1a;王老师 编辑&#xff1a;学姐 论文1 论文标题&#xff1a; Boosting Video Object Segmentation via Space-time Correspondence Learning 论文链接&#xff1a; https://arxiv.org/pdf/2304.06211v1.pdf代码链接&#xff1a;暂未开源 …

QGIS数据可视化学习笔记02——CSV数据和表连接

在其他的GIS软件中&#xff0c;表的连接操作是十分常用的操作&#xff0c;在QGIS中也是一样的&#xff0c;接下来我们介绍QGIS中属性表之间的连接以及如何添加CSV数据到属性表中。 1、表的连接 &emsp如关系型数据库一样&#xff0c;两表连接的前提是&#xff0c;两个表中都…

使用大模型进行代码阅读——关于LLaMA模型代码的一些思考

使用大模型进行代码阅读 关于LLaMA模型代码的一些思考 关于这个模型的代码其实挺简单的&#xff0c;如果了解transformer decoder的代码&#xff0c;这个代码理解起来相对会比较容易一些&#xff0c;但是在这个代码里面有几个问题&#xff0c;是自己的一些思考或者是疑问点吧…

【NLP教程】用python调用百度AI开放平台进行情感倾向分析

一、背景 Hi&#xff0c;大家&#xff01;我是 马哥python说 &#xff0c;一名10年程序猿。 今天我来演示一下&#xff1a;通过百度AI开放平台&#xff0c;利用python调用百度接口进行中文情感倾向分析&#xff0c;并得出情感极性分为积极、消极还是中性以及置信度结果。 二…

HTB-Cache

HTB-Cache 信息收集80端口 立足www-data -> ashash -> luffyluffy -> root 信息收集 80端口 主页是一些hacker的介绍。 作者介绍能收集可能的用户名ASH以及有一个其他项目可能有用&#xff0c;如下&#xff1a; “Check out his other projects like Cache:HMS(Hosp…

【Python小技巧】使用Gradio 构建基于ChatGPT的AI绘图 Web 应用(附源码)

文章目录 前言一、Gradio是什么&#xff1f;二、使用Gradio构建基于ChatGPT的 Web 应用1. 安装gradio库2. 安装openai库&#xff08;ChatGPT的python库&#xff09;3. Web 应用示例&#xff08;源代码&#xff09; 总结 前言 都说ChatGPT也可以生成图片&#xff0c;好奇的我也…

母婴市场竞争激烈,如何通过软文营销脱颖而出

如今&#xff0c;随着宝宝数量增加以及人们对孩子的重视程度的增加&#xff0c;母婴市场愈发火爆。然而&#xff0c;母婴行业的竞争也越来越激烈&#xff0c;企业需要不断开拓新市场才能生存。在这样的情况下&#xff0c;软文营销成为了母婴企业拓展市场的一种有效方式。 首先&…

Docker Compose的安装教程、使用教程示例

Docker Compose的安装教程、使用教程示例 安装教程下载修改权限创建软链测试是否安装成功 使用教程1: 定义python应用2.创建容器的Dockerfile文件3.定义docker-compose脚本web容器&#xff1a;redis容器&#xff1a; 4.使用Compose构建并运行您的应用程序5.访问浏览器 安装教程…

【人体姿态估计】(一)原理介绍

【人体姿态估计】&#xff08;一&#xff09;原理介绍 一、背景 人体姿态估计本质上是一个关键点检测的项目&#xff1b; 关键点检测在生活中的应用十分广泛&#xff0c;包括人脸识别、手势识别&#xff0c;而人体姿态估计则是对身体的关键点进行检测&#xff1b; 本文将介…

Python学习中更适合中国宝宝的报错整理~

人生苦短&#xff0c;我用python 最近有很多小伙伴说实在是看不懂报错的类型&#xff0c; 这次就给大家整理好了基本上玩python会遇到的报错问题 更适合中国宝宝体制喔~ python 安装包资料:点击此处跳转文末名片获取 在python中不同的异常可以用不同的类型 &#xff08;pyt…

【Python小技巧】使用Gradio构建基于ChatGPT的 Web 应用(附源码)

文章目录 前言一、Gradio是什么&#xff1f;二、使用Gradio构建基于ChatGPT的 Web 应用1. 安装gradio库2. 安装openai库&#xff08;ChatGPT的python库&#xff09;3. Web 应用示例&#xff08;源代码&#xff09; 总结 前言 随着人工智能的不断发展&#xff0c;各种智能算法越…

爆料一家互联网中厂的年终奖,真香。

前不久刷到宇宙条32岁员工14万的月薪截图&#xff0c;突然想起来已经快四月底了&#xff0c;正是各大互联网公司年终奖开奖的时候&#xff0c;但相比以往&#xff0c;今年互联网圈好像安静了很多。各种“凡尔赛”的年终奖金额刷屏的情况不复存在。 各家大厂都暗戳戳地分完了奖…

js实现iframe框架的面包屑功能

js实现iframe框架的面包屑功能 什么是iframe框架什么是面包屑面包屑的js实现思路代码实现小结 什么是iframe框架 iFrame全称Inline Frame是HTML中的一个标签&#xff0c;用于在一个HTML文档中嵌入另一个HTML文档。iFrame框架可以将一个HTML文档嵌入到另一个HTML文档中的一个独…

后代选择器

知识点&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" content"IEedge"> <meta name"viewport" c…

深度学习入门到实践:相关基础概述

绪论 深度学习&#xff08;Deep Learning&#xff09;是近年来发展十分迅速的研究领域&#xff0c;并且在人工智能的很多子领域都取得了巨大的成功。从根源来讲&#xff0c;深度学习是机器学习的一个分支&#xff0c;是指一类问题以及解决这类问题的方法。     深度学习问题…

【Arduino SD卡和数据记录教程】

【Arduino SD卡和数据记录教程】 1. 前言2. 工作原理3. Arduino SD 卡模块代码4. Arduino SD卡数据记录1. 前言 在本Arduino教程中,我们将学习如何将SD卡模块与Arduino板一起使用。此外,结合DS3231实时时钟模块,我们将制作一个数据记录示例,将温度传感器的数据存储到SD卡中…

分布式消息队列Kafka(一)

一.kafka基础架构 &#xff08;1&#xff09;Producer&#xff1a;消息生产者&#xff0c;就是向 Kafka broker 发消息的客户端。 &#xff08;2&#xff09;Consumer&#xff1a;消息消费者&#xff0c;向 Kafka broker 取消息的客户端。 &#xff08;3&#xff09;Consumer…

每日一个小技巧:1分钟告诉你截图翻译软件哪个好用

随着互联网的发展&#xff0c;我们对各种语言文字的翻译需求越来越高。在翻译时&#xff0c;大家经常会遇到一些生词或专业术语&#xff0c;这时候我们就需要使用翻译软件来帮助理解。但是&#xff0c;有些时候大家需要翻译的内容并不是一段文本&#xff0c;而是一张图片或截图…