diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index d789aa4..d5055f0 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -52,7 +52,8 @@ export default withMermaid({ { text: 'SpringBoot Starter原理', link: '/Web-Backend/SpringBoot/SpringBootStarter' }, { text: 'SpringBoot Bean简介', link: '/Web-Backend/SpringBoot/SpringBootBean' }, { text: 'SpringBoot3原生镜像', link: '/Web-Backend/SpringBoot/SpringBoot3-GraalVM' }, - { text: 'SpingBoot技巧', link: '/Web-Backend/SpringBoot/SpingBoot-Skills' } + { text: 'SpingBoot技巧', link: '/Web-Backend/SpringBoot/SpingBoot-Skills' }, + { text: 'SpringBoot Common', link: '/Web-Backend/SpringBoot/SpringBoot-Common' } ] }, { @@ -125,7 +126,8 @@ export default withMermaid({ { text: 'SpringBoot Starter原理', link: '/Web-Backend/SpringBoot/SpringBootStarter' }, { text: 'SpringBoot Bean简介', link: '/Web-Backend/SpringBoot/SpringBootBean' }, { text: 'SpringBoot3原生镜像', link: '/Web-Backend/SpringBoot/SpringBoot3-GraalVM' }, - { text: 'SpingBoot技巧', link: '/Web-Backend/SpringBoot/SpingBoot-Skills' } + { text: 'SpingBoot技巧', link: '/Web-Backend/SpringBoot/SpingBoot-Skills' }, + { text: 'SpringBoot Common', link: '/Web-Backend/SpringBoot/SpringBoot-Common' } ] }, { text: 'FastAPI', diff --git a/docs/.vitepress/theme/components/ArticleMetadata.vue b/docs/.vitepress/theme/components/ArticleMetadata.vue new file mode 100644 index 0000000..e9e8811 --- /dev/null +++ b/docs/.vitepress/theme/components/ArticleMetadata.vue @@ -0,0 +1,66 @@ + + + + + + + + 字数: {{ wordCount }} 字 + + 时长: {{ readTime }} 分钟 + + + + + \ No newline at end of file diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index f62df37..172b376 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,5 +1,6 @@ import DefaultTheme from 'vitepress/theme' import Confetti from "./components/Confetti.vue"; +import ArticleMetadata from "./components/ArticleMetadata.vue" import type { EnhanceAppContext } from 'vitepress' import { onMounted, watch, nextTick } from 'vue' import { useRoute } from 'vitepress' @@ -27,5 +28,6 @@ export default { }, enhanceApp({ app }: EnhanceAppContext) { app.component("Confetti", Confetti); + app.component("ArticleMetadata", ArticleMetadata); }, }; diff --git a/docs/.vitepress/theme/utils/functions.ts b/docs/.vitepress/theme/utils/functions.ts new file mode 100644 index 0000000..1cd87f4 --- /dev/null +++ b/docs/.vitepress/theme/utils/functions.ts @@ -0,0 +1,19 @@ +const pattern + = /[a-zA-Z0-9_\u0392-\u03C9\u00C0-\u00FF\u0600-\u06FF\u0400-\u04FF]+|[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u3040-\u309F\uAC00-\uD7AF]+/g + +export function countWord(data: string) { + const m = data.match(pattern) + let count = 0 + if (!m) { + return 0 + } + for (let i = 0; i < m.length; i += 1) { + if (m[i].charCodeAt(0) >= 0x4E00) { + count += m[i].length + } + else { + count += 1 + } + } + return count +} \ No newline at end of file diff --git a/docs/DevOps/ElasticSearch.md b/docs/DevOps/ElasticSearch.md index eda7e44..18f8a8d 100644 --- a/docs/DevOps/ElasticSearch.md +++ b/docs/DevOps/ElasticSearch.md @@ -1,3 +1,5 @@ + + ## 一、基础概念 ## 二、Docker部署 diff --git a/docs/DevOps/Jenkins.md b/docs/DevOps/Jenkins.md index a9dd0b3..4f1a5b6 100644 --- a/docs/DevOps/Jenkins.md +++ b/docs/DevOps/Jenkins.md @@ -1,3 +1,5 @@ + + # 一、Jenkins安装 ## 1.1 简介 Jenkins 是一款开源的持续集成(Continuous Integration, CI) 和持续交付(Continuous Delivery, CD) 自动化工具,广泛应用于软件开发流程中,帮助团队实现代码构建、测试、部署的自动化,从而提升开发效率、减少人为错误,并确保软件质量。 diff --git a/docs/DevOps/OpenObserve.md b/docs/DevOps/OpenObserve.md index 0f859ac..d08e44e 100644 --- a/docs/DevOps/OpenObserve.md +++ b/docs/DevOps/OpenObserve.md @@ -1,3 +1,5 @@ + + ## 一、基础概念 ### 1.1 Fluent Bit [Fluent Bit](https://fluentbit.io/) 是一个开源的、轻量级、高性能的日志处理器和转发器。Fluent Bit 的核心任务是:**从各种来源收集日志、指标和追踪数据,进行处理和过滤,然后将其发送到一个或多个目的地。** diff --git a/docs/Flutter/DartGuide.md b/docs/Flutter/DartGuide.md index 8e98aea..e6c8420 100644 --- a/docs/Flutter/DartGuide.md +++ b/docs/Flutter/DartGuide.md @@ -1,3 +1,5 @@ + + # 一、基础语法 ## 1.1 变量与常量 ```dart diff --git a/docs/Flutter/FlutterGuide.md b/docs/Flutter/FlutterGuide.md index 0f500d0..5f94272 100644 --- a/docs/Flutter/FlutterGuide.md +++ b/docs/Flutter/FlutterGuide.md @@ -1,3 +1,5 @@ + + # 应用核心Widget ```dart // Flutter 应用的入口,配置主题、路由、国际化等。 diff --git a/docs/Flutter/Practice.md b/docs/Flutter/Practice.md index 94ee240..d25a3c0 100644 --- a/docs/Flutter/Practice.md +++ b/docs/Flutter/Practice.md @@ -1,3 +1,5 @@ + + # 安卓签名 每次安装/升级软件必须使用同一个签名,否则会将本地数据全部清空。 ## 1. 生成密钥库文件 diff --git a/docs/Web-Backend/FastAPI/FastAPI-Guide.md b/docs/Web-Backend/FastAPI/FastAPI-Guide.md index 46894e9..8b38e86 100644 --- a/docs/Web-Backend/FastAPI/FastAPI-Guide.md +++ b/docs/Web-Backend/FastAPI/FastAPI-Guide.md @@ -1,3 +1,5 @@ + + # 一、简介 [FastAPI](https://fastapi.tiangolo.com/zh/) 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 并基于标准的 Python 类型提示。 安装: diff --git a/docs/Web-Backend/FastAPI/OAuth2.md b/docs/Web-Backend/FastAPI/OAuth2.md index b786fd0..fd1c20a 100644 --- a/docs/Web-Backend/FastAPI/OAuth2.md +++ b/docs/Web-Backend/FastAPI/OAuth2.md @@ -1,3 +1,5 @@ + + # 一、基础概念 ## 1.1 OAuth2 Password Bearer 模式 用于**用户名+密码**登录,获取**access_token**。 diff --git a/docs/Web-Backend/Others/Flyway.md b/docs/Web-Backend/Others/Flyway.md index eb67298..b5afc19 100644 --- a/docs/Web-Backend/Others/Flyway.md +++ b/docs/Web-Backend/Others/Flyway.md @@ -1,3 +1,5 @@ + + # 一、简介 Flyway 是一个开源的数据库版本控制工具,它极大地简化了数据库的迁移和版本管理。它的核心思想是像**用 Git 管理代码一样来管理数据库的结构**。 diff --git a/docs/Web-Backend/Others/MyBatis.md b/docs/Web-Backend/Others/MyBatis.md index 8bc7c05..481dae5 100644 --- a/docs/Web-Backend/Others/MyBatis.md +++ b/docs/Web-Backend/Others/MyBatis.md @@ -1,3 +1,5 @@ + + # 一、简介 [MyBatis](https://mybatis.org/mybatis-3/)是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 diff --git a/docs/Web-Backend/Others/MySQL-Knowledge.md b/docs/Web-Backend/Others/MySQL-Knowledge.md index 2881f0d..1ea3ca7 100644 --- a/docs/Web-Backend/Others/MySQL-Knowledge.md +++ b/docs/Web-Backend/Others/MySQL-Knowledge.md @@ -1,3 +1,5 @@ + + # MySQL知识点 ## 一、基础知识 ### 1.1 数据类型 diff --git a/docs/Web-Backend/Others/Netword-Program.md b/docs/Web-Backend/Others/Netword-Program.md index a5746dd..1438e14 100644 --- a/docs/Web-Backend/Others/Netword-Program.md +++ b/docs/Web-Backend/Others/Netword-Program.md @@ -1,36 +1,38 @@ -# TCP -## 定义 - TCP(Transmission Control Protocol,传输控制协议)是互联网核心的面向连接、可靠、字节流的传输层协议,工作在 OSI 模型的传输层(TCP/IP 模型的传输层),基于 IP 协议提供端到端的可靠数据传输服务。它是 HTTP、HTTPS、WebSocket、MQTT 等应用层协议的底层依赖,核心目标是解决 IP 协议 “无连接、不可靠、无顺序” 的缺陷,确保数据在不可靠的网络中准确、完整、有序地传输。 + -## 特性 -### 面向连接 +# 一、TCP +## 1.1 定义 + TCP(Transmission Control Protocol,传输控制协议)是互联网核心的**面向连接、可靠、字节流**的传输层协议,工作在 OSI 模型的传输层(TCP/IP 模型的传输层),基于 IP 协议提供端到端的可靠数据传输服务。它是 HTTP、HTTPS、WebSocket、MQTT 等应用层协议的底层依赖,核心目标是解决 IP 协议 “无连接、不可靠、无顺序” 的缺陷,确保数据在不可靠的网络中准确、完整、有序地传输。 + +## 1.2 特性 +### 1.2.1 面向连接 通信前必须完成「三次握手」建立连接,通信后通过「四次挥手」释放连接: - 三次握手:客户端发 SYN → 服务器回 SYN+ACK → 客户端发 ACK(确保双方收发能力正常); - 四次挥手:客户端发 FIN → 服务器回 ACK → 服务器发 FIN → 客户端回 ACK(确保数据传输完毕)。 -### 可靠传输 +### 1.2.2 可靠传输 - 序号与确认号:每个字节都有序号,接收方收到后回复确认号,未收到则发送方重传; - 超时重传:发送方未在规定时间收到确认,自动重传数据; - 流量控制:通过滑动窗口机制,防止发送方发送过快导致接收方缓冲区溢出; - 拥塞控制:通过慢启动、拥塞避免等算法,适应网络带宽变化。 -### 面向字节流 +### 1.2.3 面向字节流 TCP 将应用层数据视为连续的字节流,不保留应用层数据的边界(与 UDP 的 “数据报” 模式不同): - 发送方:应用层数据被拆分为 TCP 报文段(Segment)发送,拆分规则由 TCP 协议决定(如 MSS 限制)。 - 接收方:将收到的报文段按顺序重组为完整的字节流,再交给应用层,确保数据顺序与发送时一致。 -### 有序传输 +### 1.2.4 有序传输 TCP 报文段头部包含 “序号(Sequence Number)” 和 “确认号(Acknowledgment Number)”: - 序号(SN):标识发送方当前发送的字节流位置(如序号为 100 表示当前报文段的第一个字节是整个字节流的第 100 字节)。 - 确认号(ACK):标识接收方期望下次接收的字节流位置(如确认号为 200 表示已正确接收前 199 字节,下次需从 200 字节开始接收)。 - 接收方通过序号排序报文段,丢弃重复报文,确保按发送顺序交付数据。 -### 全双工通信 +### 1.2.5 全双工通信 TCP 连接是双向的,双方可同时发送和接收数据,无需等待对方结束发送: - 每个方向都有独立的发送缓冲区和接收缓冲区,以及独立的滑动窗口用于流量控制。 - 示例:客户端发送数据的同时,服务器可同步向客户端返回响应,无需等待客户端发送完毕。 -## 优缺点 +## 1.3 优缺点 优点: 1. 可靠、有序、无丢包 2. 支持流量 / 拥塞控制 @@ -41,17 +43,404 @@ 2. 头部开销大(20-60 字节) 3. 不适合实时性要求极高的场景(如直播低延迟) +## 1.4 Python实现 + TCP服务器: +```python +import socket +import signal +import threading +from concurrent.futures import ThreadPoolExecutor +from dotenv import load_dotenv +import os + +from model.mqtt import UNKNOWN_MESSAGE +from processor.tcp_processor import TcpMessageProcessor +from config.logger_config import logger + + +class TCPServer: + def __init__(self): + load_dotenv() + + self.host = os.getenv("TCP_HOST", 'localhost') + self.port = int(os.getenv("TCP_PORT", 9100)) + + self.max_workers = 10 + self.timeout = 300 + + self.server_socket = None + self.running = False + self.thread_pool = ThreadPoolExecutor(max_workers=self.max_workers) + self.message_processor = TcpMessageProcessor() + + # 已经连接的客户端 + self.clients = {} + self.client_lock = threading.Lock() + + self.server_thread = None + + signal.signal(signal.SIGTERM, self._handle_signal) + signal.signal(signal.SIGINT, self._handle_signal) + + def _handle_signal(self, signum, frame): + """处理终止信号,触发优雅关闭""" + logger.info(f"收到信号 {signum},准备关闭服务器...") + self.running = False + + def _start_loop(self): + """启动服务器""" + try: + # 创建 TCP/IP socket AF_INET: IPv4地址族 SOCK_STREAM: TCP协议(面向连接) + self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # 设置SO_REUSEADDR选项,允许重用地址和端口,避免服务端重启时出现 “地址已被占用” 的错误 + self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + # 绑定到指定主机和端口 0.0.0.0 表示所有主机 + self.server_socket.bind((self.host, self.port)) + # 设置最大等待连接数 + self.server_socket.listen(5) + # 设置socket超时时间(1.0秒) + self.server_socket.settimeout(1.0) + self.running = True + + logger.info(f"TCP服务器启动,监听 {self.host}:{self.port} " + f"(最大线程: {self.max_workers}, 超时: {self.timeout}s)") + + while self.running: + try: + # accept() 会阻塞直到有客户端连接 + # client_socket: 与客户端通信的新socket client_address: 客户端地址(ip, port)元组 + client_socket, client_address = self.server_socket.accept() + # 获取客户端IP + client_ip = client_address[0] + # 设置客户端socket超时 + # 防止客户端长时间不发送数据 + client_socket.settimeout(self.timeout) + logger.info(f"新连接: {client_address}") + + # 存储客户端 + with self.client_lock: + self.clients[client_ip] = client_socket + + # 提交到线程池处理 + # 提交到线程池是为了让服务器能同时服务多个客户端,而不让一个慢客户端阻塞所有其他客户端 + self.thread_pool.submit(self.handle_client, client_socket, client_ip) + except socket.timeout: + continue + except Exception as e: + if self.running: + logger.error(f"接受连接失败: {str(e)}") + except Exception as e: + logger.error(f"TCP服务器启动失败: {str(e)}") + finally: + self.stop() + + def start(self): + self.server_thread = threading.Thread(target=self._start_loop, daemon=True) + self.server_thread.start() + + def stop(self): + if not self.running: + return + + self.running = False + logger.info("开始关闭服务器...") + + # 移除客户端 + with self.client_lock: + for client_socket in self.clients.values(): + try: + client_socket.close() + except Exception as e: + logger.warning(f"关闭客户端连接失败: {str(e)}") + self.clients.clear() + + # 关闭线程池 + self.thread_pool.shutdown(wait=True) + logger.info("所有客户端处理线程已结束") + + # 关闭连接 + if self.server_socket: + self.server_socket.close() + logger.info(f"服务器已关闭({self.host}:{self.port})") + + def handle_client(self, client_socket, client_ip): + """处理客户端连接""" + try: + while True: + data = client_socket.recv(1024) + if not data: + logger.info(f"客户端 {client_ip} 主动断开连接") + break + + message = data.decode('utf-8').strip() + logger.info(f"收到 {client_ip} 的消息: {message}") + + # 放到消息处理器里面处理 + response = self.message_processor.process(message) + + # 回复消息 + if response != UNKNOWN_MESSAGE: + client_socket.sendall(response.encode('utf-8')) + logger.info(f"回复 {client_ip}: {response}") + except socket.timeout: + logger.warning(f"客户端 {client_ip} 超时未活动") + except Exception as e: + logger.error(f"处理 {client_ip} 出错: {str(e)}") + finally: + # 异常情况下关闭连接 + with self.client_lock: + if client_ip in self.clients: + del self.clients[client_ip] + + try: + client_socket.close() + logger.info(f"客户端 {client_ip} 连接已关闭") + except Exception as e: + logger.warning(f"关闭 {client_ip} 连接失败: {str(e)}") + + def send_to_client(self, client_ip, message): + # 先获取客户端连接(加锁保护) + with self.client_lock: + client_socket = self.clients.get(client_ip) + if not client_socket: + logger.warning(f"客户端 {client_ip} 不存在或已断开连接") + return False + + # 发送消息 + try: + client_socket.sendall(message.encode('utf-8')) + logger.info(f"主动发送消息给 {client_ip}: {message}") + return True + except Exception as e: + logger.error(f"向 {client_ip} 发送消息失败: {str(e)}") + # 发送失败时移除无效连接 + with self.client_lock: + if client_ip in self.clients: + del self.clients[client_ip] + return False + + +tcp_server = TCPServer() +``` + + main.py: +```python +from endpoint.tcp_server import tcp_server +from config.logger_config import logger +import time + +if __name__ == "__main__": + try: + tcp_server.start() + + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + logger.info("收到终止信号,开始关闭程序...") + finally: + tcp_server.stop() + logger.info("程序已退出") + except Exception as e: + logger.critical(f"程序启动失败: {str(e)}", exc_info=True) + exit(1) +``` + + 启动tcp_server后,通过while循环防止主线程退出,从而让后台的TCP服务器线程能继续运行。 + 在TCPServer中,通过while循环持续接收tcp客户端的连接,每当有一个客户端连接时,会提交到线程池中去处理,通过自定义消息处理器,将处理完的结果返回给客户端。 + TCP消息处理器: +::: code-group +```python [抽象消息处理器] +from abc import ABC, abstractmethod + +class AbstractDeviceMessageParser(ABC): + """设备消息解析器基类""" + + @abstractmethod + def check(self, message): + """判断当前解析器是否能处理该消息""" + pass + + @abstractmethod + def parse(self, message): + """解析消息内容""" + pass + + @abstractmethod + def response(self, parsed_data): + """根据解析后的数据生成响应""" + pass +``` + +```python [示例消息处理器] +from datetime import datetime + +from model.band import BandData +from model.mqtt import MqttTopic, MqttData, UNKNOWN_MESSAGE +from parser.abstract_parsers import AbstractDeviceMessageParser +import re + +from utils.index import str_length_to_4hex +from config.logger_config import logger +from endpoint.mqtt_client import mqtt_client + + +class JuweiBandParser(AbstractDeviceMessageParser): + """聚伟手环消息解析器""" + + def __init__(self): + # 聚伟手环消息格式: [MNYD*设备ID*内容长度*内容] + self.pattern = r'MNYD' + self.vendor = "聚伟手环" + self.tag = "MNYD" + self.device_id = "" + + def check(self, message): + """检查是否为聚伟手环的消息格式""" + return re.search(self.pattern, message) is not None + + def parse(self, message): + """解析聚伟手环消息""" + try: + parts = message.strip("[]").split("*") + + self.device_id = parts[1] + content = parts[3] + + return { + 'vendor': self.vendor, + 'device_id': self.device_id, + 'content': content, + } + except Exception as e: + logger.error(f"解析{self.vendor}消息出错: {str(e)}") + return None + + def publish_vital_data(self, item, value): + mqtt_client.publish( + topic=MqttTopic.JuWei_Band_Post.value, + payload=MqttData( + deviceIp="", + deviceId=self.device_id, + payload=BandData(item=item, value=value).model_dump_json(), + ).model_dump_json()) + + def response(self, parsed_data): + """生成聚伟手环的响应消息""" + if not parsed_data: + return UNKNOWN_MESSAGE + + # 处理不同命令 + content = parsed_data['content'] + + parts = content.split(",", 1) + content_tag = parts[0] if len(parts) > 0 else "" + content_value = parts[1] if len(parts) > 1 else "" + + replay = UNKNOWN_MESSAGE + + logger.info(f"解析{content_tag}消息") + + match content_tag: + # PING消息 [MNYD*334588000000156*0004*PING] + case "PING": + replay = "PING,1" + # 日期,步数,翻滚次数,电量百分数,里程数(km) + # [MNYD*334588000000156*0014*KA,120414,50,100,100,100.12] + case "KA": + replay = content_tag + # 位置数据上报 + # case "UD": + # replay = "" + # 报警数据上报 + # [MNYD*334588000000156*00CD*AL,180916,064153,A,22.570512,N,113.8623267, + # E,0.00,154.8,0.0,11,100,100,0,0,00100018,7,0,460,1,9529, + # 21809,155,9529,21242,132,9529,21405,131,9529,63554,131,9529, + # 63555,130,9529,63556,118,9529,21869,116,0,12.4] + case "AL": + replay = content_tag + # 获取服务器端时间 + # [MNYD*YYYYYYYYYYYYYYY*LEN*LGZONE] + case "LGZONE": + now = datetime.now() + current_date = now.date().strftime("%Y-%m-%d") + current_time = now.time().strftime("%H:%M:%S") + replay = f"{content_tag},+8,{current_time},{current_date}" + # 请求位置数据 TODO + case "WG": + replay = content_tag + # 请求电话本设置信息 TODO + case "PHLQ": + replay = "PHL" + # 请求SOS设置信息 TODO + case "SOS": + replay = content_tag + # 终端心率上传 + case "heart": + self.publish_vital_data(content_tag, content_value) + replay = content_tag + # 上传体温数据 [MNYD*334588000000156*0009*temp,36.2] + case "temp": + self.publish_vital_data(content_tag, content_value) + replay = content_tag + # 上传血压数据 [MNYD*334588000000156*000C*blood,150,70] + case "blood": + self.publish_vital_data(content_tag, content_value) + replay = content_tag + # 上传血氧数据 [MNYD*334588000000156*0009*oxygen,97] + case "oxygen": + self.publish_vital_data(content_tag, content_value) + replay = content_tag + # 上传睡眠数据报告 + case "SLEEPRPT": + replay = "SLEEP" + case _: + logger.info("不需要回复") + return UNKNOWN_MESSAGE + + return f"[{self.tag}*{parsed_data['device_id']}*{str_length_to_4hex(replay)}*{replay}]" +``` + +```python [消息处理器] +from model.mqtt import UNKNOWN_MESSAGE +from parser.juwei_band_parser import JuweiBandParser +from config.logger_config import logger + +class TcpMessageProcessor: + """消息处理器,负责将消息路由到正确的设备解析器""" + + def __init__(self): + # 注册所有支持的设备解析器 + self.parsers = [JuweiBandParser()] + + def process(self, message): + """处理消息,返回响应""" + # 尝试找到能处理该消息的解析器 + for parser in self.parsers: + if parser.check(message): + parsed_data = parser.parse(message) + if parsed_data: + logger.info(f"处理{parsed_data['vendor']}消息: {message}") + return parser.response(parsed_data) + + # 没有找到合适的解析器 + logger.warning(f"未识别的消息格式: {message}") + return UNKNOWN_MESSAGE +``` +::: + + # HTTP ## 定义 - HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网的核心协议之一,用于客户端(如浏览器、App)与服务器之间的通信,是万维网(WWW)数据交换的基础。它定义了请求 / 响应的格式、传输规则和状态码等核心机制,支持从简单文本到复杂多媒体(图片、视频、文件)的传输,也是现代 Web 应用的底层通信标准。 + HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网的核心协议之一,用于**客户端(如浏览器、App)与服务器之间的通信**,是万维网(WWW)数据交换的基础。它定义了请求 / 响应的格式、传输规则和状态码等核心机制,支持从简单文本到复杂多媒体(图片、视频、文件)的传输,也是现代 Web 应用的底层通信标准。 ## 特性 ### 请求 - 响应模式 通信由客户端主动发起请求,服务器接收后处理并返回响应,不存在服务器主动向客户端推送数据的情况(HTTP/2 引入 Server Push 扩展,可主动推送关联资源)。 - 一次完整通信流程:客户端建立连接 → 发送请求 → 服务器处理 → 返回响应 → 连接关闭(HTTP/1.1 默认开启长连接 Keep-Alive)。 + 一次完整通信流程:**客户端建立连接 → 发送请求 → 服务器处理 → 返回响应 → 连接关闭**(HTTP/1.1 默认开启长连接 Keep-Alive)。 ### 无状态 - 服务器不会保存客户端的会话状态(如登录状态、浏览记录),每次请求都是独立的,服务器无法通过协议本身识别连续请求是否来自同一客户端。通过 Cookie、Session、Token(如 JWT)等机制补充状态管理。 + **服务器不会保存客户端的会话状态**(如登录状态、浏览记录),每次请求都是独立的,服务器无法通过协议本身识别连续请求是否来自同一客户端。通过 Cookie、Session、Token(如 JWT)等机制补充状态管理。 ## 版本 ### HTTP/1.0(1996 年) @@ -159,8 +548,8 @@ Set-Cookie: sessionId=abc123; Path=/ # WebSocket ## 定义 - WebSocket 是一种全双工、双向、持久化的网络通信协议(属于应用层协议),由 HTML5 规范定义,专门解决 HTTP 协议无法实现服务器主动向客户端推送数据的问题。 - 它的核心特点是:一旦客户端与服务器建立连接,双方就可以在这个连接上实时、双向地发送数据,无需像 HTTP 那样每次通信都由客户端发起请求,非常适合实时通信场景(如聊天、直播弹幕、实时数据监控、在线协作等)。 + WebSocket 是一种**全双工、双向、持久化的网络通信协议**(属于应用层协议),由 HTML5 规范定义,专门解决 HTTP 协议无法实现服务器主动向客户端推送数据的问题。 + 它的核心特点是:**一旦客户端与服务器建立连接,双方就可以在这个连接上实时、双向地发送数据**,无需像 HTTP 那样每次通信都由客户端发起请求,非常适合实时通信场景(如聊天、直播弹幕、实时数据监控、在线协作等)。 | 特性 | HTTP | WebSocket | |---------------------|-------------------------------|-------------------------------| @@ -209,7 +598,7 @@ Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= # 服务器加密后的密 # MQTT ## 定义 - MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一种轻量级、低带宽、低功耗的发布 / 订阅(Publish/Subscribe)模式物联网(IoT)通信协议,由 IBM 于 1999 年设计,核心目标是解决受限设备(如传感器、嵌入式设备)和低带宽、不稳定网络环境下的高效数据传输问题。 + MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一种**轻量级、低带宽、低功耗的发布 / 订阅(Publish/Subscribe)模式物联网(IoT)通信协议**,由 IBM 于 1999 年设计,核心目标是解决受限设备(如传感器、嵌入式设备)和低带宽、不稳定网络环境下的高效数据传输问题。 ## 架构 - 发布者(Publisher):发送消息的设备 / 服务; diff --git a/docs/Web-Backend/Others/RustFS.md b/docs/Web-Backend/Others/RustFS.md index 3ce1444..ed0a229 100644 --- a/docs/Web-Backend/Others/RustFS.md +++ b/docs/Web-Backend/Others/RustFS.md @@ -1,3 +1,5 @@ + + # 简介 [RustFS](https://rustfs.com.cn/) 是一个基于 Rust 构建的高性能分布式对象存储系统。 具体以下特点: diff --git a/docs/Web-Backend/SpringBoot/SpingBoot-Skills.md b/docs/Web-Backend/SpringBoot/SpingBoot-Skills.md index 6a39392..67365b8 100644 --- a/docs/Web-Backend/SpringBoot/SpingBoot-Skills.md +++ b/docs/Web-Backend/SpringBoot/SpingBoot-Skills.md @@ -1,3 +1,5 @@ + + # 一、JsonView JsonView 是 Jackson 提供的注解,用于控制对象序列化/反序列化时包含哪些字段。可以实现: - 不同接口返回不同字段 diff --git a/docs/Web-Backend/SpringBoot/SpringBoot-Common.md b/docs/Web-Backend/SpringBoot/SpringBoot-Common.md new file mode 100644 index 0000000..3f24fda --- /dev/null +++ b/docs/Web-Backend/SpringBoot/SpringBoot-Common.md @@ -0,0 +1,871 @@ + + +# 一、框架说明 + 本框架基于Spring Boot3框架二次开发,增加了依赖包管理和启动项配置等功能。 + +# 二、项目结构 + +| 项目模块 | 模块含义 | 主要功能 | +| :-----------: | :-------------------: | :---------------------------------: | +| starters | SpringBoot 启动项 | 主要包括web、jdbc和log启动项配置 | +| autoconfigure | staters具体启动配置类 | 主要包括web和jdbc具体的启动项配置类 | +| dependencies | 依赖项 | 主要包括本框架中的依赖包管理 | +| framework | 通用配置 | 主要包括web和data的一些通用工具方法 | + +# 三、项目说明 +## 3.1 common模块 + 该模块主要声明项目结构,包括autoconfigure、dependencies、framework、starter-parent和starters等模块。 +```xml + + + dependencies + starters + framework + autoconfigure + starter-parent + +``` + +## 3.2 dependencies模块 + 该模块为其他模块的父模块,声明了一些常用依赖包及版本,通过``管理,只声明依赖的版本,并不会实际引入依赖。 + 后续有新增依赖时,需要先在dependencies模块声明版本,然后在相应的starter模块中增加依赖。 +```xml + + 17 + + ${java.version} + ${java.version} + + 3.5.0 + 2.7.0 + + 1.8.8 + 2.7.0 + + 3.0.4 + 3.5.12 + 1.43.0 + 5.8.26 + + 2.19.0 + 4.4 + 3.14.0 + 3.9.0 + 31.1-jre + 1.5.5.Final + 1.0.6 + + 1.18.30 + 0.2.0 + + 3.11.0 + 0.41.0 + 0.10.2 + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + + + com.sndyuk + logback-more-appenders + ${logback-more-appenders.version} + + + + org.komamitsu + fluency-fluentd + ${fluency-fluentd.version} + + + + + cn.dev33 + sa-token-bom + ${sa.token.version} + pom + import + + + + + cn.hutool + hutool-bom + ${hutool.version} + pom + import + + + + + org.mybatis + mybatis-spring + ${mybatis-spring.version} + + + + + com.baomidou + mybatis-plus-bom + ${mybatis.plus.version} + pom + import + + + + + commons-io + commons-io + ${commons.io.version} + + + + org.apache.commons + commons-collections4 + ${commons.collections.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + commons-net + commons-net + ${commons.net.version} + + + + com.google.guava + guava-bom + ${guava.version} + pom + import + + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + + com.github.yitter + yitter-idgenerator + ${yitter.idgenerator.version} + + + + + com.cxx + starter-logging + 2.0.0 + + + com.cxx + starter-web + 2.0.0 + + + com.cxx + starter-jdbc + 2.0.0 + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + io.fabric8 + docker-maven-plugin + ${docker-maven-plugin.version} + + + org.graalvm.buildtools + native-maven-plugin + ${native-maven-plugin.version} + + + + +``` + +::: tip +dependencyManagement部分只声明依赖的版本,并不会实际引入依赖。 +需要在具体的模块中显式声明依赖,才能让模块使用这些库 +::: + +## 3.3 framework模块 + 该模块主要是一些通用的配置。 + +### 3.3.1 data + 主要提供了2个数据库实体类的基类。 +::: code-group +```java [AbstractIdEntity] +@Getter +@Setter +public abstract class AbstractIdEntity { + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; +} +``` + +```java [AbstractEntity] +@Getter +@Setter +public abstract class AbstractEntity extends AbstractIdEntity { + @TableField(value = "create_time", fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(value = "create_by", fill = FieldFill.INSERT) + private String createBy; + + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE) + private String updateBy; +} +``` +::: + +### 3.3.2 web + 主要提供了自定义异常处理类。 +::: code-group +```java [ErrorResponse] +/** + * 错误响应 + */ +public final class ErrorResponse { + /** + * 自定义code + */ + private final String code; + + /** + * 自定义消息 + */ + private final String message; + + public ErrorResponse(String message) { + this.code = "Unspecified"; + this.message = message; + } + + public ErrorResponse(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} +``` + +```java [CustomException] +/** + * 自定义异常类 + */ +public class CustomException extends AbstractException { + + public CustomException(String errorMessage) { + super(errorMessage); + } + + public CustomException(String code, String errorMessage) { + super(code, errorMessage); + } + + public CustomException(String errorMessage, Exception innerException) { + super(errorMessage, innerException); + } + + public CustomException(String code, String errorMessage, Exception innerException) { + super(code, errorMessage, innerException); + } + +} +``` + +```java [AbstractException] +/** + * 抽象异常类 + */ +public abstract class AbstractException extends RuntimeException { + protected String code; + + public AbstractException(String errorMessage) { + super(errorMessage); + } + + public AbstractException(String code, String errorMessage) { + super(errorMessage); + this.code = code; + } + + public AbstractException(String errorMessage, Exception innerException) { + super(errorMessage, innerException); + } + + public AbstractException(String code, String errorMessage, Exception innerException) { + super(errorMessage, innerException); + this.code = code; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} +``` +::: + +### 3.4 starters模块 + 该模块为整个项目的核心模块,在实际项目中,通过引入相应的starter模块,并配合autoconfigure模块中的功能,即可实现快速自动装配。 + 该模块的pom.xml声明的依赖会注入到实际的项目中。 + 在springboot3中,可以在**main->resources->META-INF->spring->org.springframework.boot.autoconfigure.AutoConfiguration.imports**中声明需要自动执行的类。 + +#### 3.4.1 stater-jdbc + 该模块主要提供数据库相关的配置功能。 + 目前实现的功能有: +1. 配置MybatisPlus拦截器,添加乐观锁和分页插件。 +2. 自定义MybatisPlus ID生成器(雪花ID)。 +3. 配置MybatisPlus自动填充字段(create_time、create_by、update_time和update_by)。 + +::: code-group +```java [JdbcAutoConfiguration] +/** + * jdbc自动配置 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(SqlSessionFactory.class) +@Import({YitterGenerator.class, MybatisMetaObjectHandler.class}) +public class JdbcAutoConfiguration { + /** + * MybatisPlus拦截器 + * @return 拦截器 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 乐观锁插件 + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + // 分页插件 + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } +} +``` + +```java [YitterGenerator] +/** + * 雪花id生成器 + */ +public class YitterGenerator implements IdentifierGenerator { + + @Override + public Number nextId(Object entity) { + return YitIdHelper.nextId(); + } + +} +``` + +```java [MybatisMetaObjectHandler] +/** + * Mybatis Plus自动填充 + */ +public class MybatisMetaObjectHandler implements MetaObjectHandler { + @Override + public void insertFill(MetaObject metaObject) { + this.strictInsertFill(metaObject, Constants.CREATE_TIME_FLAG, LocalDateTime.class, LocalDateTime.now()); + this.strictInsertFill(metaObject, Constants.UPDATE_TIME_FLAG, LocalDateTime.class, LocalDateTime.now()); + + if (StpUtil.isLogin()) { + this.strictInsertFill(metaObject, Constants.CREATE_BY_FLAG, String.class, StpUtil.getLoginIdAsString()); + this.strictInsertFill(metaObject, Constants.UPDATE_BY_FLAG, String.class, StpUtil.getLoginIdAsString()); + } + } + + @Override + public void updateFill(MetaObject metaObject) { + this.strictUpdateFill(metaObject, Constants.UPDATE_TIME_FLAG, LocalDateTime.class, LocalDateTime.now()); + + if (StpUtil.isLogin()) { + this.strictUpdateFill(metaObject, Constants.UPDATE_BY_FLAG, String.class, StpUtil.getLoginIdAsString()); + } + } +} +``` +::: + + `@ConditionalOnClass(SqlSessionFactory.class)`表示只有存在SqlSessionFactory类时,这个配置类才会生效。即如果没有 MyBatis 相关依赖,这个配置类会被 Spring 完全忽略。 + `@Import`用于导入其他配置类或组件到当前配置类中。 + +#### 3.4.2 starter-web + 该模块主要提供web相关的配置功能。 + 目前实现的功能有: +1. 注册审计拦截器 +2. 注册Sa-Token拦截器 +3. 注册安全拦截器 +4. 配置CORS跨越 +5. 全局异常处理器 + +::: code-group +```java [ServerAutoConfiguration] +@Configuration() +@Import({DefaultExceptionAdvice.class, AuditBodyAdvice.class, CustomProperties.class}) +public class ServerAutoConfiguration implements WebMvcConfigurer { + @Resource + private CustomProperties customProperties; + + /** + * 注册拦截器 需要实现 WebMvcConfigurer 接口 + * @param registry 注册器 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 注册安全拦截器 + // registry.addInterceptor(new SecurityInterceptor()); + // 注册审计拦截器 + registry.addInterceptor(new AuditInterceptor(customProperties.getBasePackage())); + // 注册Sa-Token拦截器 登录校验 + registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())) + .excludePathPatterns("/error", "/swagger-ui/**", "/swagger-resources/**", "/v3/api-docs/**") + .excludePathPatterns("/files/**") + .excludePathPatterns("/doc.html", "/webjars/**"); + } + + /** + * 配置CORS跨越 需要实现 WebMvcConfigurer 接口 + * @return 过滤器 + */ + @Bean + @Order(-128) + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + // 允许所有来源 + config.addAllowedOrigin("*"); + // 允许所有请求头 + config.addAllowedHeader("*"); + // 允许所有请求方法 + config.addAllowedMethod("*"); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} +``` + +```java [AuditInterceptor] +public class AuditInterceptor implements HandlerInterceptor { + private static final Logger logger = LoggerFactory.getLogger(AuditInterceptor.class); + + private final String basePackage; + + public AuditInterceptor(String basePackage) { + this.basePackage = basePackage; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + // 如果是Http请求 + if (handler instanceof HandlerMethod hd) { + request.setAttribute(Constants.AUDIT_TIME_FLAG, System.currentTimeMillis()); + // 判断该请求是否需要审计 + if (WebUtils.checkIsAuditPackages(hd.getBeanType().getPackage(), basePackage)) { + handleAuditPackageRequest(request); + } + } + + // true表示继续处理请求 + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + // 这里我们不需要处理时间计算,所有的计算在 afterCompletion 中完成 + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + // 获取开始时间 + Long startTime = (Long) request.getAttribute(Constants.AUDIT_TIME_FLAG); + if (startTime != null) { + long duration = System.currentTimeMillis() - startTime; + logger.info(" Request URL {} | Time Taken {} ms", request.getRequestURI(), duration); + } + } + + /** + * 处理审计包请求 + * @param request 请求 + */ + private void handleAuditPackageRequest(HttpServletRequest request) { + if (StringUtils.isEmpty(request.getQueryString())) { + logger.info(" {} {}", request.getMethod(), request.getRequestURI()); + } else { + logger.info(" {} {}?{}", request.getMethod(), request.getRequestURI(), WebUtils.format2UTF8(request.getQueryString())); + } + } +} +``` + +```java [DefaultExceptionAdvice] +/** + * 全局异常处理器 + */ +@RestControllerAdvice +public class DefaultExceptionAdvice { + + private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionAdvice.class); + + /** + * 处理SaToken权限错误 + * @param e 权限异常 + * @return 错误响应 + */ + @ExceptionHandler(SaTokenException.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public ErrorResponse handleSaTokenException(SaTokenException e) { + ErrorResponse response = new ErrorResponse(String.valueOf(e.getCode()), e.getMessage()); + logger.error("<{}> {}", response.getCode(), e.toString()); + return response; + } + + /** + * 处理参数校验错误 + * @param e 校验异常 + * @return 错误响应 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ErrorResponse handleValidationException(MethodArgumentNotValidException e) { + ErrorResponse response = new ErrorResponse(WebUtils.formatValidationException(e.getBindingResult().getFieldErrors())); + logger.error("<{}> {}", response.getCode(), response.getMessage()); + return response; + } + + /** + * 处理自定义异常错误 + * @param e 自定义异常 + * @return 错误响应 + */ + @ExceptionHandler(AbstractException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleAbstractException(AbstractException e) { + ErrorResponse response; + if (e.getCode() == null || e.getCode().isEmpty()) { + response = new ErrorResponse(e.getMessage()); + } else { + response = new ErrorResponse(e.getCode(), e.getMessage()); + } + logger.error(String.format("<%s> ", response.getCode()), e); + return response; + } + + /** + * 处理默认异常错误 + * @param e 默认异常 + * @return 错误响应 + */ + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleDefaultException(Exception e) { + ErrorResponse response = new ErrorResponse(e.getMessage()); + logger.error(String.format("<%s> ", response.getCode()), e); + return response; + } +} +``` + +```java [AuditBodyAdvice] +@ControllerAdvice +@Import({CustomProperties.class}) +public class AuditBodyAdvice implements RequestBodyAdvice, ResponseBodyAdvice { + @Resource + private CustomProperties customProperties; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + + private static final Logger logger = LoggerFactory.getLogger(AuditBodyAdvice.class); + + @Override + public boolean supports(MethodParameter methodParameter, Type targetType, Class extends HttpMessageConverter>> converterType) { + // 判断是否为需要审计的包 + String auditPackages = customProperties.getBasePackage(); + return WebUtils.checkIsAuditPackages(methodParameter.getDeclaringClass().getPackage(), auditPackages) + && AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType); + } + + @Override + public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class extends HttpMessageConverter>> converterType) throws IOException { + return inputMessage; + } + + @Override + public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class extends HttpMessageConverter>> converterType) { + // 入参结束 + try { + String jsonBody = objectMapper.writeValueAsString(body); + logger.info(" {}", jsonBody); + } catch (JsonProcessingException e) { + logger.info(" {}", body); + } + return body; + } + + @Override + public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class extends HttpMessageConverter>> converterType) { + return body; + } + + @Override + public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType) { + // 判断是否为需要审计的包 + String auditPackages = customProperties.getBasePackage(); + return WebUtils.checkIsAuditPackages(returnType.getDeclaringClass().getPackage(), auditPackages) + && AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType); + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { + // 出参结束 + if (body != null && !(body instanceof ErrorResponse)) { + try { + String jsonBody = objectMapper.writeValueAsString(body); + logger.info(" {}", jsonBody); + } catch (JsonProcessingException e) { + logger.info(" {}", body); + } + } + return body; + } + +} +``` + +```java [CustomProperties] +/** + * 用于将配置文件(如 application.properties 或 application.yml)中的属性值绑定到 Java 对象 + */ +@ConfigurationProperties(prefix = "web-starter") +public class CustomProperties { + // yml中web-starter下的base-package字段值 + private String basePackage = ""; + + public String getBasePackage() { + return basePackage; + } + + public void setBasePackage(String basePackage) { + this.basePackage = basePackage; + } +} +``` +::: + + `@ConfigurationProperties`用于将配置文件(如 application.properties 或 application.yml)中的属性值绑定到 Java 对象 + +::: tip +SpringBoot MVC总体执行顺序: +1. 进入Tomcat容器 +2. 进入Filter过滤器 +3. 进入Servlet容器 +4. 进入Interceptor拦截器 +5. 进入Controller控制器 +6. 进入AOP +::: + +::: tip +请求和响应体拦截器 +RequestBodyAdvice, ResponseBodyAdvice主要发生在Controller执行前后: +1. preHandle():请求处理前 +2. beforeBodyRead:请求体反序列化前 +3. @RequestBody:Controller方法参数绑定 +4. Controller:请求处理 +5. beforeBodyWrite():响应体序列化之前 +6. postHandle():请求处理后 +7. 视图渲染 +8. afterCompletion():请求结束 +::: + +::: tip +审计拦截器 可以实现请求日志打印等功能 +HandlerInterceptor 拦截器执行顺序: +1. preHandle():请求处理前 按注册顺序依次执行。 +2. Controller:请求处理 请求到达Controller并被处理。 +3. postHandle():请求处理后,视图渲染前 按注册顺序逆序执行。 +4. afterCompletion():视图渲染后 按注册顺序逆序执行。 +::: + +# 四、项目发布 + +## 4.1 发布到Git中 +1. 在Gitee/Github中创建工程,需要在工程中创建一个文件夹,例如repo,后续发布的文件要放在该文件夹下。 +2. 在maven的setting.xml中添加server和repository信息 +```xml + + + gitee + Cxx0822 + token + + + + + + gitee + + + central + https://maven.aliyun.com/repository/central + + true + + + true + + + + + gitee + https://gitee.com/Cxx0822/springboot2-common/raw/master/repo + + + + + + central + https://maven.aliyun.com/repository/central + + true + + + true + + + + + + + + gitee + +``` + + 注:这里要去掉mirror的阿里云镜像。 + +3. 在工程的根目录的pom.xml中添加发布配置: +```xml + + + gitee + springboot2-common + file:D:/temp/maven + + +``` + + 注:gitee不支持通过deploy发布jar包,可以先发布到本地,再将文件复制到项目文件夹下,通过git push推送。 + +4. 将本地产生的发布文件上传至git仓库中。 +5. 其他项目引用: +```xml + + com.cxx + starter-web + 1.0.0 + + + com.cxx + starter-jdbc + 1.0.0 + + + com.cxx + starter-logging + 1.0.0 + +``` + +# 五、项目使用 +1. 将父工程改为starter-parent模块 +```xml + + com.cxx + starter-parent + 2.0.0 + + +``` +2. 根据需要引入starter-web、starter-jdbc和starter-logging模块 +```xml + + + com.cxx + starter-web + 2.0.0 + + + com.cxx + starter-jdbc + 2.0.0 + + + com.cxx + starter-logging + 2.0.0 + + +``` +3. 打包模块时,需要添加spring-boot-maven-plugin +```xml + + org.springframework.boot + spring-boot-maven-plugin + +``` \ No newline at end of file diff --git a/docs/Web-Backend/SpringBoot/SpringBoot-Start-Process.md b/docs/Web-Backend/SpringBoot/SpringBoot-Start-Process.md index 638b3fa..dd84d5c 100644 --- a/docs/Web-Backend/SpringBoot/SpringBoot-Start-Process.md +++ b/docs/Web-Backend/SpringBoot/SpringBoot-Start-Process.md @@ -1,3 +1,5 @@ + + # 一、启动流程  diff --git a/docs/Web-Backend/SpringBoot/SpringBoot3-GraalVM.md b/docs/Web-Backend/SpringBoot/SpringBoot3-GraalVM.md index ad3d567..f7d3a0d 100644 --- a/docs/Web-Backend/SpringBoot/SpringBoot3-GraalVM.md +++ b/docs/Web-Backend/SpringBoot/SpringBoot3-GraalVM.md @@ -1,3 +1,5 @@ + + # 一、原生镜像 传统 Java 应用基于 JVM 运行,需要加载完整的类库和 JVM 运行时环境,导致启动时间长、内存占用高。而**原生镜像技术通过提前编译(AOT)将 Java 应用直接编译为本地机器码,无需 JVM 即可运行**,具有以下核心优势: diff --git a/docs/Web-Backend/SpringBoot/SpringBootBean.md b/docs/Web-Backend/SpringBoot/SpringBootBean.md index 0723b74..b9c3e10 100644 --- a/docs/Web-Backend/SpringBoot/SpringBootBean.md +++ b/docs/Web-Backend/SpringBoot/SpringBootBean.md @@ -1,3 +1,5 @@ + + # 一、Bean概念 ## 1.1 定义 Spring bean是Spring框架在运行时管理的对象,Bean是一个由Spring IoC容器实例化、组装和管理的对象。 diff --git a/docs/Web-Backend/SpringBoot/SpringBootStarter.md b/docs/Web-Backend/SpringBoot/SpringBootStarter.md index 7b900bb..5a7195a 100644 --- a/docs/Web-Backend/SpringBoot/SpringBootStarter.md +++ b/docs/Web-Backend/SpringBoot/SpringBootStarter.md @@ -1,3 +1,5 @@ + + # 一、简介 Spring Boot Starter是一组预定义的依赖项集合,旨在简化Maven或Gradle等构建工具中的依赖管理。每个Starter都包含了实现特定功能所需的库和组件,以及相应的配置文件。开发者只需在项目中引入相应的Starter依赖,即可快速搭建起具备该功能的项目骨架。 Starter=依赖+自动配置+配置文件 diff --git a/docs/Web-Backend/SpringBoot/SpringIOC.md b/docs/Web-Backend/SpringBoot/SpringIOC.md index f4680e8..fc694ca 100644 --- a/docs/Web-Backend/SpringBoot/SpringIOC.md +++ b/docs/Web-Backend/SpringBoot/SpringIOC.md @@ -1,3 +1,5 @@ + + # 一、IOC Spring框架的核心是IOC(控制反转)容器,它负责管理应用程序中的对象(称为Bean)的创建、配置和组装。 diff --git a/docs/Web-Backend/SpringBoot/SpringMVC.md b/docs/Web-Backend/SpringBoot/SpringMVC.md index 2abcf5b..c5ca129 100644 --- a/docs/Web-Backend/SpringBoot/SpringMVC.md +++ b/docs/Web-Backend/SpringBoot/SpringMVC.md @@ -1,3 +1,5 @@ + + # 一、基础概念 Spring MVC(Spring Model-View-Controller)是 Spring Framework 中的一部分,它是一个基于 **请求驱动** 的 Web 框架,主要用于构建 Web 应用程序,并且遵循 MVC(模型-视图-控制器) 设计模式。它将 Web 应用的业务逻辑、用户界面和请求处理分离,使得代码更加模块化和可维护。 diff --git a/docs/Web-Backend/index.md b/docs/Web-Backend/index.md index 8d940e1..782b3a0 100644 --- a/docs/Web-Backend/index.md +++ b/docs/Web-Backend/index.md @@ -18,6 +18,7 @@ SpringBoot 框架学习系列: - [SpringBoot Bean简介](/Web-Backend/SpringBoot/SpringBootBean) - [SpringBoot3原生镜像](/Web-Backend/SpringBoot/SpringBoot3-GraalVM) - [SpingBoot技巧](/Web-Backend/SpringBoot/SpingBoot-Skills) +- [SpringBoot Common](/Web-Backend/SpringBoot/SpringBoot-Common) ### 🐍 FastAPI Python FastAPI 框架学习: diff --git a/docs/Web-Front/Others/Browser-Process.md b/docs/Web-Front/Others/Browser-Process.md index 9e58fbe..a648555 100644 --- a/docs/Web-Front/Others/Browser-Process.md +++ b/docs/Web-Front/Others/Browser-Process.md @@ -1,3 +1,5 @@ + + # 一、网络请求阶段 1. DNS 解析 diff --git a/docs/Web-Front/Others/JavaScript-Guide.md b/docs/Web-Front/Others/JavaScript-Guide.md index 5b7d2f5..bb853d1 100644 --- a/docs/Web-Front/Others/JavaScript-Guide.md +++ b/docs/Web-Front/Others/JavaScript-Guide.md @@ -1,3 +1,5 @@ + + # JavaScript简介 ## JavaScript 实现 完整的`JavaScript`实现包含以下几个部分: diff --git a/docs/Web-Front/Others/Package.md b/docs/Web-Front/Others/Package.md index 033179c..1e88580 100644 --- a/docs/Web-Front/Others/Package.md +++ b/docs/Web-Front/Others/Package.md @@ -1,3 +1,5 @@ + + # npm 2010年随Node.js发布的npm,首次为JavaScript引入了标准的包管理系统。其核心创新是**package.json文件**,通过语义化版本规范定义了依赖声明标准。这一设计解决了手动管理依赖时的版本混乱问题,为模块化开发提供了基础设施。 diff --git a/docs/Web-Front/Others/Vite-Principle.md b/docs/Web-Front/Others/Vite-Principle.md index 8166d95..4258280 100644 --- a/docs/Web-Front/Others/Vite-Principle.md +++ b/docs/Web-Front/Others/Vite-Principle.md @@ -1,3 +1,5 @@ + + # 一、Vite简介 Vite是新一代的前端构建工具,在尤雨溪开发Vue3.0的时候诞生。类似于Webpack+ Webpack-dev-server。 其主要利用浏览器**ESModule特性**导入组织代码,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。生产中利用Rollup作为打包工具,号称下一代的前端构建工具。 diff --git a/docs/Web-Front/Vue/Vue3-Architecture.md b/docs/Web-Front/Vue/Vue3-Architecture.md index 8b15b9b..886a789 100644 --- a/docs/Web-Front/Vue/Vue3-Architecture.md +++ b/docs/Web-Front/Vue/Vue3-Architecture.md @@ -1,3 +1,5 @@ + + # 一、Vue3 整体架构图 ```mermaid graph TD diff --git a/docs/Web-Front/Vue/Vue3-Common.md b/docs/Web-Front/Vue/Vue3-Common.md index 8072083..b736fe5 100644 --- a/docs/Web-Front/Vue/Vue3-Common.md +++ b/docs/Web-Front/Vue/Vue3-Common.md @@ -1,3 +1,5 @@ + + # 一、依赖管理 本项目会传递安装的依赖有:
+ + 字数: {{ wordCount }} 字 + + 时长: {{ readTime }} 分钟 +