[{"content":"Vue 选择用 JavaScript 开发，而不是 Java、Python 等其他语言，核心原因与​​前端开发的本质需求​​、​​JavaScript 的特性​​以及​​前端生态的演进​​密切相关。\n​​一、前端开发的“宿命”：必须用 JavaScript​​浏览器的核心语言是 JavaScript（简称 JS）。无论你用什么语言写前端代码（比如 Java、Python），最终都需要​​编译/转译成 JS​​，才能在浏览器中运行。这是由浏览器的底层设计决定的——浏览器只认识 JS（和 Wasm 等少数补充技术）。\nVue 作为一个​​前端框架​​，本质是为浏览器服务的工具库。它的代码需要直接操作 DOM、处理浏览器事件（如点击、滚动）、调用浏览器 API（如 fetch、localStorage），这些都​​只能在 JS 环境中完成​​。\n例子：如果你用 Java 写前端框架，Java 代码必须先编译成 JS（通过工具如 GWT），但这样会失去 JS 的动态性和灵活性，而且失去了直接操作浏览器 API 的能力。因此，前端框架用 JS 是“原生适配”，其他语言需要额外转译，增加了复杂度。\n​​二、JavaScript 的特性：完美契合前端需求​​Vue 的核心设计目标是​​轻量、灵活、响应式​​，而 JavaScript 的特性恰好能满足这些需求：\n1. ​​动态类型语言：灵活适配复杂场景​​JS 是动态类型语言（变量类型可变），适合前端开发中​​频繁变化的场景​​（比如用户输入、动态数据渲染）。Vue 的响应式系统（reactive/ref）需要动态追踪数据变化，JS 的动态性让这种追踪更简单（比如通过 Proxy 或 Object.defineProperty 监听对象属性变化）。\n对比：Java 是静态类型语言，变量类型固定。如果用 Java 实现类似的响应式系统，需要额外的类型检查和反射机制，代码会更复杂，性能也可能受影响。\n2. ​​函数式编程支持：简化状态管理​​JS 支持函数式编程（如高阶函数、闭包、箭头函数），这为 Vue 的​​组合式 API​​（setup 函数、computed、watch）提供了天然支持。函数式编程能让状态管理更简洁（比如用 computed 缓存计算结果），避免复杂的状态同步问题。\n例子：Vue 中用 computed 定义派生状态时，本质是利用 JS 的闭包和函数式特性，自动追踪依赖并缓存结果。\n3. ​​原型链与对象系统：实现响应式的“基础设施”​​Vue 的响应式原理（如 reactive）依赖 JS 的对象系统（原型链、属性描述符）。通过修改对象的 getter/setter，可以高效地追踪数据变化并触发视图更新。这种实现方式在 JS 中非常自然，而在静态语言（如 Java）中需要通过反射或字节码增强，实现成本更高。\n例子：Vue 3 用 Proxy 替代 Object.defineProperty 实现响应式，正是利用了 JS 对 Proxy 的原生支持，避免了旧版浏览器兼容性问题。\n4. ​​生态成熟：前端工具链的“通用语言”​​JS 拥有全球最大的开发者社区和最成熟的前端工具链（如 npm/yarn/pnpm 包管理、Webpack/Vite 打包工具、TypeScript 类型支持）。Vue 选择 JS，可以直接复用这些工具，降低开发门槛，让更多开发者快速上手。\n对比：如果用 Java 写前端框架，需要额外解决包管理、构建工具、类型系统等问题，生态成本极高。\n​​三、历史与社区因素：JavaScript 是前端的事实标准​​Vue 诞生于 2014 年，当时的前端生态已经以 JS 为主导：\n​​ECMAScript 标准​​不断完善（ES6 引入 class、Promise、let/const 等特性），JS 的能力大幅提升；\n​​Node.js​​ 兴起（2009 年发布），让 JS 能在后端运行，进一步巩固了其“全栈语言”的地位；\n​​前端框架​​（如 Angular 1.x、Backbone.js）已普遍用 JS 开发，社区习惯了 JS 的语法和思维。\nVue 作为后来者，选择 JS 是顺应趋势，避免重复造轮子（比如重新构建一套类型系统、工具链）。\n​​四、其他语言的局限性​​虽然理论上可以用其他语言写前端框架（比如用 TypeScript 增强 JS，或用 Dart 编译成 JS），但其他语言在前端场景中存在明显短板：\n1. ​​Java​​编译型语言：需要编译步骤，开发效率低（前端需要“热更新”“实时调试”）；\n静态类型：虽然严谨，但前端开发中动态数据（如用户输入）的处理更灵活；\n生态脱节：Java 主要用于后端（如 Spring），前端工具链（如打包、调试）不支持 Java。\n2. ​​Python​​解释型语言：虽然开发灵活，但前端需要与浏览器 API 深度交互（如操作 DOM），Python 没有原生的 DOM 操作库；\n性能问题：Python 执行效率低于 JS（浏览器用 JS 引擎优化，如 V8），复杂前端逻辑可能卡顿；\n生态不匹配：前端工具链（如 npm）不支持 Python 包，依赖管理困难。\n3. ​​TypeScript​​其实 Vue 3 已经用 TypeScript 重写了（TS 是 JS 的超集），但 TS 最终还是要编译成 JS 才能在浏览器运行。TS 的作用是​​增强类型检查​​，而不是替代 JS——Vue 选择 JS 作为基础，TS 作为辅助，是更务实的选择。\n​​总结：Vue 用 JS 的核心原因​​​​浏览器限制​​：前端代码必须在浏览器中运行，而浏览器只支持 JS（及 Wasm 等补充技术）；\n​​语言特性匹配​​：JS 的动态性、函数式编程支持、对象系统，完美契合 Vue 响应式、灵活的设计目标；\n​​生态成熟​​：JS 拥有全球最大的前端工具链和社区，Vue 复用这些资源能快速发展和普及；\n​​历史趋势​​：JS 是前端的事实标准，Vue 顺应趋势，避免重复造轮子。\n简单说：​​Vue 用 JS，是因为 JS 是前端开发的“通用语言”，没有其他语言能比 JS 更适合浏览器环境，也没必要为了“尝鲜”牺牲开发效率和生态支持​​。\n","date":"2025-06-29T17:28:02.733092662Z","image":"https://qiuyingtyan.top/upload/%E5%88%B0%E6%A0%B9%E7%9B%AE%E5%BD%95%E4%B8%8A%E4%BC%A0%E6%BA%90%E6%96%87%E4%BB%B6.webp","permalink":"https://qiuyingtyan.top/p/vuewei-shi-me-shi-jsxie-de-er-bu-shi-qi-ta-yu-yan-xie-de/","title":"Vue为什么是js写的而不是其它语言写的？"},{"content":"前后端分离是一种​​开发模式升级​​，核心是把“前端页面渲染”和“后端数据处理”拆成两个独立的部分，各司其职。它不是“必须”，但在现代Web开发中已成为主流，主要解决了传统开发模式的痛点。\n​​一、传统开发模式的痛点（前后端耦合）​​在前后端分离之前，主流的开发模式是“前后端一体”（比如JSP、PHP、早期的ASP.NET）。前端（HTML/CSS/JS）和后端（Java/Python等）代码混在一个项目里，甚至写在同一个文件里（比如JSP里既有HTML又有Java代码）。这种模式有3个大问题：\n1. ​​开发效率低，协作困难​​前端和后端工程师要“抢代码”：\n前端改个页面样式（比如调整按钮颜色），需要后端重新编译、部署整个项目；\n后端改个接口参数（比如把userId改成uid），前端所有调用这个接口的地方都要改；\n页面逻辑（比如表单验证）可能一半在前端JS写，一半在后端Java写，重复劳动多。\n例子：你做一个登录页面，前端用JS校验手机号格式，后端也要用Java再校验一遍——因为担心前端绕过校验直接发非法数据。两边代码重复，改一处漏一处。\n2. ​​维护成本高，代码臃肿​​页面逻辑和业务逻辑混在一起，代码像“浆糊”：\n一个JSP文件可能包含HTML标签、CSS样式、JavaScript脚本，还穿插着后端Java代码（比如\u0026lt;% String name = request.getParameter(\"name\"); %\u0026gt;）；\n后端为了生成动态HTML，需要拼接大量字符串（比如out.print(\"\u0026lt;div\u0026gt;\" + data + \"\u0026lt;/div\u0026gt;\")），代码可读性差，改起来容易出错。\n例子：修一个按钮的样式，可能要翻遍整个JSP文件找对应的HTML标签和CSS类，还要注意后端有没有硬编码的样式逻辑。\n3. ​​扩展性差，无法应对复杂需求​​传统模式适合“简单页面”，但遇到复杂交互（比如单页应用SPA、实时数据监控）就力不从心：\n前端无法独立优化：页面渲染依赖后端返回的完整HTML，每次数据变化都要刷新整个页面；\n后端压力大：除了处理业务逻辑，还要负责页面渲染、模板引擎（如Thymeleaf），浪费计算资源；\n移动端适配难：PC端页面直接套用到手机上，布局混乱，需要为不同设备写多套代码。\n例子：你想做一个实时监控大屏（像你项目中的运维监控），需要每秒更新10次数据。传统模式下，每次更新都要后端重新生成整个HTML页面，前端被动刷新，延迟高、卡顿明显。\n​​二、前后端分离解决了什么问题？​​前后端分离后，前端和后端变成“两个独立的服务”：\n​​前端​​：专注页面渲染、用户交互（用Vue/React/Angular等框架），通过调用后端提供的API（如/api/server/cpu）获取数据；\n​​后端​​：专注业务逻辑、数据处理（用Spring Boot/Django等框架），只负责提供API，不关心页面怎么渲染。\n这种模式带来了4大核心优势：\n1. ​​开发效率高，协作更简单​​前端和后端可以​​并行开发​​：\n前端工程师拿到后端提供的API文档（如Swagger），直接写页面调用接口，不需要等后端代码完成；\n后端工程师只需要保证API的正确性（返回正确的数据格式），不需要关心前端怎么渲染页面；\n代码修改互不影响：前端改样式只需调自己的CSS，后端改接口参数只需更新文档，前端同步调整即可。\n例子：你做运维监控的图表页面，前端用ECharts调用后端的/api/metrics/cpu接口拿数据，后端只需要保证接口返回{time: \"10:00\", value: 80}这样的JSON，前端自己处理渲染，改颜色、调图表类型都不用后端参与。\n2. ​​代码更清晰，维护成本低​​前后端代码完全分离：\n前端代码（HTML/CSS/JS）只存在于前端项目（如Vue工程），后端代码（Java/Python）只存在于后端项目（如Spring Boot工程）；\n页面逻辑（如按钮点击事件）和业务逻辑（如查询数据库）分开，没有重复代码；\n前端可以专注优化用户体验（比如页面加载速度、动画效果），后端专注优化数据处理（比如数据库查询、接口性能）。\n例子：修前端按钮的样式，只需要在前端的CSS文件里改，后端完全不用管；后端改了接口返回的数据格式（比如把cpu字段改成cpuUsage），只需要更新API文档，前端同步调整解析逻辑即可。\n3. ​​扩展性强，能应对复杂需求​​前后端分离天然支持现代Web应用的复杂需求：\n​​单页应用（SPA）​​：前端通过AJAX/Vue Router实现页面无刷新切换（比如你项目的监控看板，切换不同服务器时不用刷新整个页面）；\n​​多端适配​​：前端可以用同一套代码适配PC、手机、平板（响应式设计），后端只需要提供通用API；\n​​实时交互​​：通过WebSocket（你项目中的SSH终端）实现双向通信，后端主动推送数据给前端（比如服务器CPU突然飙升时，后端主动发消息给前端更新图表）；\n​​前后端独立部署​​：前端可以部署在CDN（加速访问），后端部署在云服务器（处理业务），互不影响，故障隔离。\n例子：你想给运维监控系统加一个“移动端适配”功能，前端只需要用媒体查询调整布局，后端完全不用改；如果后端需要新增一个监控指标（如内存使用率），只需要添加一个新API（/api/metrics/memory），前端调用即可，无需重构页面。\n4. ​​团队协作更高效​​前后端可以分成两个独立的团队：\n前端团队专注于用户体验、交互设计（比如用Vue3+Element Plus做页面）；\n后端团队专注于业务逻辑、数据存储（比如用Spring Boot+MyBatis-Plus写接口）；\n接口文档（如Swagger）作为“合同”，明确双方的输入输出，减少沟通成本。\n例子：你项目的“子账户权限管理”功能，前端团队可以做权限控制页面（比如选择用户角色、分配服务器），后端团队做权限校验逻辑（比如用户是否有权限操作某台服务器），双方通过API文档对齐需求。\n​​三、前后端分离的“代价”与注意事项​​当然，前后端分离也不是完美的，需要处理一些新问题：\n1. ​​跨域问题​​前端和后端可能部署在不同的域名/端口（比如前端在http://localhost:8080，后端在http://localhost:8081），浏览器会阻止跨域请求。需要后端配置CORS（跨域资源共享）或用Nginx反向代理解决（你项目中可能用了Spring Security的CORS配置）。\n2. ​​接口文档维护​​前后端协作依赖清晰的API文档（如参数、返回值、错误码）。需要用Swagger/OpenAPI自动生成文档，避免“接口写好了但前端不会用”的问题。\n3. ​​前端复杂度提升​​前端需要处理更多逻辑（比如状态管理、路由跳转、错误处理），可能需要引入框架（Vue/React）和工具（Vuex/Pinia、Vue Router），学习成本增加。\n4. ​​首屏加载时间​​前端需要加载更多的JS/CSS资源，可能导致首屏加载变慢。可以通过代码分割（懒加载）、CDN加速、静态资源压缩（如图片转WebP）优化。\n​​总结：为什么要前后端分离？​​前后端分离的本质是​​“专业的人做专业的事”​​：\n前端专注于“用户看到的部分”（页面美观、交互流畅）；\n后端专注于“数据处理的逻辑”（业务正确、性能高效）；\n两者通过API解耦，降低协作成本，提升开发效率，更好地应对复杂Web应用的需求。\n在运维监控系统中，前后端分离尤其重要：前端需要灵活渲染各种图表（如CPU/内存使用率趋势图），后端需要高效处理大量监控数据（如InfluxDB存储、实时推送），两者独立开发能最大化发挥各自的优势。\n","date":"2025-06-29T17:26:14.667398008Z","image":"https://qiuyingtyan.top/upload/117175864_p0_master1200-peul.webp","permalink":"https://qiuyingtyan.top/p/wei-shi-me-yao-qian-hou-duan-fen-chi/","title":"为什么要前后端分离"},{"content":"1. 输入网址 → 浏览器解析与缓存检查​​你在地址栏输入 www.example.com 并回车后，浏览器首先做两件事：\n​​解析网址​​：判断协议（默认 http 或 https）、域名（www.example.com）、路径（如 /page）等。\n​​检查缓存​​：先查本地是否有该网址的缓存（包括 HTML、CSS、JS、图片等资源）。如果有且未过期（通过 Cache-Control 或 Expires 头判断），直接使用缓存，跳过后续网络请求（节省时间）。\n例子：你昨天访问过 www.example.com，今天再次输入，若缓存未过期，浏览器直接从本地读取页面显示。\n​​2. DNS 解析：域名 → IP 地址​​如果缓存中没有，浏览器需要找到 www.example.com 对应的服务器 IP 地址（如 192.168.1.1），这一步叫 ​​DNS 解析​​。流程类似“查电话簿”：\n​​本地 DNS 缓存​​：先查浏览器/操作系统的本地 DNS 缓存（可能存过该域名的 IP）。\n​​递归查询​​：若本地没有，浏览器向本地 DNS 服务器（如运营商提供的）发起请求。本地 DNS 服务器若也没有，会递归查询：\n​​根域名服务器​​（全球 13 台，如 a.root-servers.net）：告诉本地 DNS“负责 .com 的顶级域名服务器（TLD）地址”。\n​​顶级域名服务器（TLD）​​（如 .com 的服务器）：告诉本地 DNS“example.com 的权威 DNS 服务器地址”。\n​​权威 DNS 服务器​​（example.com 的官方服务器）：返回 www.example.com 对应的 IP 地址（如 93.184.216.34）。\n​​缓存结果​​：本地 DNS 服务器会将结果缓存，方便后续请求；浏览器也会缓存 IP，减少重复查询。\n关键：DNS 解析可能耗时几十到几百毫秒，所以现代浏览器会并行查询（如预解析链接中的域名）。\n​​3. 建立 TCP 连接（三次握手）​​拿到服务器 IP 后，浏览器通过 ​​TCP 协议​​与服务器建立可靠连接。TCP 是“可靠的快递员”，需要先“确认对方在线”：\n​​第一次握手​​：浏览器发送一个 TCP 包（SYN=1），告诉服务器“我想和你建立连接”。\n​​第二次握手​​：服务器回复（SYN=1, ACK=1），表示“收到你的请求，我也想连接”。\n​​第三次握手​​：浏览器再回复（ACK=1），表示“收到你的确认，连接建立成功”。\n注意：如果是 HTTPS（https://www.example.com），在 TCP 连接建立后，还需要进行 ​​TLS 握手​​（协商加密算法、交换密钥），确保后续数据加密传输（防止被窃听）。\n​​4. 发送 HTTP 请求​​TCP 连接建立后，浏览器向服务器发送 ​​HTTP 请求​​（如 GET /page HTTP/1.1），请求内容包括：\n​​请求行​​：方法（GET/POST）、路径（/page）、协议版本（HTTP/1.1）。\n​​请求头​​：浏览器信息（User-Agent）、接受的响应格式（Accept）、Cookies（Cookie）、缓存策略（If-None-Match）等。\n​​请求体​​：如果是 POST 请求，会附带表单数据或 JSON（如登录时提交的用户名密码）。\n例子：你访问 www.example.com/login 并提交表单，浏览器会发送 POST 请求，请求体包含 username=admin\u0026amp;password=123。\n​​5. 服务器处理请求并返回响应​​服务器收到 HTTP 请求后，根据路径（如 /page）和参数（如查询字符串 ?id=1），调用后端程序处理：\n​​静态资源​​（如 HTML、CSS、图片）：直接读取服务器文件，返回给浏览器。\n​​动态资源​​（如用户登录、数据查询）：后端代码（如 Java/Python/Node.js）执行逻辑（查数据库、调用接口），生成动态内容（如 HTML 模板填充数据）。\n处理完成后，服务器返回 ​​HTTP 响应​​，包含：\n​​状态码​​：表示请求结果（200 OK 成功，404 Not Found 资源不存在，500 Internal Error 服务器错误）。\n​​响应头​​：内容类型（Content-Type: text/html）、缓存策略（Cache-Control）、服务器信息（Server: Nginx）等。\n​​响应体​​：实际内容（如 HTML 代码、JSON 数据、图片二进制流）。\n例子：请求 www.example.com 返回 200 OK，响应体是 HTML 代码 \u0026lt;html\u0026gt;\u0026lt;body\u0026gt;Hello World\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;。\n​​6. 浏览器解析与渲染页面​​浏览器收到响应后，开始“翻译”HTML 并显示页面，核心步骤是 ​​构建 DOM 树 → 构建 CSSOM 树 → 合并渲染树 → 布局 → 绘制​​：\n（1）解析 HTML，构建 DOM 树浏览器从 HTML 第一行开始解析，将标签（如 \u0026lt;div\u0026gt;, \u0026lt;p\u0026gt;）转换为树状结构的 ​​DOM 树​​（Document Object Model），每个节点代表一个 HTML 元素。\n注意：遇到 \u0026lt;script\u0026gt; 标签时，默认会​​阻塞渲染​​（防止 JS 修改 DOM 导致布局错乱），直到 JS 执行完毕（除非用 async 或 defer 标记）。\n（2）解析 CSS，构建 CSSOM 树同时，浏览器解析 CSS（内联/外部样式表），将样式规则（如 color: red、width: 100px）转换为 ​​CSSOM 树​​（CSS Object Model），描述每个 DOM 节点的样式。\n（3）合并 DOM 和 CSSOM，生成渲染树（Render Tree）将 DOM 树和 CSSOM 树合并，生成 ​​渲染树​​（仅包含可见元素，如排除 display: none 的节点）。\n（4）计算布局（Layout）渲染树中每个节点需要确定​​位置和大小​​（如 div 在屏幕左上角，宽度 300px），这一步叫“布局”或“重排”（Reflow）。\n（5）绘制（Paint）根据布局结果，将每个节点的颜色、边框、阴影等视觉属性填充到屏幕像素中，这一步叫“绘制”或“重绘”（Repaint）。\n例子：一个 \u0026lt;div style=\"color:red; width:100px;\"\u0026gt;Hello\u0026lt;/div\u0026gt; 会被解析为 DOM 节点，结合 CSSOM 中的 color:red 和 width:100px，最终在屏幕上显示红色、宽 100px 的“Hello”文字。\n​​7. 页面显示完成，后续交互​​渲染完成后，页面正式显示在屏幕上。此时浏览器还会：\n​​执行 JS​​：如果页面中有 JS 代码（如 script 标签），会触发事件（如 onclick）、操作 DOM（如动态添加元素）。\n​​监听事件​​：等待用户交互（如点击链接、滚动页面），触发新的 HTTP 请求或 JS 逻辑。\n例子：页面上的“提交”按钮绑定了 JS 事件，点击后会发送 POST 请求到服务器，重复上述流程（DNS 解析→TCP 连接→发送请求→接收响应→重新渲染部分页面）。\n​​总结流程图​​输入网址 → 缓存检查 → DNS 解析（域名→IP） → TCP 连接（三次握手） → 发送 HTTP 请求 → 服务器处理并返回响应 → 浏览器解析渲染（DOM/CSSOM→渲染树→布局→绘制） → 显示页面 → 后续交互。\n整个过程通常需要 ​​几百毫秒到几秒​​（取决于网络速度、服务器性能、页面复杂度），优化关键步骤（如减少 DNS 查询、压缩资源、使用 CDN）可显著提升加载速度。\n","date":"2025-06-29T17:21:22.509076685Z","image":"https://qiuyingtyan.top/upload/110762197_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/shu-ru-wang-zhi-hou-ye-mian-xian-shi-de-liu-cheng-shi-shi-me/","title":"输入网址后页面显示的流程是什么？"},{"content":"UDP 和 TCP 是传输层的两个核心协议，就像“快递员”和“邮局”的区别——一个追求速度，一个追求稳妥。\n​​1. 连接性：是否需要“先打电话”​​​​TCP（传输控制协议）​​：​​必须先建立连接​​，像打电话一样。\n发送数据前，双方要先“握手”确认对方在线（三次握手），传输结束后还要“挥手”断开连接（四次挥手）。\n例子：你要给朋友寄重要文件，得先打电话确认他在家（建立连接），送完文件再确认收货（断开连接）。\n​​UDP（用户数据报协议）​​：​​无需建立连接​​，像发短信一样。\n直接把数据打包成“包裹”（数据报）发出去，不管对方是否在线、能不能收到。\n例子：你在群里发一条“大家集合”，不用挨个确认每个人是否看到，发了就完事。\n​​2. 可靠性：是否“保价运输”​​​​TCP​​：​​可靠传输​​，像“保价快递”。\n它保证数据​​不丢失、不重复、按顺序到达​​。如果中途丢包，TCP 会自动重传；如果接收方没准备好，发送方会等待（流量控制）；如果网络拥堵，TCP 会减速（拥塞控制）。\n例子：你用微信发一段语音，微信底层用 TCP，确保你发的每一段语音对方都能完整听到，不会漏一段或乱序。\n​​UDP​​：​​不可靠传输​​，像“普通平信”。\n发出去的数据包可能丢、可能乱序，UDP 不负责找回或排序。它只管“尽力而为”地把数据送出去，不关心结果。\n例子：你看视频直播时，偶尔卡顿或花屏，就是因为直播常用 UDP——丢几帧画面影响不大，但延迟太高会影响体验，所以直播协议会在 UDP 上加“补救”（比如前向纠错）。\n​​3. 传输方式：是“流水”还是“包裹”​​​​TCP​​：​​流式传输​​，像“水管流水”。\n数据没有明确的“边界”，发送方和接收方按“字节流”处理。比如你发“你好”和“世界”，接收方可能收到“你好世界”（合并了），需要自己按逻辑拆分。\n​​UDP​​：​​数据报传输​​，像“一个个包裹”。\n每个 UDP 包（数据报）是独立的，有明确的长度和标识。接收方收到后，能直接知道这是哪个包、多长，不需要额外处理。\n​​4. 头部开销：包裹的“包装费”​​​​TCP​​：​​头部大​​（至少 20 字节），像“精装礼盒”。\n为了实现可靠传输，TCP 头部需要包含序号、确认号、窗口大小等信息，额外占用带宽。\n​​UDP​​：​​头部小​​（仅 8 字节），像“信封”。\n只有源端口、目的端口、长度、校验和，没有复杂的控制信息，适合轻量传输。\n​​5. 适用场景：什么时候用谁？​​​​TCP 适合​​：需要​​可靠、有序​​的场景，比如：\n网页浏览（HTTP 基于 TCP）；\n文件下载（FTP 基于 TCP）；\n邮件发送（SMTP 基于 TCP）；\n你的运维监控系统中，客户端上报数据（用 HTTP 即 TCP），因为需要确保数据完整到达服务端。\n​​UDP 适合​​：需要​​实时性、低延迟​​，允许少量丢包的场景，比如：\n视频通话（丢几帧画面影响小，但延迟高会卡）；\n在线游戏（移动指令延迟高会“操作跟手”）；\nDNS 查询（域名解析请求小，丢包可重试）；\n物联网传感器（比如温度上报，偶尔丢一个数据不影响整体趋势）。\n​​总结对比表​​特性\nTCP\nUDP\n连接性\n必须建立连接（三次握手）\n无需连接\n可靠性\n可靠（不丢包、不乱序）\n不可靠（可能丢包、乱序）\n传输方式\n流式（无边界）\n数据报（有边界）\n头部开销\n至少 20 字节\n仅 8 字节\n典型场景\n网页、文件下载、邮件\n视频通话、游戏、DNS\n你的项目场景\n客户端上报数据（HTTP/TCP）\n若需实时监控（如高频指标）\n​​回到运维监控系统​​：\n客户端上报数据用 HTTP（基于 TCP），因为需要确保每一条监控数据（比如 CPU 使用率）完整到达服务端，不能丢。而如果你们需要实时展示某些高频指标（比如每秒 100 次的心跳），可能会用 UDP——但需要在应用层自己处理丢包（比如丢包后用前一个值填充），因为 UDP 不保证可靠。\n简单说：​​需要稳妥选 TCP，需要速度选 UDP​​。\n","date":"2025-06-29T17:18:06.416969615Z","image":"https://qiuyingtyan.top/upload/99407045_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/udphe-tcpde-qu-bie/","title":"UDP和TCP的区别"},{"content":"HTTP 和 WebSocket 是两种不同的网络通信协议，核心区别在于​​通信方向​​、​​连接状态​​和​​适用场景​​。\n​​1. 通信方向：单向 vs 全双工​​​​HTTP​​：​​单向请求-响应模式​​。\n只能由客户端主动发起请求（比如打开网页、点按钮查数据），服务器收到请求后返回响应，之后连接就关闭了（HTTP/1.1 虽然支持长连接，但还是“你问我答”的模式）。\n例子：你去奶茶店点单（客户端请求），店员做奶茶后给你（服务器响应），之后店员等你下次再来（连接关闭）。\n​​WebSocket​​：​​全双工双向通信​​。\n连接建立后，客户端和服务器可以​​随时互相发消息​​，不需要一方先发起请求。就像两个人开着视频通话，你说一句，我说一句，不用每次都“喂，听得到吗？”。\n例子：你和朋友开微信语音，你能随时说话，朋友也能随时插话，不用等对方先开口。\n​​2. 连接状态：无状态 vs 长连接​​​​HTTP​​：​​无状态​​。\n每次请求都是独立的，服务器不记得“你是谁”（除非用 Cookie/Token 手动记录）。比如你登录后刷新页面，服务器需要重新验证你的身份。\n例子：你去超市买东西，每次结账都要出示会员卡（Token），店员不会记住你之前买过什么。\n​​WebSocket​​：​​长连接​​。\n一旦连接建立（握手阶段），双方保持“在线”状态，直到一方主动关闭。服务器能记住客户端的身份（比如通过 Token 鉴权），适合需要持续交互的场景。\n例子：你在家开空调，用手机 APP 远程控制，APP 和空调服务器保持长连接，你能随时查看温度、调整模式，不用每次操作都重新连接。\n​​3. 协议层级与握手方式​​​​HTTP​​：基于 TCP 的应用层协议，直接使用 TCP 连接。\n每次请求都要携带完整的 HTTP 头部（比如 Host、Cookie、User-Agent），冗余信息多，适合“一次性”数据传输。\n​​WebSocket​​：基于 TCP 的应用层协议，但​​通过 HTTP 握手升级​​。\n连接建立时，客户端先发一个 HTTP 请求（Upgrade: websocket），服务器同意后，双方切换为 WebSocket 协议，之后的通信用更小的帧头（仅 2-10 字节），效率更高。\n​​4. 适用场景​​​​HTTP 适合​​：\n传统的“客户端发起请求，服务器返回结果”的场景，比如：\n刷网页（加载 HTML/CSS/JS）；\n调用 API 查数据（比如查天气、查订单）；\n提交表单（登录、下单）。\n​​WebSocket 适合​​：\n需要​​实时双向交互​​的场景，比如：\n聊天软件（消息实时推送）；\n监控看板（服务器状态实时更新）；\n远程控制（比如你项目中用 WebSocket 实现的 SSH 终端，打字、回显实时同步）；\n股票行情（股价变动秒级推送）。\n​​总结对比表​​特性\nHTTP\nWebSocket\n通信方向\n单向（客户端→服务器）\n全双工（双向）\n连接状态\n无状态（每次请求独立）\n长连接（保持在线）\n协议层级\n纯应用层（基于 TCP）\n应用层（HTTP 握手后升级）\n头部开销\n大（每次请求带完整头部）\n小（仅初始握手，后续帧头很小）\n典型场景\n网页加载、API 调用、表单提交\n实时聊天、监控、远程控制\n在运维监控系统里用 WebSocket 实现 SSH 远程控制，就是典型的“需要实时双向交互”的场景——用户在网页敲命令，服务器实时返回执行结果，这用 HTTP 的“请求-响应”模式根本做不到（每次发命令都要等服务器响应，无法实时显示输入的字符）。所以选 WebSocket 是因为它的全双工特性，能保证操作和反馈的实时性。\n","date":"2025-06-29T17:15:37.569778726Z","image":"https://qiuyingtyan.top/upload/816f5072-3ca1-47a4-9f3b-789ebde14a8d.webp","permalink":"https://qiuyingtyan.top/p/httphe-websockettong-xin-de-qu-bie/","title":"Http和WebSocket通信的区别"},{"content":"​​一、数据库设计：理清角色-权限-用户的关系​​RBAC 的核心是三张核心表 + 两张关联表，具体如下（简化版）：\n表名\n作用\nsys_user\n用户表（存储账号、密码、状态等）\nsys_role\n角色表（比如「系统管理员」「普通运维」「只读用户」）\nsys_permission\n权限表（具体操作权限，比如「server:manage」「monitor:view」）\nsys_user_role\n用户-角色关联表（一个用户可有多个角色）\nsys_role_perm\n角色-权限关联表（一个角色可拥有多个权限）\nsys_server\n服务器表（存储被管理的服务器信息，比如IP、名称）\nsys_server_role\n服务器-角色关联表（一个服务器可分配给多个角色，实现「某服务器由某角色管理」）\n举个例子：\n用户A的角色是「运维组」，「运维组」角色拥有「server:manage」权限；\n服务器1被分配了「运维组」角色，所以用户A能管理服务器1；\n服务器2被分配了「管理员」角色，用户A没有「管理员」角色，所以管不了服务器2。\n​​二、服务端实现：SpringSecurity 整合 JWT + RBAC​​我们用 SpringSecurity 做权限校验，结合 JWT 实现无状态认证，核心步骤如下：\n1. ​​自定义用户认证（UserDetailsService）​​用户登录时，前端传账号密码，服务端通过 UserDetailsService 加载用户信息（包括角色、权限）。这里需要从数据库查用户，再查关联的角色和权限，封装成 UserDetails 对象返回。\n@Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private SysUserMapper userMapper; // MyBatis-Plus 用户表Mapper @Override public UserDetails loadUserByUsername(String username) { // 1. 查用户是否存在 SysUser user = userMapper.selectOne(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getUsername, username)); if (user == null) { throw new UsernameNotFoundException(\u0026quot;用户不存在\u0026quot;); } // 2. 查用户的所有角色（通过 sys_user_role 关联表） List\u0026amp;lt;SysRole\u0026amp;gt; roles = roleMapper.selectRolesByUserId(user.getId()); // 3. 查角色对应的所有权限（通过 sys_role_perm 关联表） List\u0026amp;lt;String\u0026amp;gt; perms = roles.stream() .flatMap(role -\u0026amp;gt; permMapper.selectPermsByRoleId(role.getId()).stream()) .collect(Collectors.toList()); // 4. 封装成 SpringSecurity 的 UserDetails（包含权限） return new CustomUserDetails( user.getUsername(), user.getPassword(), user.getStatus() == 1, // 是否启用 true, true, true, Collections.emptyList(), // 权限集合（这里用字符串列表，实际可用 Permission 对象） roles, perms ); } }2. ​​权限拦截与校验​​通过 @PreAuthorize 注解或自定义拦截器，校验用户是否有权限访问某个接口或操作。比如：\n@RestController @RequestMapping(\u0026quot;/server\u0026quot;) public class ServerController {\n// 只有拥有 'server:manage' 权限的用户才能访问 @PreAuthorize(\u0026quot;hasAuthority('server:manage')\u0026quot;) @PostMapping(\u0026quot;/add\u0026quot;) public Result addServer(@RequestBody Server server) { // 添加服务器逻辑 } // 拥有 'server:view' 权限的用户都能查看 @PreAuthorize(\u0026quot;hasAuthority('server:view')\u0026quot;) @GetMapping(\u0026quot;/list\u0026quot;) public Result listServers() { // 查询服务器列表 } }3. ​​动态权限加载（解决角色/权限变更后缓存问题）​​因为用了 JWT（无状态），用户权限变更后，旧 JWT 仍然有效，可能导致权限未及时更新。我们的解决方法是：\n在 sys_user 表加 version 字段（每次修改用户权限时 version+1）；\nJWT 中携带 version 信息；\n每次请求时，服务端校验 JWT 中的 version 是否与数据库一致，不一致则拒绝请求并让用户重新登录。\n// 自定义 JWT 校验过滤器（关键逻辑） public class JwtAuthenticationFilter extends OncePerRequestFilter {\n@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { String token = extractToken(request); if (token != null) { try { // 解析 JWT 得到用户信息和 version Claims claims = jwtUtil.parseToken(token); String username = claims.getSubject(); Integer jwtVersion = claims.get(\u0026quot;version\u0026quot;, Integer.class); // 查数据库用户的当前 version SysUser user = userMapper.selectByUsername(username); if (user.getVersion() == null || !user.getVersion().equals(jwtVersion)) { // 版本不一致，权限可能变更，拒绝请求 response.setStatus(HttpStatus.UNAUTHORIZED.value()); return; } // 校验通过，生成 UserDetails 并设置到 SecurityContext UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); SecurityContextHolder.getContext().setAuthentication(auth); filterChain.doFilter(request, response); } catch (JwtException e) { // Token 无效，返回未授权 response.setStatus(HttpStatus.UNAUTHORIZED.value()); } } else { filterChain.doFilter(request, response); } } }4. ​​服务器-角色关联的权限控制​​除了用户角色的权限，还要控制「用户是否能管理某台具体服务器」。比如：\n用户A有「运维组」角色，该角色被分配了服务器1和服务器2；\n用户A访问服务器3的管理接口时，需要校验「运维组」是否拥有服务器3的管理权限。\n这部分在服务端接口中额外处理：\n@PostMapping(\u0026quot;/operate/{serverId}\u0026quot;) public Result operateServer(@PathVariable Long serverId, @RequestBody OperateReq req) { // 1. 当前用户 String username = SecurityContextHolder.getContext().getAuthentication().getName(); // 2. 查用户拥有的角色 List\u0026lt;SysRole\u0026gt; userRoles = roleService.getUserRoles(username); // 3. 查这些角色是否被分配了当前服务器 boolean hasPermission = serverRoleService.checkServerAssignedToRoles(serverId, userRoles); if (!hasPermission) { throw new AccessDeniedException(\u0026ldquo;无权限操作该服务器\u0026rdquo;); } // 4. 执行操作\u0026hellip; }​​三、前端配合：动态菜单与按钮权限​​前端（Vue3）需要根据用户权限动态渲染菜单和按钮，避免显示无权限的功能。具体步骤：\n1. ​​登录后获取权限信息​​用户登录成功后，后端返回用户的角色、权限列表（比如 [\u0026lsquo;server:manage\u0026rsquo;, \u0026lsquo;monitor:view\u0026rsquo;]），前端存储到 Vuex 或 Pinia 中。\n2. ​​动态生成菜单​​菜单数据从后端获取（根据用户权限过滤），比如：\n管理员看到「服务器管理」「监控看板」「用户管理」；\n普通运维只看到「服务器管理」「监控看板」。\n// Vue 组件中获取菜单 async function getMenus() { const res = await axios.get(\u0026rsquo;/api/menus\u0026rsquo;, { headers: { Authorization: Bearer ${token} } }); // 根据用户权限过滤菜单（后端已处理，前端直接渲染） store.commit(\u0026lsquo;setMenus\u0026rsquo;, res.data); }3. ​​按钮级权限控制​​通过自定义指令 v-permission 控制按钮是否显示：\n// 注册全局指令 app.directive(\u0026lsquo;permission\u0026rsquo;, { mounted(el, binding) { const perms = store.state.user.permissions; // 用户权限列表 const requiredPerm = binding.value; // 需要的权限（如 \u0026lsquo;server:manage\u0026rsquo;） if (!perms.includes(requiredPerm)) { el.parentNode?.removeChild(el); // 无权限则移除按钮 } } });\n// 使用示例 \u0026lt;button v-permission=\u0026quot;\u0026lsquo;server:manage\u0026rsquo;\u0026quot;\u0026gt;删除服务器\u0026lt;/button\u0026gt;​​四、关键细节与踩坑​​​​权限缓存​​：用户权限信息存在 Redis 中（键：user:perm:${username}），避免每次请求都查数据库。用户登出或权限变更时，删除对应缓存。\n​​JWT 与 RBAC 结合​​：JWT 中除了用户信息，还要存角色/权限的摘要（比如角色ID列表），避免每次请求都查数据库（但最终校验还是以数据库为准）。\n​​动态路由​​：前端路由需要根据权限动态添加（比如用 router.addRoute()），避免无权限的路由被访问。\n​​服务器-角色关联的灵活性​​：通过 sys_server_role 表实现「多对多」关系，一个服务器可分配给多个角色，一个角色可管理多个服务器，满足复杂权限需求。\n总结来说， RBAC 实现围绕「用户-角色-权限-服务器」四者关系，通过 SpringSecurity 做权限校验，JWT 做无状态认证，前端动态渲染，最终实现了「不同账户管理不同服务器」的灵活权限控制。\n","date":"2025-06-29T17:07:17.713862212Z","image":"https://qiuyingtyan.top/upload/86499845_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/rbacshi-zen-me-xie-de/","title":"rbac是怎么写的？"},{"content":"其实项目里用WebSocket主要是为了前端能通过网页直接操作后端的服务器，比如执行命令、看实时输出，就像本地连SSH一样。那我分服务端和前端两部分说说吧。\n​​服务端怎么实现的？​​首先，服务端得支持WebSocket协议。Spring Boot有内置的WebSocket支持，我们用了@EnableWebSocketMessageBroker来配置。核心是配两个东西：​​端点​​和​​消息代理​​。端点就是前端连过来的入口，比如/ssh-websocket，前端通过这个地址建立连接。消息代理的话，我们用SimpleBroker来处理订阅，前端发送的消息会通过/app前缀的路径到服务端，服务端处理完再通过/topic推送给前端。\n然后，​​鉴权​​是关键。因为不是谁都能连这个WebSocket，得确保是登录过的用户。我们在握手阶段（也就是前端刚连上的时候）做了校验：前端请求头里带着JWT Token，服务端从请求里掏出来，用自定义的JwtTokenProvider验证是否有效。如果无效，直接拒绝连接；有效的话，就把用户信息（比如用户名）存到attributes里，后面处理消息时能取到。\n接下来是​​会话管理​​。每个连进来的WebSocket会话（WebSocketSession）得和具体的SSH会话绑定。我们用了JSCH库来连服务器，每个用户连服务器前，得先创建一个JSCH的Session（比如输入用户名、密码、服务器地址）。为了不让这些会话乱，我们用了一个ConcurrentHashMap来存——键是WebSocket的sessionId，值是对应的JSCH Session。这样前端发命令时，服务端能根据sessionId找到对应的SSH连接，执行命令并返回结果。\n处理消息的时候，前端发来的消息是JSON格式的，比如{\"type\":\"command\", \"content\":\"ls -l\"}。服务端解析后，如果是执行命令，就用JSCH的ChannelExec执行，把结果读出来再封装成JSON（比如{\"type\":\"output\", \"content\":\"xxx\"}），通过WebSocket发回前端。如果是调整窗口大小（比如用户拖拽终端窗口），就调用JSCH的setPtySize方法改终端尺寸。\n还有，前端断开的时候，服务端得清理资源。我们在afterConnectionClosed方法里，根据sessionId把对应的JSCH Session关掉，避免资源泄露。\n​​前端怎么实现的？​​前端用的是Vue3，主要依赖SockJS和StompJS——因为不是所有浏览器都支持原生的WebSocket，SockJS能兼容，StompJS用来处理消息协议。然后，用Xterm.js来渲染终端界面，它模拟了真实的命令行效果，支持输入、输出、光标移动这些。\n具体步骤大概是这样的：\n​​建立连接​​：页面加载时，前端用SockJS连服务端的/ssh-websocket端点，再用Stomp订阅消息。比如：\nconst socket = new SockJS('/api/ssh-websocket'); // 实际路径可能带前缀 this.stompClient = Stomp.over(socket); this.stompClient.connect({}, () =\u0026gt; { // 连成功后订阅消息，比如服务端返回的输出会到这里 this.stompClient.subscribe('/user/topic/ssh-output', message =\u0026gt; { const res = JSON.parse(message.body); if (res.type === 'OUTPUT') { this.terminal.write(res.content); // 输出到终端 } }); });​​初始化终端​​：用Xterm.js创建一个终端实例，挂载到页面的某个div上，设置主题（比如暗黑模式）、字体大小这些。然后监听用户的键盘输入，用户按键盘时，调用terminal.onData(data)拿到输入的内容，通过WebSocket发给服务端。\n​​发送命令和调整窗口​​：用户输入命令后，前端把命令包装成JSON（比如{\"type\":\"command\", \"content\":\"ls\"}），通过stompClient.send('/app/ssh-command', {}, JSON.stringify(payload))发给服务端。如果是调整窗口大小（比如浏览器窗口变了），就获取终端的行数和列数，同样发消息给服务端，让后端调整JSCH会话的终端尺寸。\n​​处理断开和重连​​：如果WebSocket断了，前端会自动尝试重连（可能需要加个延迟，避免一直连不上）。比如在disconnect事件里，设置个定时器，过几秒再试一次。\n​​遇到的坑和解决​​其实刚开始踩了不少坑。比如，​​会话绑定​​的时候，如果用户开了多个标签页，每个标签页的sessionId不一样，得确保每个标签页的SSH会话独立，不然会串命令。后来用ConcurrentHashMap存sessionId和JSCH会话的映射，每个标签页独立管理，解决了这个问题。\n还有​​鉴权失败​​的情况，前端连WebSocket时没带Token或者Token过期，服务端直接拒绝，但前端没提示。后来在连接失败的回调里加了个提示，让用户重新登录。\n另外，​​命令输出的实时性​​也有问题。刚开始服务端执行命令后，一次性把所有输出发给前端，如果命令输出很大（比如ls -l很多文件），前端渲染会卡。后来改成按行流式发送，服务端读一行发一行，前端逐行渲染，体验好多了。\n总的来说，WebSocket的核心是服务端和前端配合好消息格式、会话管理和鉴权，确保命令能准确执行，结果能实时显示。虽然中间遇到了一些细节问题，但通过调试和优化都解决了。\n","date":"2025-06-29T17:04:52.320756102Z","image":"https://qiuyingtyan.top/upload/116506885_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/websocketzen-me-xie-de/","title":"websocket怎么写的？"},{"content":"在 MySQL 中，连表（多表连接查询）是处理多表关联数据的核心操作，主要用于从多个表中提取关联数据。以下从​​连表问题的解决思路​​、​​常见连接类型的区别（重点左连接 vs 内连接）​​两个方面详细说明：\n一、MySQL 连表问题的解决思路连表问题的本质是通过​​关联字段​​将多个表的数据按逻辑关系合并，核心步骤如下：\n1. 明确业务需求首先需要明确：需要哪些表的数据？表之间的关联关系（如用户表 → 订单表，通过 user_id 关联）？需要过滤什么条件？最终要展示哪些字段？\n2. 选择合适的连接类型根据业务需求选择连接类型（内连接、左连接、右连接等），这是解决连表问题的关键。\n3. 编写连接语法使用 JOIN 关键字（或隐式连接的逗号分隔表名 + WHERE 条件）定义连接关系，核心是：\nON 子句：定义表间的关联条件（如 user.id = order.user_id）。\nWHERE 子句：对连接后的结果集进一步过滤（可选）。\n4. 处理 NULL 值和重复数据连接可能导致某些字段为 NULL（如左连接中右表无匹配的行），或因关联条件不严谨导致重复数据（如一对多关系未正确过滤），需通过 COALESCE()、DISTINCT 或业务逻辑处理。\n二、左连接（LEFT JOIN）与内连接（INNER JOIN）的区别MySQL 支持多种连接类型，最常用的是 ​​内连接（INNER JOIN）​​ 和 ​​左连接（LEFT JOIN）​​，核心区别在于​​是否保留主表的所有行​​。\n1. 内连接（INNER JOIN）​​逻辑​​：仅返回两个表中​​关联字段完全匹配​​的行（即同时存在于主表和关联表中的数据）。\n​​特点​​：\n若主表某行在关联表中无匹配，则该行不会出现在结果中。\n是最严格的连接类型，适合“需要关联表中存在对应数据”的场景（如查询“有订单的用户”）。\n​​示例​​：\n用户表（user）和订单表（order），查询“有订单的用户及其订单金额”：\nSELECT u.name, o.amount FROM user u INNER JOIN order o ON u.id = o.user_id;结果仅包含 user 和 order 中 user_id 匹配的行。\n2. 左连接（LEFT JOIN / LEFT OUTER JOIN）​​逻辑​​：以​​左表（第一个表）为基准​​，返回左表的所有行，无论右表是否有匹配；若右表无匹配，则右表字段用 NULL 填充。\n​​特点​​：\n主表（左表）数据完整保留，适合“需要主表所有数据，关联表可选”的场景（如查询“所有用户，包括无订单的用户”）。\n右表无匹配时，结果中右表字段为 NULL（可通过 COALESCE(o.amount, 0) 处理）。\n​​示例​​：\n同样查询用户和订单，但需要所有用户（包括无订单的）：\nSELECT u.name, COALESCE(o.amount, 0) AS amount FROM user u LEFT JOIN order o ON u.id = o.user_id;结果包含 user 表的所有行，无订单的用户 amount 为 0（或 NULL）。\n3. 其他连接类型（补充）​​右连接（RIGHT JOIN）​​：与左连接相反，以右表为基准保留所有行（实际使用较少，可通过调整主表位置用左连接替代）。\n​​全连接（FULL JOIN）​​：MySQL 不直接支持，需通过 LEFT JOIN UNION RIGHT JOIN 模拟，返回左右表所有行（无匹配时用 NULL 填充）。\n三、实际应用中的注意事项​​关联条件的位置​​：\nON 子句：定义表间关联的核心条件（如 u.id = o.user_id），在连接时过滤。\nWHERE 子句：对连接后的结果集进一步过滤（如 o.create_time \u0026gt; '2023-01-01'），在连接后过滤。\n​​避免笛卡尔积​​：\n若忘记写 ON 子句（或 WHERE 条件错误），会导致两表所有行两两组合（笛卡尔积），结果数据量爆炸（如 1000 行 × 1000 行 = 100 万行）。\n​​性能优化​​：\n连接字段建议添加索引（如 user.id 和 order.user_id），否则大表连接会非常慢。\n大表连接时，优先过滤数据量小的表（减少参与连接的行数）。\n总结​​内连接​​：只保留两表关联字段匹配的行，适合“需要关联表存在数据”的场景。\n​​左连接​​：保留左表所有行，右表无匹配时用 NULL 填充，适合“需要主表完整数据”的场景。\n解决连表问题的关键是：明确业务需求 → 选择连接类型 → 定义关联条件 → 处理结果中的 NULL 或重复数据。\n","date":"2025-06-29T17:00:46.085138009Z","image":"https://qiuyingtyan.top/upload/101796526_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/mysql-lianbiao-lianjie/","title":"MySQL中连表的问题是怎么解决的？左连接内连接之类的区别是什么？"},{"content":"整体架构位置RabbitMQ 在项目中作为消息中间件，主要负责处理​​短信发送任务队列​​，实现异步处理和流量削峰。\n核心组件​​生产者(Producer)​​:\n位于服务端应用中\n当系统需要发送短信时，将短信任务封装后发送到RabbitMQ\n​​队列(Queue)​​:\n创建专用的短信发送队列\n设置适当的消息持久化策略\n可能配置死信队列处理发送失败的短信\n​​消费者(Consumer)​​:\n独立的监听器组件负责消费队列中的短信任务\n实现重试机制处理发送失败的短信\n采用批量处理提高效率\n消息处理流程服务端产生短信发送需求\n将短信任务序列化为JSON格式\n发送到RabbitMQ的指定交换器\n交换器将消息路由到特定队列\n监听器从队列获取消息\n调用短信网关API发送短信\n处理发送结果，记录日志\n关键配置​​消息持久化​​: 确保重启后未处理的消息不丢失\n​​确认机制​​: 确保消息处理成功后才从队列中删除\n​​流量控制​​: 通过队列长度限制防止内存溢出\n​​死信处理​​: 对失败消息进行特殊处理\n​​并发控制​​: 限制同时处理消息的数量\n架构优势​​解耦​​: 短信发送逻辑与主业务逻辑分离\n​​异步​​: 非阻塞方式处理短信发送\n​​削峰​​: 缓冲突发的大量短信发送请求\n​​可靠​​: 确保短信发送任务不会因服务重启而丢失\n","date":"2025-06-29T16:58:05.911039584Z","image":"https://qiuyingtyan.top/upload/%E5%AE%89%E8%A3%85%E5%BA%94%E7%94%A8.webp","permalink":"https://qiuyingtyan.top/p/rabbitmqde-jia-gou/","title":"RabbitMQ的架构"},{"content":"1. 客户端数据采集与上报使用​​oshi框架​​实时采集监控主机的硬件和系统数据（CPU、内存、磁盘等）\n通过​​Spring Quartz​​定时任务定期（如每30秒）收集数据\n采集的数据以JSON格式存储了服务端连接信息\n采用​​HTTP请求​​将监控数据上报到服务端\n2. 服务端数据处理与存储服务端接收客户端上报的监控数据\n将数据存入​​InfluxDB​​时序数据库（高效存储时间序列监控数据）\n使用​​Redis​​进行缓存优化，提高数据获取效率\n3. 前端数据获取与展示​​WebSocket连接​​：服务端与前端建立WebSocket连接\n​​实时推送​​：当有新的监控数据时，服务端主动通过WebSocket推送到前端\n​​图表渲染​​：前端接收到数据后，使用图表库（可能是ECharts等）实时更新监控数据图表\n​​历史查询​​：前端也可通过API从InfluxDB获取历史数据进行展示\n","date":"2025-06-29T16:55:57.171616352Z","image":"https://qiuyingtyan.top/upload/92793694_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/jian-kong-shu-ju-shi-zen-me-huo-qu-tui-song-dao-qian-duan-de/","title":"监控数据是怎么获取推送到前端的？"},{"content":"客户端开发难点​​跨平台数据采集​​\n问题：不同操作系统硬件数据采集方式不同\n解决方案：使用Oshi框架实现跨平台硬件监控，针对不同操作系统封装统一的接口调用方式\n​​定时任务可靠性​​\n问题：确保定时任务在客户端重启或异常关闭后能恢复\n解决方案：利用Spring Quartz持久化任务配置到数据库，结合客户端启动时的任务恢复机制\n​​安全连接信息存储​​\n问题：注册信息与连接信息需要安全存储\n解决方案：JSON配置文件加密存储，关键信息加密处理\n服务端开发难点​​实时数据处理与展示​​\n问题：监控数据量大，前端图表需要平滑更新\n解决方案：使用InfluxDB时序数据库优化存储与查询效率，设计合理的数据保留策略\n​​WebSocket连接管理​​\n问题：大量客户端连接时的性能与断线重连问题\n解决方案：实现WebSocket心跳检测机制，结合Redis记录连接状态，优化连接池配置\n​​权限控制与多账户管理​​\n问题：不同账户对不同服务器的权限控制\n解决方案：基于Spring Security + JWT实现细粒度权限控制，设计角色-权限-资源的多层关联模型\n​​高并发请求处理​​\n问题：大量客户端上报数据导致接口压力\n解决方案：Redis实现接口限流，消息队列(RabbitMQ)异步处理非实时任务，优化数据库批量写入策略\n​​分布式系统跟踪​​\n问题：分布式环境下请求链路追踪困难\n解决方案：自定义雪花ID生成器，通过过滤器在请求入口生成唯一ID并传递至整个调用链\n前端开发难点​​实时数据可视化​​\n问题：大量数据点的高效渲染与实时更新\n解决方案：ECharts按需渲染和数据采样，结合WebSocket实现数据推送而非轮询\n​​伪终端交互体验​​\n问题：Web端SSH操作的流畅性与一致性\n解决方案：Xterm.js结合WebSocket实现全功能终端模拟，添加输入缓冲和命令缓存机制\n​​响应式设计与暗黑模式​​\n问题：复杂UI组件在暗黑模式下的样式适配\n解决方案：基于CSS变量实现主题切换，Element Plus按需定制主题\n​​前端性能优化​​\n问题：多标签页和复杂图表导致的内存占用高\n解决方案：路由懒加载，组件按需引入，合理使用keep-alive缓存组件状态\n系统架构难点​​服务间通信与解耦​​\n问题：多服务间通信复杂，需要保证消息可靠传递\n解决方案：RabbitMQ实现异步通信和解耦，支持消息持久化和失败重试\n​​多环境配置管理​​\n问题：开发、测试、生产环境的差异化配置管理\n解决方案：Spring Profile结合外部配置中心，实现一键环境切换\n​​日志收集与分析​​\n问题：分布式系统中日志分散，问题定位困难\n解决方案：统一日志格式(包含雪花ID)，ELK收集分析，支持请求链路追踪\n","date":"2025-06-29T16:54:17.794291675Z","image":"https://qiuyingtyan.top/upload/93438097_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/xiang-mu-zhong-yu-dao-de-kun-nan-he-nan-dian-shi-zen-me-jie-jue-de/","title":"项目中遇到的困难和难点是怎么解决的？"},{"content":"项目中 SSH 连接与类似 Xshell 页面的实现主要依赖 ​​服务端 JSCH 框架​​ 和 ​​前端 Xterm.js 组件​​，结合 WebSocket 实现实时交互。\n一、SSH 连接的核心实现（服务端）​​服务端通过 ​​JSCH（Java SSH2 客户端库）​​ 建立与目标服务器的 SSH 连接，负责执行命令、获取输出，并将结果返回前端。关键流程如下：\n1. ​​SSH 连接建立​​​​触发条件​​：前端用户在 Xshell 页面输入命令（如 ssh user@host）并提交，或直接通过页面发起对某台服务器的管理请求。\n​​服务端处理​​：\n服务端接收前端通过 WebSocket 发送的 SSH 连接请求（包含目标服务器 IP、端口、用户名、密码/密钥等信息）。\n使用 JSCH 创建 Session 对象，配置连接参数（如超时时间、加密算法），并通过 session.connect() 建立到目标服务器的 SSH 连接。\n2. ​​命令执行与输出获取​​​​命令传递​​：前端用户在 Xterm 页面输入命令（如 ls -l），通过 WebSocket 发送到服务端。\n​​命令执行​​：服务端通过 JSCH 的 ChannelExec 或 ChannelShell 通道，将命令发送到目标服务器执行。\n若为交互式命令（如需要持续输入的 top），使用 ChannelShell 保持长连接，持续接收输出；\n若为单条命令（如 df -h），使用 ChannelExec 执行后关闭通道。\n​​输出捕获​​：通过 channel.setOutputStream() 或 channel.setErrStream() 捕获命令的标准输出和错误输出，存储为字符串或字节数组。\n3. ​​结果返回前端​​服务端将捕获的输出通过 WebSocket 实时推送回前端，前端 Xterm.js 将输出渲染到终端界面，完成一次完整的“输入-执行-输出”闭环。\n​​二、类似 Xshell 页面的实现（前端）​​前端通过 ​​Xterm.js​​ 模拟终端界面，结合 WebSocket 与服务端实时交互，实现类 Xshell 的操作体验。关键功能如下：\n1. ​​终端界面渲染​​​​Xterm.js 初始化​​：在前端页面（如 Vue 组件）中初始化 Xterm.js 实例，配置终端参数（如行高、字体、主题色、光标样式）。\nimport { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; // 初始化终端 const term = new Terminal({ cursorBlink: true, theme: { background: \u0026lsquo;#1E1E1E\u0026rsquo;, foreground: \u0026lsquo;#FFFFFF\u0026rsquo; }, fontSize: 14, fontFamily: \u0026lsquo;Menlo, Monaco, \u0026ldquo;Courier New\u0026rdquo;, monospace\u0026rsquo; }); const fitAddon = new FitAddon(); term.loadAddon(fitAddon); term.open(document.getElementById(\u0026lsquo;ssh-terminal\u0026rsquo;)); // 挂载到 DOM fitAddon.fit(); // 自适应容器大小​​ANSI 转义序列支持​​：Xterm.js 内置解析 ANSI 转义码（如颜色 \\033[31m、光标移动 \\033[1A、清屏 \\033[2J），能正确渲染远程服务器的输出（如命令提示符、进度条、错误信息）。\n2. ​​用户输入与实时交互​​​​输入监听​​：监听终端的 onData 事件，捕获用户输入的字符（包括组合键如 Ctrl+C、Tab 补全）。\nterm.onData(data =\u0026gt; { // 通过 WebSocket 发送用户输入到服务端 websocket.send(JSON.stringify({ type: \u0026lsquo;ssh-command\u0026rsquo;, data })); });​​实时输出显示​​：服务端通过 WebSocket 推送输出数据时，前端调用 term.write(output) 将内容写入终端，模拟真实终端的逐行/逐字符显示效果。\n3. ​​会话管理与状态保持​​​​长连接支持​​：使用 WebSocket 维持前端与服务端的持久连接，确保长时间运行的命令（如 top、tail -f）输出能实时刷新。\n​​会话断开处理​​：若 WebSocket 连接中断（如网络波动），前端提示用户重新连接；服务端检测到连接断开后，关闭对应的 JSCH Session 释放资源。\n​​三、前后端协同流程总结​​整个 SSH 交互流程可简化为以下步骤：\n用户在前端 Xterm 页面输入命令（如 ssh root@192.168.1.100）并回车；\n前端通过 WebSocket 将命令发送至服务端；\n服务端使用 JSCH 解析命令，建立与目标服务器的 SSH 连接；\n服务端执行命令（或传递到目标服务器执行），捕获输出；\n服务端将输出通过 WebSocket 实时推送回前端；\n前端 Xterm.js 渲染输出，用户看到命令执行结果；\n重复步骤 1-6，完成交互式操作。\n​​四、关键技术点补充​​​​权限控制​​：服务端通过 SpringSecurity + JWT 校验用户身份，结合子账户权限配置（如限制某些用户仅能访问特定服务器），确保 SSH 操作的安全性。\n​​连接复用​​：服务端缓存活跃的 SSH Session（如通过 Redis 存储会话 ID 与 Session 对象的映射），避免频繁创建/销毁连接带来的性能损耗。\n​​异常处理​​：服务端捕获 JSCH 异常（如连接超时、认证失败），通过 WebSocket 返回结构化错误信息（如 { code: 401, msg: \u0026ldquo;认证失败\u0026rdquo; }），前端统一处理并提示用户。\n综上，你的项目通过 ​​服务端 JSCH 实现 SSH 连接与命令执行​​，结合 ​​前端 Xterm.js 模拟终端界面​​ 和 ​​WebSocket 实时通信​​，最终实现了类似 Xshell 的交互式 SSH 管理功能。\n","date":"2025-03-05T16:09:58.083488754Z","image":"https://qiuyingtyan.top/upload/86432139_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/ru-he-shi-xian-sshlian-jie-he-lei-si-xshellye-mian/","title":"如何实现ssh连接和类似xshell页面？"},{"content":"一、什么是Maven？Maven 是一个流行的项目管理工具和构建工具，可以对 Java 项目进行构建、依赖管理。简单来说，它就像一个“助手”，使得项目的开发和管理变得更加简单和高效。\n其它知识点补充：\n项目管理工具：指的是一种软件，帮助开发者组织、管理和跟踪项目的进展。使用 Maven，可以轻松创建、配置和维护 Java 项目。\n构建：是指将源代码转换为可以运行的程序的过程。Maven 可以自动化这个过程，它会根据指定的配置文件（通常是 pom.xml）来编译代码、打包程序、运行测试等。\n依赖管理：是指处理项目中使用的库或其他组件（称为依赖）的过程。很多 Java 项目会依赖于其他库来实现某些功能。Maven 可以自动下载这些依赖，并确保它们的版本兼容，这样开发者就不需要手动管理这些库的版本和下载。\n二、Maven有什么用？我们为什么要学Maven？Maven 有什么用项目的自动构建，包括代码的编译、测试、打包、安装、部署等操作。\n依赖管理，项目使用到哪些依赖，可以快速完成导入，不需要手动导入jar包。\n我们为什么要学 Maven？简单一句话，你不学maven就别想学别的东西(例如springcloud)！\n三、Maven项目结构\n而下面的pom.xml则是Maven的核心配置，也是整个项目的所有依赖、插件、以及各种配置的集合，它也是使用XML格式编写的，一个标准的pom配置长这样：\n\u0026lt;?xml version=\"1.0\" encoding=\"UTF-8\"?\u0026gt; \u0026lt;project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;BookManage\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0\u0026amp;lt;/version\u0026amp;gt; \u0026amp;lt;properties\u0026amp;gt; \u0026amp;lt;maven.compiler.source\u0026amp;gt;17\u0026amp;lt;/maven.compiler.source\u0026amp;gt; \u0026amp;lt;maven.compiler.target\u0026amp;gt;17\u0026amp;lt;/maven.compiler.target\u0026amp;gt; \u0026amp;lt;project.build.sourceEncoding\u0026amp;gt;UTF-8\u0026amp;lt;/project.build.sourceEncoding\u0026amp;gt; \u0026amp;lt;/properties\u0026amp;gt; \u0026lt;/project\u0026gt;pom配置解释：project:：根节点。\nmodelVersion:：定义了当前模型的版本，不用管它。\ngroupId、artifactId、version:：这三个元素合在一起，用于唯一区别每个项目，别人如果需要将我们编写的代码作为依赖，那么就必须通过这三个元素来定位我们的项目，我们称为一个项目的基本坐标，所有的项目一般都有自己的Maven坐标，因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了，无需再下载Jar文件，而是Maven自动帮助我们下载依赖并导入。\ngroupId 一般用于指定组名称，命名规则一般和包名一致，比如我们这里使用的是org.example，一个组下面可以有很多个项目。\nartifactId 一般用于指定项目在当前组中的唯一名称，也就是说在组中用于区分于其他项目的标记。\nversion 代表项目版本，随着我们项目的开发和改进，版本号也会不断更新，我们可以手动指定当前项目的版本号，其他人使用我们的项目作为依赖时，也可以根据版本号进行选择（这里的SNAPSHOT代表快照，一般表示这是一个处于开发中的项目，正式发布项目一般只带版本号）\nproperties：这里一般都是一些变量和选项的配置，我们这里指定了JDK的源代码和编译版本为17，同时下面的源代码编码格式为UTF-8，无需进行修改。\n四、Maven依赖导入\u0026lt;dependencies\u0026gt; //里面填写的就是所有的依赖 \u0026lt;/dependencies\u0026gt;举例说明（以插入Lombok依赖为例）：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.projectlombok\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lombok\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.18.36\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;五、Maven依赖作用域除了三个基本的属性用于定位坐标外，依赖还可以添加以下属性：\ntype：依赖的类型，对于项目坐标定义的packaging。大部分情况下，该元素不必声明，其默认值为jar\nscope：依赖的范围\noptional：标记依赖是否可选\nexclusions：用来排除传递性依赖（一个项目有可能依赖于其他项目，就像我们的项目，如果别人要用我们的项目作为依赖，那么就需要一起下载我们项目的依赖，如Lombok）\n1. type作用：定义依赖的类型，对应项目的打包类型（packaging）。\n默认值：jar。\n使用场景：当依赖的类型不是 jar 时，例如 war（Web 应用）、pom（父项目定义）、zip 等，需要显式声明。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;example-artifact\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;war\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt;2. scope（重点）作用：定义依赖的作用范围。主要范围包括：\ncompile（默认值）：编译、测试和运行时都需要。\nprovided：编译和测试时需要，但运行时由容器（如 Tomcat）提供。\nruntime：运行和测试时需要，编译时不需要。\ntest：仅测试时需要，编译和运行时不需要。\nsystem：类似于 provided，但需要本地提供依赖路径（几乎不用）。\nimport：用于引入 BOM（Bill of Materials）依赖管理。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.0.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt;3. optional作用：标记依赖是否为可选依赖，避免被传递到依赖的消费者（下游项目）。\n默认值：false。\n使用场景：当依赖是可选功能模块，不想影响下游项目时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;optional-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;4. exclusions作用：用于排除传递性依赖，防止不必要的依赖被引入。\n使用场景：当依赖的项目中包含的某些传递性依赖与你的项目冲突时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-tomcat\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;六、Maven安装、可选和排除1、Maven 安装问题导入：如何在其他项目中引入我们自己编写的Maven项目作为依赖使用呢？\n先创建一个用于测试的简单项目：\n\u0026lt;?xml version=\u0026ldquo;1.0\u0026rdquo; encoding=\u0026ldquo;UTF-8\u0026rdquo;?\u0026gt; \u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;TestMaven\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0-SNAPSHOT\u0026amp;lt;/version\u0026amp;gt; ... \u0026lt;/project\u0026gt;public class TestUtils { public static void test() { System.out.println(\u0026ldquo;家人们谁懂啊，蒸虾头，怎么会有人想吃我家鸽鸽下的蛋\u0026rdquo;); } }接着我们点击右上角的Maven选项，然后执行install或直接在命令行中输入mvn install来安装我们自己的项目到本地Maven仓库中。\n接着我们就可以在需要使用此项目作为依赖的其他项目中使用它了，只需要填写和这边一样的坐标：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.test\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;TestMaven\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;接着我们就可以在项目中直接使用了：\npublic static void main(String[] args) { TestUtils.test(); }2、Maven 中的可选依赖（Optional）作用\nMaven 的可选依赖属性 optional 用于标记某个依赖是否是可选的。\n默认行为：Maven 会传递所有依赖的依赖（传递性依赖），即如果项目 A 依赖项目 B，而项目 B 又依赖项目 C，则项目 A 会默认获取 B 和 C。\n使用 optional 可以避免这种传递性。\n场景\n当某个依赖是额外的功能模块，而下游项目可能不需要它时。\n避免引入无用的依赖，减少构建时间和冲突风险。\n示例\n项目 A 依赖项目 B，项目 B 依赖项目 C：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;项目 B 中声明 C 为可选依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;结果：项目 A 不会自动获取 C，除非手动添加。\n3、Maven 中的依赖排除（Exclusions）作用\n排除传递性依赖中不需要的模块。\n为什么需要？\n防止依赖冲突（例如不同版本的库）。\n删除多余的依赖以优化项目构建。\n示例\n项目 A 依赖项目 B，而项目 B 又依赖项目 C，但项目 A 不需要 C。\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;传递性依赖的层级示例\n项目 A -\u0026gt; 项目 B -\u0026gt; 项目 C。\n如果不需要项目 C，可通过 exclusions 将其排除，最终 A 只包含 B。\n常见问题解决\n依赖冲突：当传递性依赖中某个库的多个版本引入冲突时，排除旧版本并手动引入所需版本。\n避免不必要的依赖：减少构建体积，避免不需要的模块。\nMaven的继承Maven 的继承机制允许项目的 POM 文件继承另一个 POM 文件中的配置，从而避免重复定义相同的配置。\n1. 继承的特点层级结构：Maven 的继承是树状结构，一个子项目只能继承一个父项目。\n统一管理：父 POM 通常定义通用的依赖、插件、构建配置等，子 POM 自动继承。\n减少重复：多个子项目可以共享父 POM 中的配置，提高复用性。\n2. 父 POM 定义父 POM 中定义通用配置，例如：\nx3. 子项目继承子项目通过 \u0026lt;parent\u0026gt; 标签指定父 POM：\nx子项目继承父 POM 的依赖管理和插件配置。\n可在子项目中覆盖或补充父 POM 的配置。\n多模块机制用于将一个大项目拆分为多个模块，每个模块都是一个独立的 Maven 项目，但可以共享配置和依赖。\nMaven的多模块1. 多模块结构多模块项目通常有一个父 POM 和多个子模块，目录结构如下：\nproject-root/ ├── pom.xml # 父 POM ├── module-a/ # 子模块 A │ └── pom.xml ├── module-b/ # 子模块 B │ └── pom.xml2. 父 POM（根 POM）父 POM 除了继承机制的配置外，还需要声明模块：\n\u0026lt;project\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026lt;groupId\u0026gt;com.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;multi-module-project\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;packaging\u0026gt;pom\u0026lt;/packaging\u0026gt; \u0026lt;!\u0026ndash; 必须是 pom 类型 \u0026ndash;\u0026gt;\n\u0026amp;lt;modules\u0026amp;gt; \u0026amp;lt;module\u0026amp;gt;module-a\u0026amp;lt;/module\u0026amp;gt; \u0026amp;lt;module\u0026amp;gt;module-b\u0026amp;lt;/module\u0026amp;gt; \u0026amp;lt;/modules\u0026amp;gt; \u0026lt;/project\u0026gt;3. 子模块的 POM子模块 POM 需要指定父项目：\n\u0026lt;project\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026lt;parent\u0026gt; \u0026lt;groupId\u0026gt;com.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;multi-module-project\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/parent\u0026gt; \u0026lt;artifactId\u0026gt;module-a\u0026lt;/artifactId\u0026gt; \u0026lt;/project\u0026gt;4. 构建和依赖管理构建：在根目录运行 mvn install，会依次构建所有模块。\n依赖管理：模块之间可以互相依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;module-a\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;5. 优势模块化：每个模块负责单一功能，便于开发和维护。\n共享配置：通过父 POM 统一管理版本和插件。\n并行构建：Maven 可以并行构建多个模块，加快构建速度。\nMaven测试和打包在 IDEA 中，Maven 项目有多个生命周期，每个生命周期对应一个插件执行任务。常见的命令如下：\nclean：清理 target 文件夹，解决缓存问题。\nvalidate：验证项目的可用性。\ncompile：编译项目为 .class 文件。\ninstall：将项目安装到本地仓库，供其他项目作为依赖使用。\nverify：依次执行 validate、compile、package 等生命周期阶段。\n每个命令执行完后，IDE 会显示 BUILD SUCCESS，如果出现错误则会显示详细信息。\n1. 测试命令通过 test 命令，Maven 会自动运行 test 目录下的所有测试案例。测试类的名称需要以 Test 结尾（例如 MainTest），测试方法必须标注 @Test 注解。示例如下：\npublic class MainTest {\n@Test public void test() { System.out.println(\u0026quot;我测你码\u0026quot;); } }2. 打包命令使用 package 命令可以将项目打包成 JAR 文件，生成的 JAR 文件可以作为其他项目的依赖或可执行文件。执行 package 时，会先自动执行测试命令，确保项目没有问题。如果希望跳过测试，可以使用以下命令：\nmvn package -Dmaven.test.skip=true3. 打包带依赖的 JAR 文件通常，打包后的 JAR 文件仅包含项目自己的类，而缺少所需的外部依赖。如果要将项目及其依赖一起打包，可以使用 maven-assembly-plugin 插件。插件配置如下：\n\u0026lt;plugin\u0026gt; \u0026lt;artifactId\u0026gt;maven-assembly-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;3.1.0\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;descriptorRefs\u0026gt; \u0026lt;descriptorRef\u0026gt;jar-with-dependencies\u0026lt;/descriptorRef\u0026gt; \u0026lt;/descriptorRefs\u0026gt; \u0026lt;archive\u0026gt; \u0026lt;manifest\u0026gt; \u0026lt;addClasspath\u0026gt;true\u0026lt;/addClasspath\u0026gt; \u0026lt;mainClass\u0026gt;com.test.Main\u0026lt;/mainClass\u0026gt; \u0026lt;/manifest\u0026gt; \u0026lt;/archive\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;executions\u0026gt; \u0026lt;execution\u0026gt; \u0026lt;id\u0026gt;make-assembly\u0026lt;/id\u0026gt; \u0026lt;phase\u0026gt;package\u0026lt;/phase\u0026gt; \u0026lt;goals\u0026gt; \u0026lt;goal\u0026gt;single\u0026lt;/goal\u0026gt; \u0026lt;/goals\u0026gt; \u0026lt;/execution\u0026gt; \u0026lt;/executions\u0026gt; \u0026lt;/plugin\u0026gt;配置好后，重新执行 package 命令，最终会生成两个 JAR 文件：一个是普通的 JAR 文件，另一个是包含所有依赖的 JAR 文件。我们只需要执行以下命令即可运行：\njava -jar your-project-jar-with-dependencies.jar\n\u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;BookManage\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0\u0026amp;lt;/version\u0026amp;gt; \u0026amp;lt;properties\u0026amp;gt; \u0026amp;lt;maven.compiler.source\u0026amp;gt;17\u0026amp;lt;/maven.compiler.source\u0026amp;gt; \u0026amp;lt;maven.compiler.target\u0026amp;gt;17\u0026amp;lt;/maven.compiler.target\u0026amp;gt; \u0026amp;lt;project.build.sourceEncoding\u0026amp;gt;UTF-8\u0026amp;lt;/project.build.sourceEncoding\u0026amp;gt; \u0026amp;lt;/properties\u0026amp;gt; \u0026lt;/project\u0026gt;pom配置解释：project:：根节点。\nmodelVersion:：定义了当前模型的版本，不用管它。\ngroupId、artifactId、version:：这三个元素合在一起，用于唯一区别每个项目，别人如果需要将我们编写的代码作为依赖，那么就必须通过这三个元素来定位我们的项目，我们称为一个项目的基本坐标，所有的项目一般都有自己的Maven坐标，因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了，无需再下载Jar文件，而是Maven自动帮助我们下载依赖并导入。\ngroupId 一般用于指定组名称，命名规则一般和包名一致，比如我们这里使用的是org.example，一个组下面可以有很多个项目。\nartifactId 一般用于指定项目在当前组中的唯一名称，也就是说在组中用于区分于其他项目的标记。\nversion 代表项目版本，随着我们项目的开发和改进，版本号也会不断更新，我们可以手动指定当前项目的版本号，其他人使用我们的项目作为依赖时，也可以根据版本号进行选择（这里的SNAPSHOT代表快照，一般表示这是一个处于开发中的项目，正式发布项目一般只带版本号）\nproperties：这里一般都是一些变量和选项的配置，我们这里指定了JDK的源代码和编译版本为17，同时下面的源代码编码格式为UTF-8，无需进行修改。\n四、Maven依赖导入\u0026lt;dependencies\u0026gt; //里面填写的就是所有的依赖 \u0026lt;/dependencies\u0026gt;举例说明（以插入Lombok依赖为例）：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.projectlombok\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lombok\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.18.36\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;五、Maven依赖作用域除了三个基本的属性用于定位坐标外，依赖还可以添加以下属性：\ntype：依赖的类型，对于项目坐标定义的packaging。大部分情况下，该元素不必声明，其默认值为jar\nscope：依赖的范围\noptional：标记依赖是否可选\nexclusions：用来排除传递性依赖（一个项目有可能依赖于其他项目，就像我们的项目，如果别人要用我们的项目作为依赖，那么就需要一起下载我们项目的依赖，如Lombok）\n1. type作用：定义依赖的类型，对应项目的打包类型（packaging）。\n默认值：jar。\n使用场景：当依赖的类型不是 jar 时，例如 war（Web 应用）、pom（父项目定义）、zip 等，需要显式声明。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;example-artifact\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;war\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt;2. scope（重点）作用：定义依赖的作用范围。主要范围包括：\ncompile（默认值）：编译、测试和运行时都需要。\nprovided：编译和测试时需要，但运行时由容器（如 Tomcat）提供。\nruntime：运行和测试时需要，编译时不需要。\ntest：仅测试时需要，编译和运行时不需要。\nsystem：类似于 provided，但需要本地提供依赖路径（几乎不用）。\nimport：用于引入 BOM（Bill of Materials）依赖管理。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.0.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt;3. optional作用：标记依赖是否为可选依赖，避免被传递到依赖的消费者（下游项目）。\n默认值：false。\n使用场景：当依赖是可选功能模块，不想影响下游项目时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;optional-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;4. exclusions作用：用于排除传递性依赖，防止不必要的依赖被引入。\n使用场景：当依赖的项目中包含的某些传递性依赖与你的项目冲突时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-tomcat\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;六、Maven安装、可选和排除1、Maven 安装问题导入：如何在其他项目中引入我们自己编写的Maven项目作为依赖使用呢？\n先创建一个用于测试的简单项目：\n\u0026lt;?xml version=\u0026ldquo;1.0\u0026rdquo; encoding=\u0026ldquo;UTF-8\u0026rdquo;?\u0026gt; \u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;TestMaven\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0-SNAPSHOT\u0026amp;lt;/version\u0026amp;gt; ... \u0026lt;/project\u0026gt;public class TestUtils { public static void test() { System.out.println(\u0026ldquo;家人们谁懂啊，蒸虾头，怎么会有人想吃我家鸽鸽下的蛋\u0026rdquo;); } }接着我们点击右上角的Maven选项，然后执行install或直接在命令行中输入mvn install来安装我们自己的项目到本地Maven仓库中。\n接着我们就可以在需要使用此项目作为依赖的其他项目中使用它了，只需要填写和这边一样的坐标：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.test\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;TestMaven\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;接着我们就可以在项目中直接使用了：\npublic static void main(String[] args) { TestUtils.test(); }2、Maven 中的可选依赖（Optional）作用\nMaven 的可选依赖属性 optional 用于标记某个依赖是否是可选的。\n默认行为：Maven 会传递所有依赖的依赖（传递性依赖），即如果项目 A 依赖项目 B，而项目 B 又依赖项目 C，则项目 A 会默认获取 B 和 C。\n使用 optional 可以避免这种传递性。\n场景\n当某个依赖是额外的功能模块，而下游项目可能不需要它时。\n避免引入无用的依赖，减少构建时间和冲突风险。\n示例\n项目 A 依赖项目 B，项目 B 依赖项目 C：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;项目 B 中声明 C 为可选依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;结果：项目 A 不会自动获取 C，除非手动添加。\n3、Maven 中的依赖排除（Exclusions）作用\n排除传递性依赖中不需要的模块。\n为什么需要？\n防止依赖冲突（例如不同版本的库）。\n删除多余的依赖以优化项目构建。\n示例\n项目 A 依赖项目 B，而项目 B 又依赖项目 C，但项目 A 不需要 C。\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;传递性依赖的层级示例\n项目 A -\u0026gt; 项目 B -\u0026gt; 项目 C。\n如果不需要项目 C，可通过 exclusions 将其排除，最终 A 只包含 B。\n常见问题解决\n依赖冲突：当传递性依赖中某个库的多个版本引入冲突时，排除旧版本并手动引入所需版本。\n避免不必要的依赖：减少构建体积，避免不需要的模块。\nMaven的继承Maven 的继承机制允许项目的 POM 文件继承另一个 POM 文件中的配置，从而避免重复定义相同的配置。\n1. 继承的特点层级结构：Maven 的继承是树状结构，一个子项目只能继承一个父项目。\n统一管理：父 POM 通常定义通用的依赖、插件、构建配置等，子 POM 自动继承。\n减少重复：多个子项目可以共享父 POM 中的配置，提高复用性。\n2. 父 POM 定义父 POM 中定义通用配置，例如：\nx3. 子项目继承子项目通过 \u0026lt;parent\u0026gt; 标签指定父 POM：\nx子项目继承父 POM 的依赖管理和插件配置。\n可在子项目中覆盖或补充父 POM 的配置。\n多模块机制用于将一个大项目拆分为多个模块，每个模块都是一个独立的 Maven 项目，但可以共享配置和依赖。\nMaven的多模块1. 多模块结构多模块项目通常有一个父 POM 和多个子模块，目录结构如下：\nproject-root/ ├── pom.xml # 父 POM ├── module-a/ # 子模块 A │ └── pom.xml ├── module-b/ # 子模块 B │ └── pom.xml2. 父 POM（根 POM）父 POM 除了继承机制的配置外，还需要声明模块：\n\u0026lt;project\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026lt;groupId\u0026gt;com.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;multi-module-project\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;packaging\u0026gt;pom\u0026lt;/packaging\u0026gt; \u0026lt;!\u0026ndash; 必须是 pom 类型 \u0026ndash;\u0026gt;\n\u0026amp;lt;modules\u0026amp;gt; \u0026amp;lt;module\u0026amp;gt;module-a\u0026amp;lt;/module\u0026amp;gt; \u0026amp;lt;module\u0026amp;gt;module-b\u0026amp;lt;/module\u0026amp;gt; \u0026amp;lt;/modules\u0026amp;gt; \u0026lt;/project\u0026gt;3. 子模块的 POM子模块 POM 需要指定父项目：\n\u0026lt;project\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026lt;parent\u0026gt; \u0026lt;groupId\u0026gt;com.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;multi-module-project\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/parent\u0026gt; \u0026lt;artifactId\u0026gt;module-a\u0026lt;/artifactId\u0026gt; \u0026lt;/project\u0026gt;4. 构建和依赖管理构建：在根目录运行 mvn install，会依次构建所有模块。\n依赖管理：模块之间可以互相依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;module-a\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;5. 优势模块化：每个模块负责单一功能，便于开发和维护。\n共享配置：通过父 POM 统一管理版本和插件。\n并行构建：Maven 可以并行构建多个模块，加快构建速度。\nMaven测试和打包在 IDEA 中，Maven 项目有多个生命周期，每个生命周期对应一个插件执行任务。常见的命令如下：\nclean：清理 target 文件夹，解决缓存问题。\nvalidate：验证项目的可用性。\ncompile：编译项目为 .class 文件。\ninstall：将项目安装到本地仓库，供其他项目作为依赖使用。\nverify：依次执行 validate、compile、package 等生命周期阶段。\n每个命令执行完后，IDE 会显示 BUILD SUCCESS，如果出现错误则会显示详细信息。\n1. 测试命令通过 test 命令，Maven 会自动运行 test 目录下的所有测试案例。测试类的名称需要以 Test 结尾（例如 MainTest），测试方法必须标注 @Test 注解。示例如下：\npublic class MainTest {\n@Test public void test() { System.out.println(\u0026quot;我测你码\u0026quot;); } }2. 打包命令使用 package 命令可以将项目打包成 JAR 文件，生成的 JAR 文件可以作为其他项目的依赖或可执行文件。执行 package 时，会先自动执行测试命令，确保项目没有问题。如果希望跳过测试，可以使用以下命令：\nmvn package -Dmaven.test.skip=true3. 打包带依赖的 JAR 文件通常，打包后的 JAR 文件仅包含项目自己的类，而缺少所需的外部依赖。如果要将项目及其依赖一起打包，可以使用 maven-assembly-plugin 插件。插件配置如下：\nx配置好后，重新执行 package 命令，最终会生成两个 JAR 文件：一个是普通的 JAR 文件，另一个是包含所有依赖的 JAR 文件。我们只需要执行以下命令即可运行：\nb\n\u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;BookManage\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0\u0026amp;lt;/version\u0026amp;gt; \u0026amp;lt;properties\u0026amp;gt; \u0026amp;lt;maven.compiler.source\u0026amp;gt;17\u0026amp;lt;/maven.compiler.source\u0026amp;gt; \u0026amp;lt;maven.compiler.target\u0026amp;gt;17\u0026amp;lt;/maven.compiler.target\u0026amp;gt; \u0026amp;lt;project.build.sourceEncoding\u0026amp;gt;UTF-8\u0026amp;lt;/project.build.sourceEncoding\u0026amp;gt; \u0026amp;lt;/properties\u0026amp;gt; \u0026lt;/project\u0026gt;pom配置解释：project:：根节点。\nmodelVersion:：定义了当前模型的版本，不用管它。\ngroupId、artifactId、version:：这三个元素合在一起，用于唯一区别每个项目，别人如果需要将我们编写的代码作为依赖，那么就必须通过这三个元素来定位我们的项目，我们称为一个项目的基本坐标，所有的项目一般都有自己的Maven坐标，因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了，无需再下载Jar文件，而是Maven自动帮助我们下载依赖并导入。\ngroupId 一般用于指定组名称，命名规则一般和包名一致，比如我们这里使用的是org.example，一个组下面可以有很多个项目。\nartifactId 一般用于指定项目在当前组中的唯一名称，也就是说在组中用于区分于其他项目的标记。\nversion 代表项目版本，随着我们项目的开发和改进，版本号也会不断更新，我们可以手动指定当前项目的版本号，其他人使用我们的项目作为依赖时，也可以根据版本号进行选择（这里的SNAPSHOT代表快照，一般表示这是一个处于开发中的项目，正式发布项目一般只带版本号）\nproperties：这里一般都是一些变量和选项的配置，我们这里指定了JDK的源代码和编译版本为17，同时下面的源代码编码格式为UTF-8，无需进行修改。\n四、Maven依赖导入\u0026lt;dependencies\u0026gt; //里面填写的就是所有的依赖 \u0026lt;/dependencies\u0026gt;举例说明（以插入Lombok依赖为例）：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.projectlombok\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lombok\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.18.36\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;五、Maven依赖作用域除了三个基本的属性用于定位坐标外，依赖还可以添加以下属性：\ntype：依赖的类型，对于项目坐标定义的packaging。大部分情况下，该元素不必声明，其默认值为jar\nscope：依赖的范围\noptional：标记依赖是否可选\nexclusions：用来排除传递性依赖（一个项目有可能依赖于其他项目，就像我们的项目，如果别人要用我们的项目作为依赖，那么就需要一起下载我们项目的依赖，如Lombok）\n1. type作用：定义依赖的类型，对应项目的打包类型（packaging）。\n默认值：jar。\n使用场景：当依赖的类型不是 jar 时，例如 war（Web 应用）、pom（父项目定义）、zip 等，需要显式声明。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;example-artifact\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;war\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt;2. scope（重点）作用：定义依赖的作用范围。主要范围包括：\ncompile（默认值）：编译、测试和运行时都需要。\nprovided：编译和测试时需要，但运行时由容器（如 Tomcat）提供。\nruntime：运行和测试时需要，编译时不需要。\ntest：仅测试时需要，编译和运行时不需要。\nsystem：类似于 provided，但需要本地提供依赖路径（几乎不用）。\nimport：用于引入 BOM（Bill of Materials）依赖管理。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.0.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt;3. optional作用：标记依赖是否为可选依赖，避免被传递到依赖的消费者（下游项目）。\n默认值：false。\n使用场景：当依赖是可选功能模块，不想影响下游项目时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;optional-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;4. exclusions作用：用于排除传递性依赖，防止不必要的依赖被引入。\n使用场景：当依赖的项目中包含的某些传递性依赖与你的项目冲突时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-tomcat\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;六、Maven安装、可选和排除Maven 安装问题导入：如何在其他项目中引入我们自己编写的Maven项目作为依赖使用呢？\n先创建一个用于测试的简单项目：\n\u0026lt;?xml version=\u0026ldquo;1.0\u0026rdquo; encoding=\u0026ldquo;UTF-8\u0026rdquo;?\u0026gt; \u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;TestMaven\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0-SNAPSHOT\u0026amp;lt;/version\u0026amp;gt; ... \u0026lt;/project\u0026gt;public class TestUtils { public static void test() { System.out.println(\u0026ldquo;家人们谁懂啊，蒸虾头，怎么会有人想吃我家鸽鸽下的蛋\u0026rdquo;); } }接着我们点击右上角的Maven选项，然后执行install或直接在命令行中输入mvn install来安装我们自己的项目到本地Maven仓库中。\n接着我们就可以在需要使用此项目作为依赖的其他项目中使用它了，只需要填写和这边一样的坐标：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.test\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;TestMaven\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;接着我们就可以在项目中直接使用了：\npublic static void main(String[] args) { TestUtils.test(); }Maven 中的可选依赖（Optional）作用\nMaven 的可选依赖属性 optional 用于标记某个依赖是否是可选的。\n默认行为：Maven 会传递所有依赖的依赖（传递性依赖），即如果项目 A 依赖项目 B，而项目 B 又依赖项目 C，则项目 A 会默认获取 B 和 C。\n使用 optional 可以避免这种传递性。\n场景\n当某个依赖是额外的功能模块，而下游项目可能不需要它时。\n避免引入无用的依赖，减少构建时间和冲突风险。\n示例\n项目 A 依赖项目 B，项目 B 依赖项目 C：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;项目 B 中声明 C 为可选依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;结果：项目 A 不会自动获取 C，除非手动添加。\nMaven 中的依赖排除（Exclusions）作用\n排除传递性依赖中不需要的模块。\n为什么需要？\n防止依赖冲突（例如不同版本的库）。\n删除多余的依赖以优化项目构建。\n示例\n项目 A 依赖项目 B，而项目 B 又依赖项目 C，但项目 A 不需要 C。\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;传递性依赖的层级示例\n项目 A -\u0026gt; 项目 B -\u0026gt; 项目 C。\n如果不需要项目 C，可通过 exclusions 将其排除，最终 A 只包含 B。\n常见问题解决\n依赖冲突：当传递性依赖中某个库的多个版本引入冲突时，排除旧版本并手动引入所需版本。\n避免不必要的依赖：减少构建体积，避免不需要的模块。\nMaven继承和多模块\n\u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;BookManage\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0\u0026amp;lt;/version\u0026amp;gt; \u0026amp;lt;properties\u0026amp;gt; \u0026amp;lt;maven.compiler.source\u0026amp;gt;17\u0026amp;lt;/maven.compiler.source\u0026amp;gt; \u0026amp;lt;maven.compiler.target\u0026amp;gt;17\u0026amp;lt;/maven.compiler.target\u0026amp;gt; \u0026amp;lt;project.build.sourceEncoding\u0026amp;gt;UTF-8\u0026amp;lt;/project.build.sourceEncoding\u0026amp;gt; \u0026amp;lt;/properties\u0026amp;gt; \u0026lt;/project\u0026gt;pom配置解释：project:：根节点。\nmodelVersion:：定义了当前模型的版本，不用管它。\ngroupId、artifactId、version:：这三个元素合在一起，用于唯一区别每个项目，别人如果需要将我们编写的代码作为依赖，那么就必须通过这三个元素来定位我们的项目，我们称为一个项目的基本坐标，所有的项目一般都有自己的Maven坐标，因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了，无需再下载Jar文件，而是Maven自动帮助我们下载依赖并导入。\ngroupId 一般用于指定组名称，命名规则一般和包名一致，比如我们这里使用的是org.example，一个组下面可以有很多个项目。\nartifactId 一般用于指定项目在当前组中的唯一名称，也就是说在组中用于区分于其他项目的标记。\nversion 代表项目版本，随着我们项目的开发和改进，版本号也会不断更新，我们可以手动指定当前项目的版本号，其他人使用我们的项目作为依赖时，也可以根据版本号进行选择（这里的SNAPSHOT代表快照，一般表示这是一个处于开发中的项目，正式发布项目一般只带版本号）\nproperties：这里一般都是一些变量和选项的配置，我们这里指定了JDK的源代码和编译版本为17，同时下面的源代码编码格式为UTF-8，无需进行修改。\n四、Maven依赖导入\u0026lt;dependencies\u0026gt; //里面填写的就是所有的依赖 \u0026lt;/dependencies\u0026gt;举例说明（以插入Lombok依赖为例）：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.projectlombok\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lombok\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.18.36\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;五、Maven依赖作用域除了三个基本的属性用于定位坐标外，依赖还可以添加以下属性：\ntype：依赖的类型，对于项目坐标定义的packaging。大部分情况下，该元素不必声明，其默认值为jar\nscope：依赖的范围\noptional：标记依赖是否可选\nexclusions：用来排除传递性依赖（一个项目有可能依赖于其他项目，就像我们的项目，如果别人要用我们的项目作为依赖，那么就需要一起下载我们项目的依赖，如Lombok）\n1. type作用：定义依赖的类型，对应项目的打包类型（packaging）。\n默认值：jar。\n使用场景：当依赖的类型不是 jar 时，例如 war（Web 应用）、pom（父项目定义）、zip 等，需要显式声明。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;example-artifact\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;war\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt;2. scope（重点）作用：定义依赖的作用范围。主要范围包括：\ncompile（默认值）：编译、测试和运行时都需要。\nprovided：编译和测试时需要，但运行时由容器（如 Tomcat）提供。\nruntime：运行和测试时需要，编译时不需要。\ntest：仅测试时需要，编译和运行时不需要。\nsystem：类似于 provided，但需要本地提供依赖路径（几乎不用）。\nimport：用于引入 BOM（Bill of Materials）依赖管理。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.0.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt;3. optional作用：标记依赖是否为可选依赖，避免被传递到依赖的消费者（下游项目）。\n默认值：false。\n使用场景：当依赖是可选功能模块，不想影响下游项目时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;optional-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;4. exclusions作用：用于排除传递性依赖，防止不必要的依赖被引入。\n使用场景：当依赖的项目中包含的某些传递性依赖与你的项目冲突时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-tomcat\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;六、Maven安装、可选和排除Maven 安装问题导入：如何在其他项目中引入我们自己编写的Maven项目作为依赖使用呢？\n先创建一个用于测试的简单项目：\n\u0026lt;?xml version=\u0026ldquo;1.0\u0026rdquo; encoding=\u0026ldquo;UTF-8\u0026rdquo;?\u0026gt; \u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;TestMaven\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0-SNAPSHOT\u0026amp;lt;/version\u0026amp;gt; ... \u0026lt;/project\u0026gt;public class TestUtils { public static void test() { System.out.println(\u0026ldquo;家人们谁懂啊，蒸虾头，怎么会有人想吃我家鸽鸽下的蛋\u0026rdquo;); } }接着我们点击右上角的Maven选项，然后执行install或直接在命令行中输入mvn install来安装我们自己的项目到本地Maven仓库中。\n接着我们就可以在需要使用此项目作为依赖的其他项目中使用它了，只需要填写和这边一样的坐标：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.test\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;TestMaven\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;接着我们就可以在项目中直接使用了：\npublic static void main(String[] args) { TestUtils.test(); }Maven 中的可选依赖（Optional）作用\nMaven 的可选依赖属性 optional 用于标记某个依赖是否是可选的。\n默认行为：Maven 会传递所有依赖的依赖（传递性依赖），即如果项目 A 依赖项目 B，而项目 B 又依赖项目 C，则项目 A 会默认获取 B 和 C。\n使用 optional 可以避免这种传递性。\n场景\n当某个依赖是额外的功能模块，而下游项目可能不需要它时。\n避免引入无用的依赖，减少构建时间和冲突风险。\n示例\n项目 A 依赖项目 B，项目 B 依赖项目 C：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;项目 B 中声明 C 为可选依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;结果：项目 A 不会自动获取 C，除非手动添加。\nMaven 中的依赖排除（Exclusions）作用\n排除传递性依赖中不需要的模块。\n为什么需要？\n防止依赖冲突（例如不同版本的库）。\n删除多余的依赖以优化项目构建。\n示例\n项目 A 依赖项目 B，而项目 B 又依赖项目 C，但项目 A 不需要 C。\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;传递性依赖的层级示例\n项目 A -\u0026gt; 项目 B -\u0026gt; 项目 C。\n如果不需要项目 C，可通过 exclusions 将其排除，最终 A 只包含 B。\n常见问题解决\n依赖冲突：当传递性依赖中某个库的多个版本引入冲突时，排除旧版本并手动引入所需版本。\n避免不必要的依赖：减少构建体积，避免不需要的模块。\n\u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;BookManage\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0\u0026amp;lt;/version\u0026amp;gt; \u0026amp;lt;properties\u0026amp;gt; \u0026amp;lt;maven.compiler.source\u0026amp;gt;17\u0026amp;lt;/maven.compiler.source\u0026amp;gt; \u0026amp;lt;maven.compiler.target\u0026amp;gt;17\u0026amp;lt;/maven.compiler.target\u0026amp;gt; \u0026amp;lt;project.build.sourceEncoding\u0026amp;gt;UTF-8\u0026amp;lt;/project.build.sourceEncoding\u0026amp;gt; \u0026amp;lt;/properties\u0026amp;gt; \u0026lt;/project\u0026gt;pom配置解释：project:：根节点。\nmodelVersion:：定义了当前模型的版本，不用管它。\ngroupId、artifactId、version:：这三个元素合在一起，用于唯一区别每个项目，别人如果需要将我们编写的代码作为依赖，那么就必须通过这三个元素来定位我们的项目，我们称为一个项目的基本坐标，所有的项目一般都有自己的Maven坐标，因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了，无需再下载Jar文件，而是Maven自动帮助我们下载依赖并导入。\ngroupId 一般用于指定组名称，命名规则一般和包名一致，比如我们这里使用的是org.example，一个组下面可以有很多个项目。\nartifactId 一般用于指定项目在当前组中的唯一名称，也就是说在组中用于区分于其他项目的标记。\nversion 代表项目版本，随着我们项目的开发和改进，版本号也会不断更新，我们可以手动指定当前项目的版本号，其他人使用我们的项目作为依赖时，也可以根据版本号进行选择（这里的SNAPSHOT代表快照，一般表示这是一个处于开发中的项目，正式发布项目一般只带版本号）\nproperties：这里一般都是一些变量和选项的配置，我们这里指定了JDK的源代码和编译版本为17，同时下面的源代码编码格式为UTF-8，无需进行修改。\n四、Maven依赖导入\u0026lt;dependencies\u0026gt; //里面填写的就是所有的依赖 \u0026lt;/dependencies\u0026gt;举例说明（以插入Lombok依赖为例）：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.projectlombok\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lombok\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.18.36\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;五、Maven依赖作用域除了三个基本的属性用于定位坐标外，依赖还可以添加以下属性：\ntype：依赖的类型，对于项目坐标定义的packaging。大部分情况下，该元素不必声明，其默认值为jar\nscope：依赖的范围\noptional：标记依赖是否可选\nexclusions：用来排除传递性依赖（一个项目有可能依赖于其他项目，就像我们的项目，如果别人要用我们的项目作为依赖，那么就需要一起下载我们项目的依赖，如Lombok）\n1. type作用：定义依赖的类型，对应项目的打包类型（packaging）。\n默认值：jar。\n使用场景：当依赖的类型不是 jar 时，例如 war（Web 应用）、pom（父项目定义）、zip 等，需要显式声明。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;example-artifact\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;war\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt;2. scope（重点）作用：定义依赖的作用范围。主要范围包括：\ncompile（默认值）：编译、测试和运行时都需要。\nprovided：编译和测试时需要，但运行时由容器（如 Tomcat）提供。\nruntime：运行和测试时需要，编译时不需要。\ntest：仅测试时需要，编译和运行时不需要。\nsystem：类似于 provided，但需要本地提供依赖路径（几乎不用）。\nimport：用于引入 BOM（Bill of Materials）依赖管理。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.0.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt;3. optional作用：标记依赖是否为可选依赖，避免被传递到依赖的消费者（下游项目）。\n默认值：false。\n使用场景：当依赖是可选功能模块，不想影响下游项目时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;optional-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;4. exclusions作用：用于排除传递性依赖，防止不必要的依赖被引入。\n使用场景：当依赖的项目中包含的某些传递性依赖与你的项目冲突时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-tomcat\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;六、Maven安装、可选和排除Maven 安装问题导入：如何在其他项目中引入我们自己编写的Maven项目作为依赖使用呢？\n先创建一个用于测试的简单项目：\n\u0026lt;?xml version=\u0026ldquo;1.0\u0026rdquo; encoding=\u0026ldquo;UTF-8\u0026rdquo;?\u0026gt; \u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;com.test\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;TestMaven\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0-SNAPSHOT\u0026amp;lt;/version\u0026amp;gt; ... \u0026lt;/project\u0026gt;public class TestUtils { public static void test() { System.out.println(\u0026ldquo;家人们谁懂啊，蒸虾头，怎么会有人想吃我家鸽鸽下的蛋\u0026rdquo;); } }接着我们点击右上角的Maven选项，然后执行install或直接在命令行中输入mvn install来安装我们自己的项目到本地Maven仓库中。\n接着我们就可以在需要使用此项目作为依赖的其他项目中使用它了，只需要填写和这边一样的坐标：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.test\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;TestMaven\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;接着我们就可以在项目中直接使用了：\npublic static void main(String[] args) { TestUtils.test(); }Maven 中的可选依赖（Optional）作用Maven 的可选依赖属性 optional 用于标记某个依赖是否是可选的。\n默认行为：Maven 会传递所有依赖的依赖（传递性依赖），即如果项目 A 依赖项目 B，而项目 B 又依赖项目 C，则项目 A 会默认获取 B 和 C。\n使用 optional 可以避免这种传递性。\n场景当某个依赖是额外的功能模块，而下游项目可能不需要它时。\n避免引入无用的依赖，减少构建时间和冲突风险。\n示例项目 A 依赖项目 B，项目 B 依赖项目 C：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;项目 B 中声明 C 为可选依赖：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;结果：项目 A 不会自动获取 C，除非手动添加。\nMaven 中的依赖排除（Exclusions）作用排除传递性依赖中不需要的模块。\n为什么需要？\n防止依赖冲突（例如不同版本的库）。\n删除多余的依赖以优化项目构建。\n示例项目 A 依赖项目 B，而项目 B 又依赖项目 C，但项目 A 不需要 C。\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-b\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;project-c\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;传递性依赖的层级示例项目 A -\u0026gt; 项目 B -\u0026gt; 项目 C。\n如果不需要项目 C，可通过 exclusions 将其排除，最终 A 只包含 B。\n常见问题解决依赖冲突：当传递性依赖中某个库的多个版本引入冲突时，排除旧版本并手动引入所需版本。\n避免不必要的依赖：减少构建体积，避免不需要的模块。\n\u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;qiuying\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;BookManage\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0\u0026amp;lt;/version\u0026amp;gt; \u0026amp;lt;properties\u0026amp;gt; \u0026amp;lt;maven.compiler.source\u0026amp;gt;17\u0026amp;lt;/maven.compiler.source\u0026amp;gt; \u0026amp;lt;maven.compiler.target\u0026amp;gt;17\u0026amp;lt;/maven.compiler.target\u0026amp;gt; \u0026amp;lt;project.build.sourceEncoding\u0026amp;gt;UTF-8\u0026amp;lt;/project.build.sourceEncoding\u0026amp;gt; \u0026amp;lt;/properties\u0026amp;gt; \u0026lt;/project\u0026gt;pom配置解释：project:：根节点。\nmodelVersion:：定义了当前模型的版本，不用管它。\ngroupId、artifactId、version:：这三个元素合在一起，用于唯一区别每个项目，别人如果需要将我们编写的代码作为依赖，那么就必须通过这三个元素来定位我们的项目，我们称为一个项目的基本坐标，所有的项目一般都有自己的Maven坐标，因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了，无需再下载Jar文件，而是Maven自动帮助我们下载依赖并导入。\ngroupId 一般用于指定组名称，命名规则一般和包名一致，比如我们这里使用的是org.example，一个组下面可以有很多个项目。\nartifactId 一般用于指定项目在当前组中的唯一名称，也就是说在组中用于区分于其他项目的标记。\nversion 代表项目版本，随着我们项目的开发和改进，版本号也会不断更新，我们可以手动指定当前项目的版本号，其他人使用我们的项目作为依赖时，也可以根据版本号进行选择（这里的SNAPSHOT代表快照，一般表示这是一个处于开发中的项目，正式发布项目一般只带版本号）\nproperties：这里一般都是一些变量和选项的配置，我们这里指定了JDK的源代码和编译版本为17，同时下面的源代码编码格式为UTF-8，无需进行修改。\n四、Maven依赖导入\u0026lt;dependencies\u0026gt; //里面填写的就是所有的依赖 \u0026lt;/dependencies\u0026gt;举例说明（以插入Lombok依赖为例）：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.projectlombok\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lombok\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.18.36\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;五、Maven依赖作用域除了三个基本的属性用于定位坐标外，依赖还可以添加以下属性：\ntype：依赖的类型，对于项目坐标定义的packaging。大部分情况下，该元素不必声明，其默认值为jar\nscope：依赖的范围\noptional：标记依赖是否可选\nexclusions：用来排除传递性依赖（一个项目有可能依赖于其他项目，就像我们的项目，如果别人要用我们的项目作为依赖，那么就需要一起下载我们项目的依赖，如Lombok）\n1. type作用：定义依赖的类型，对应项目的打包类型（packaging）。\n默认值：jar。\n使用场景：当依赖的类型不是 jar 时，例如 war（Web 应用）、pom（父项目定义）、zip 等，需要显式声明。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;example-artifact\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;war\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt;2. scope作用：定义依赖的作用范围。主要范围包括：\ncompile（默认值）：编译、测试和运行时都需要。\nprovided：编译和测试时需要，但运行时由容器（如 Tomcat）提供。\nruntime：运行和测试时需要，编译时不需要。\ntest：仅测试时需要，编译和运行时不需要。\nsystem：类似于 provided，但需要本地提供依赖路径（几乎不用）。\nimport：用于引入 BOM（Bill of Materials）依赖管理。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.0.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt;3. optional作用：标记依赖是否为可选依赖，避免被传递到依赖的消费者（下游项目）。\n默认值：false。\n使用场景：当依赖是可选功能模块，不想影响下游项目时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;optional-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;4. exclusions作用：用于排除传递性依赖，防止不必要的依赖被引入。\n使用场景：当依赖的项目中包含的某些传递性依赖与你的项目冲突时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-tomcat\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;\n\u0026lt;project xmlns=\u0026ldquo;http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\u0026ldquo;http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\u0026ldquo;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt;\n\u0026amp;lt;groupId\u0026amp;gt;qiuying\u0026amp;lt;/groupId\u0026amp;gt; \u0026amp;lt;artifactId\u0026amp;gt;BookManage\u0026amp;lt;/artifactId\u0026amp;gt; \u0026amp;lt;version\u0026amp;gt;1.0\u0026amp;lt;/version\u0026amp;gt; \u0026amp;lt;properties\u0026amp;gt; \u0026amp;lt;maven.compiler.source\u0026amp;gt;17\u0026amp;lt;/maven.compiler.source\u0026amp;gt; \u0026amp;lt;maven.compiler.target\u0026amp;gt;17\u0026amp;lt;/maven.compiler.target\u0026amp;gt; \u0026amp;lt;project.build.sourceEncoding\u0026amp;gt;UTF-8\u0026amp;lt;/project.build.sourceEncoding\u0026amp;gt; \u0026amp;lt;/properties\u0026amp;gt; \u0026lt;/project\u0026gt;pom配置解释：project:：根节点。\nmodelVersion:：定义了当前模型的版本，不用管它。\ngroupId、artifactId、version:：这三个元素合在一起，用于唯一区别每个项目，别人如果需要将我们编写的代码作为依赖，那么就必须通过这三个元素来定位我们的项目，我们称为一个项目的基本坐标，所有的项目一般都有自己的Maven坐标，因此我们通过Maven导入其他的依赖只需要填写这三个基本元素就可以了，无需再下载Jar文件，而是Maven自动帮助我们下载依赖并导入。\ngroupId 一般用于指定组名称，命名规则一般和包名一致，比如我们这里使用的是org.example，一个组下面可以有很多个项目。\nartifactId 一般用于指定项目在当前组中的唯一名称，也就是说在组中用于区分于其他项目的标记。\nversion 代表项目版本，随着我们项目的开发和改进，版本号也会不断更新，我们可以手动指定当前项目的版本号，其他人使用我们的项目作为依赖时，也可以根据版本号进行选择（这里的SNAPSHOT代表快照，一般表示这是一个处于开发中的项目，正式发布项目一般只带版本号）\nproperties：这里一般都是一些变量和选项的配置，我们这里指定了JDK的源代码和编译版本为17，同时下面的源代码编码格式为UTF-8，无需进行修改。\nMaven依赖导入\u0026lt;dependencies\u0026gt; //里面填写的就是所有的依赖 \u0026lt;/dependencies\u0026gt;举例说明（以插入Lombok依赖为例）：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.projectlombok\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;lombok\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.18.36\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;Maven依赖作用域除了三个基本的属性用于定位坐标外，依赖还可以添加以下属性：\ntype：依赖的类型，对于项目坐标定义的packaging。大部分情况下，该元素不必声明，其默认值为jar\nscope：依赖的范围\noptional：标记依赖是否可选\nexclusions：用来排除传递性依赖（一个项目有可能依赖于其他项目，就像我们的项目，如果别人要用我们的项目作为依赖，那么就需要一起下载我们项目的依赖，如Lombok）\n1. type作用：定义依赖的类型，对应项目的打包类型（packaging）。\n默认值：jar。\n使用场景：当依赖的类型不是 jar 时，例如 war（Web 应用）、pom（父项目定义）、zip 等，需要显式声明。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;example-artifact\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;war\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt;2. scope作用：定义依赖的作用范围。主要范围包括：\ncompile（默认值）：编译、测试和运行时都需要。\nprovided：编译和测试时需要，但运行时由容器（如 Tomcat）提供。\nruntime：运行和测试时需要，编译时不需要。\ntest：仅测试时需要，编译和运行时不需要。\nsystem：类似于 provided，但需要本地提供依赖路径（几乎不用）。\nimport：用于引入 BOM（Bill of Materials）依赖管理。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;4.0.1\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt;3. optional作用：标记依赖是否为可选依赖，避免被传递到依赖的消费者（下游项目）。\n默认值：false。\n使用场景：当依赖是可选功能模块，不想影响下游项目时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.example\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;optional-lib\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.0\u0026lt;/version\u0026gt; \u0026lt;optional\u0026gt;true\u0026lt;/optional\u0026gt; \u0026lt;/dependency\u0026gt;4. exclusions作用：用于排除传递性依赖，防止不必要的依赖被引入。\n使用场景：当依赖的项目中包含的某些传递性依赖与你的项目冲突时。\n示例：\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.5.0\u0026lt;/version\u0026gt; \u0026lt;exclusions\u0026gt; \u0026lt;exclusion\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-tomcat\u0026lt;/artifactId\u0026gt; \u0026lt;/exclusion\u0026gt; \u0026lt;/exclusions\u0026gt; \u0026lt;/dependency\u0026gt;\n","date":"2024-11-22T16:44:00Z","image":"https://qiuyingtyan.top/upload/123125006_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/maven/","title":"Maven的入门和使用"},{"content":"什么是Lambda表达式？Lambda 表达式是 Java 中的一种简洁语法，用于表示匿名函数。\n它是 Java 8 中引入的功能，旨在提高代码的简洁性和可读性。\n尤其是在处理函数式编程或需要传递行为的场景下，例如集合操作、并行流处理等。\n我们为什么要学习或使用Lambda表达式？代码更简洁：减少了匿名类的样板代码。\n更具可读性：将行为直接传递给函数。\n结合 Stream API 提高数据处理的效率和易读性。\n什么时候可以用Lambda表达式？如果一个接口中有且只有一个待实现的抽象方法，那么我们可以将匿名内部类简写为Lambda表达式\npublic static void main(String[] args) { Study study = () -\u0026gt; System.out.println(\"我是逆蝶！\"); study.study(); }注意事项Lambda 表达式只能用于函数式接口（接口中有且只有一个抽象方法）。\n如果需要使用外部变量，它必须是隐式 final（不能在 Lambda 表达式内外修改其值）。\nLambda 表达式的语法基本语法结构为：\n(参数列表) -\u0026gt; {方法体}构成部分参数列表：可以有零个、一个或多个参数，参数类型可以省略（由编译器推断）。\n箭头符号 (-\u0026gt;)：分隔参数列表和方法体。\n方法体：包含 Lambda 表达式的逻辑，可用一条或多条语句。\n或者可以这么表示：\n([参数类型 参数名称,]...) ‐\u0026gt; { 代码语句，包括返回值 }和匿名内部类不同，Lambda仅支持接口，不支持抽象类\n接口内部必须有且仅有一个抽象方法（可以有多个方法，但是必须保证其他方法有默认实现，必须留一个抽象方法出来）\n示例1. 无参数的 Lambda 表达式() -\u0026gt; System.out.println(\"Hello Lambda!\");2. 有一个参数的 Lambda 表达式x -\u0026gt; System.out.println(x);3. 有多个参数的 Lambda 表达式(a, b) -\u0026gt; a + b4. 有多条语句的方法体(x, y) -\u0026gt; { int sum = x + y; return sum; }\n没看懂？那让我们举一个不那么抽象的例子：\n@FunctionalInterface //添加了此注解的接口，都支持lambda表达式，符合函数式接口定义 public interface Runnable { public abstract void run(); //有且仅有一个抽象方法，此方法返回值为void，且没有参数 }因此，Runable的的匿名内部类实现，就可以简写为：\nRunnable runnable = () -\u0026gt; { };我们也可以写一个玩玩：\n@FunctionalInterface public interface Test { //接口类型 String test(Integer i); //只有这一个抽象方法，且接受一个int类型参数，返回一个String类型结果 }它的Lambda表达式的实现就可以写为：\nTest test = (Integer i) -\u0026gt; { return i+\"\"; }; //这里我们就简单将i转换为字符串形式\n","date":"2024-11-22T15:59:00Z","image":"https://qiuyingtyan.top/upload/97622912_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/lambda/","title":"Lambda表达式"},{"content":"注册域名什么是域名？\n域名可以通俗地理解为互联网上的“门牌号码”或“地址”。每个网站在互联网上都有一个唯一的地址，这个地址就是域名。比如，人们常常访问的“baidu.com”，“taobao.com”等都是域名。它们由一串字符组成，通常包括字母、数字和连接符“-”，并由“.”分隔成几部分。域名的作用是让人们能够更容易地访问和记住网站，而不需要记住复杂的IP地址。同时，域名也具有一定的商业价值，可以作为企业或个人在互联网上的标识和品牌形象。\n域名注册\n博主域名是在阿里云购买的\n官网：阿里云\n阿里云-计算，为了无法计算的价值\n首先注册账户，然后输入你想购买的域名\n域名备案然后到域名控制台进行实名认证和解析域名就行。\n如果你用的内地服务器那还需要进行域名备案。\n备案的流程就不细说，自己去了解一下就行。\n购买云服务器准备\n首先需要一台云服务器，要有公网IP的，推荐雨云的：\n雨云优惠注册地址：\nhttps://www.rainyun.com/114514aa_\n优惠码：114514aa\n使用优惠码注册后绑定微信可获得5折优惠券\n注册完账号后进到雨云控制台，云服务器入口可以在后台的 总览 和 云产品 部分找到：\n点击购买云服务器，接着选择服务器区域，国内用户建议选择内地机房，因为延迟低网络稳定，比如宿迁和十堰，其中推荐十堰，因为IP便宜，如果预算充足选择宿迁更好（防御高，BGP线路）。但如果你没有备案域名那建议选择香港或美国的服务器。\n这里我买了HK的服务器（因为懒得备案）\n配置选择2核2G一般够用了，如果你网站用户多就选高点的配置，也可以后期升级配置。\n系统选Debian11就行，预安装APP一个都别选（因为可能会有些不太好的事情发生，别问，问就说是发生了些不友好的经历...）\n都选好后就可以点击立即购买了，也可以选择1元试用1天。\n购买后即可在我的云服务器这里看到你买的云服务器，点击管理。\n安装1Panel面板首先需要通过SSH客户端连接服务器\n下载安装并打开ssh客户端软件，ssh客户端软件推荐putty或mobaxterm，还有用win自带的终端或CMD也行\n安装1Panel\ncurl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh \u0026amp;\u0026amp; bash quick_start.sh安装成功后，控制台会打印面板访问信息，可通过浏览器访问 1Panel：\nhttp://目标服务器 IP 地址:目标端口/安全入口如果使用的是云服务器，请至安全组开放目标端口。\nssh 登录 1Panel 服务器后，执行 1pctl user-info 命令可获取安全入口（entrance）\n安装成功后，可使用 1pctl 命令行工具来维护 1Panel\n记得记住账号密码噢\n然后浏览器输入地址登录进去就行惹\n接着安装应用\n使用 1Panel 部署 | Halo 文档\n1Panel 简介​\n1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。\n功能​\n快速建站：深度集成 WordPress 和 Halo，域名绑定、SSL 证书配置等一键搞定。\n高效管理：通过 Web 端轻松管理 Linux 服务器，包括应用管理、主机监控、文件管理、数据库管理、容器管理等。\n安全可靠：最小漏洞暴露面，提供防火墙和安全审计等功能。\n一键备份：支持一键备份和恢复，备份数据云端存储，永不丢失。\n安装基础软件​\n在安装 Halo 之前，我们需要先在 1Panel 上安装好所需的软件，包括 OpenResty 和数据库（MySQL、PostgreSQL、MariaDB 都可以）。在接下来的文档中，我们会默认你已经安装好了这两个软件，并不再赘述。\n安装 Halo 应用​进入应用商店应用列表，选择其中的 Halo 应用进行安装。\n在应用详情页选择最新的 Halo 版本进行安装。\n参数说明：\n名称：要创建的 Halo 应用的名称。\n版本：选择最新的版本即可。\n数据库服务：Halo 应用使用的数据库应用，支持下拉选择已安装的数据库应用，1Panel 会自动配置 Halo 使用该数据库。\n数据库名：Halo 应用使用的数据库名称，1Panel 会在选中的数据库中自动创建这个数据库。\n数据库用户：Halo 应用使用的数据库用户名，1Panel 会在选中的数据库中自动创建这个用户，并添加对应的数据库授权。\n数据库用户密码：Halo 应用使用的数据库用户密码，1Panel 会在选中的数据库中自动为上一步创建的用户配置该密码。\n外部访问地址：Halo 应用的最终访问地址，如果有为 Halo 规划域名，需要配置为域名格式，例如 http://halo.example.com。否则配置为 http://服务器IP:PORT，例如 http://192.168.1.1:8090。\n端口：Halo 应用的服务端口。\n开始安装后页面自动跳转到已安装应用列表，等待刚刚安装的 Halo 应用变为已启动状态。\n创建网站​完成 Halo 应用的安装后，此时并不会自动创建一个网站，我们需要手动创建一个网站，然后将 Halo 应用绑定到这个网站上才能使用域名访问。\n点击 1Panel 菜单的 网站，进入网站列表页，点击 创建网站 按钮。\n在已装应用中选择我们刚刚新建的 Halo 应用。\n正确填写主域名，需要注意的是需要提前解析好域名到服务器 IP。\n最后，点击确认按钮，等待网站创建完成即可访问网站进行 初始化。\n这样我们就部署完成了\n","date":"2024-11-13T06:41:06.130719379Z","image":"https://qiuyingtyan.top/upload/88278917_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/memory_blog/","title":"一些回忆——搭建该博客的流程"},{"content":"前提条件\nJava环境：确保安装了jdk(博主用的jdk11.0.23)\n数据库：MySQL(博主用的MySQL8.0.26)\nRedis：在若依项目中通常用作缓存和消息队列，提升数据访问速度和处理能力(博主用的redis0.5.3)\nNginx：作为反向代理服务器，可以处理请求负载均衡、静态资源服务和 HTTPS 加密，确保高效的流量管理和更好的性能。两者结合使用，可以极大提升应用的响应速度和可靠性(博主用的nginx1.14.1)\n具体操作Java安装jdk第一种方法：个人建议去官网上下载jdk的jar包\n访问官方网站：前往 Oracle JDK 下载页面 或 OpenJDK 下载页面。\n选择版本：选择你需要的JDK版本（记得选Linux x64哦）。\n下载包：找到对应的JAR包或安装包（通常是.tar.gz或.zip格式），点击下载。\n解压缩（如需要）：下载完成后，如果是压缩包，可以使用以下命令解压：\ntar -zxvf jdk-\u0026lt;version\u0026gt;.tar.gz把下载好的jar包直接拖进你的home文件夹里\n第二种方法：直接通过命令行下载\n通过包管理器安装\nsudo dnf install java-11-openjdk-devel确认安装：\njava -version\nMySQL安装MySQLsudo dnf install @mysql启动MySQL服务sudo systemctl start mysqld设置MySQL服务sudo systemctl enable mysqld运行安全配置sudo mysql_secure_installation登录MySQL并创建若依项目需要的数据库和用户mysql -u root -p修改 root 的密码为 123456ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';FLUSH PRIVILEGES;修改 root 可以远程登录update user set host = '%' where user = 'root';FLUSH PRIVILEGES;在MySQL提示符下执行CREATE DATABASE ruoyi;CREATE USER 'ruoyiuser'@'localhost' IDENTIFIED BY 'your_password';GRANT ALL PRIVILEGES ON ruoyi.* TO 'ruoyiuser'@'localhost';FLUSH PRIVILEGES;EXIT;修改 mysql 的配置文件sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf修改如下配置bind-address=0.0.0.0重启 mysql 服务service mysql restart使用如下命令查看 MySQL 服务状态service mysql status\nRedis安装 Redissudo dnf install epel-release -ysudo dnf install redis -y启动 Redissudo systemctl start redis设置 Redissudo systemctl enable redis验证 Redis 是否正常运行：redis-cli ping返回 PONG 就是正常运行啦！\nNginx安装 Nginxsudo dnf install nginx -y启动 Nginxsudo systemctl start nginx设置 Nginxsudo systemctl enable nginx\n部署若依项目ps：博主自己从阿里的源下载失败了，所以这里就推荐用jar包\n具体步骤：\n进入若依的官网，然后复制箭头所指的地址\n打开你的IDEA，\"File\"→\"Close Project\"，关闭现有的项目\n\u0026nbsp;然后新建\"Project\"→\"Get from VCS\"→在\"URL那行粘贴下载地址\n创建项目后，更改主库数据源\n准备数据库表\n先创建一个数据库，这里我们创建一个名为 ruoyi 的数据库。\n将项目所需的表创建。\n依次将两个 sql 文件中的内容拷贝到 navicat 中，在 sql 中添加使用当前数据库的语句：\n然后执行整个 sql。\n两个 sql 文件全部执行之后，会生成这些表：\n配置数据库编辑 application-dev.yml 或 application.yml，配置数据库连接：\nspring: datasource: url: jdbc:mysql://localhost:3306/ruoyi?useUnicode=true\u0026amp;characterEncoding=utf8\u0026amp;serverTimezone=UTC username: ruoyiuser password: your_password先运行一遍确保没有报错，然后打开右侧的\"maven\"→\"ruoyi\"→\"Lifecycle\"→\"package\"，然后等待打包完成，就可以在左侧的\"ruoyi-admin\"→\"target\" 看到\"ruoyi-admin.jar\"了。\n然后右键\"ruoyi-admin.jar\"→\"Open in\"→\"Explorer\",你就看到若依的jar包啦\n新建部署目录mkdir -p /opt/deploy/ruoyi/{api,web}把你的若依jar包放进api里\n启动若依项目在 target 目录下运行：\njava -jar ruoyi-admin.jar此时后端已经准备完毕，我们接下来准备前端\n将ruoyi-vue里的ruoyi-ui打开，然后\n1、安装依赖包，运行 RUOYI-VUE/bin/package.bat\n2、打包构建，运行 RUOYI-VUE/bin/build.bat\n（前提：已安装nodejs和yarn）\n然后把这web文件放这里边\n/opt/deploy/ruoyi/web\n配置Nginx创建新的配置文件：\nsudo vi /etc/nginx/conf.d/ruoyi.conf添加以下内容：\nserver { listen 80; server_name your_domain_or_ip; location / { proxy_pass http://localhost:8080; # 若依默认端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后Esc → ： → wq\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E9%87%8D%E5%90%AF-nginx\u0026quot;\u0026gt;重启 Nginx\u0026lt;/h3\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;sudo systemctl restart nginx\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E6%9C%80%E7%BB%88%E6%95%88%E6%9E%9C\u0026quot;\u0026gt;最终效果\u0026lt;/h3\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;最后，打开浏览器，访问 http://your_domain_or_ip，就可以看到若依的登录界面了。\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%89%8D%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;前端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%90%8E%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;后端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt; datasource: url: jdbc:mysql://localhost:3306/ruoyi?useUnicode=true\u0026amp;amp;characterEncoding=utf8\u0026amp;amp;serverTimezone=UTC username: ruoyiuser password: your_password\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;先运行一遍确保没有报错，然后打开右侧的\u0026quot;maven\u0026quot;→\u0026quot;ruoyi\u0026quot;→\u0026quot;Lifecycle\u0026quot;→\u0026quot;package\u0026quot;，然后等待打包完成，就可以在左侧的\u0026quot;ruoyi-admin\u0026quot;→\u0026quot;target\u0026quot; 看到\u0026quot;ruoyi-admin.jar\u0026quot;了。\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E6%89%93%E5%8C%85%E8%8B%A5%E4%BE%9D.webp\u0026quot; alt=\u0026quot;打包若依.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后右键\u0026quot;ruoyi-admin.jar\u0026quot;→\u0026quot;Open in\u0026quot;→\u0026quot;Explorer\u0026quot;,你就看到若依的jar包啦\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E6%96%B0%E5%BB%BA%E9%83%A8%E7%BD%B2%E7%9B%AE%E5%BD%95\u0026quot;\u0026gt;新建部署目录\u0026lt;/h3\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;mkdir -p /opt/deploy/ruoyi/{api,web}\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;把你的若依jar包放进api里\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E5%90%AF%E5%8A%A8%E8%8B%A5%E4%BE%9D%E9%A1%B9%E7%9B%AE\u0026quot;\u0026gt;启动若依项目\u0026lt;/h3\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;在 target 目录下运行：\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;java -jar ruoyi-admin.jar\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;此时后端已经准备完毕，我们接下来准备前端\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;将ruoyi-vue里的ruoyi-ui打开，然后\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;1、安装依赖包，运行 RUOYI-VUE/bin/package.bat\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;2、打包构建，运行 RUOYI-VUE/bin/build.bat\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;（前提：已安装nodejs和yarn）\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E9%83%A8%E7%BD%B2%E5%89%8D%E7%AB%AF01.webp\u0026quot; alt=\u0026quot;部署前端01.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后把这web文件放这里边\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;/opt/deploy/ruoyi/web\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E9%83%A8%E7%BD%B2%E5%89%8D%E7%AB%AF02.webp\u0026quot; alt=\u0026quot;部署前端02.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E9%85%8D%E7%BD%AEnginx\u0026quot;\u0026gt;配置Nginx\u0026lt;/h3\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;创建新的配置文件：\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;sudo vi /etc/nginx/conf.d/ruoyi.conf\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;添加以下内容：\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;server { listen 80; server_name your_domain_or_ip; location / { proxy_pass http://localhost:8080; # 若依默认端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后Esc → ： → wq\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E9%87%8D%E5%90%AF-nginx\u0026quot;\u0026gt;重启 Nginx\u0026lt;/h3\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;sudo systemctl restart nginx\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E6%9C%80%E7%BB%88%E6%95%88%E6%9E%9C\u0026quot;\u0026gt;最终效果\u0026lt;/h3\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;最后，打开浏览器，访问 http://your_domain_or_ip，就可以看到若依的登录界面了。\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%89%8D%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;前端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%90%8E%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;后端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt; datasource: url: jdbc:mysql://localhost:3306/ruoyi?useUnicode=true\u0026amp;amp;characterEncoding=utf8\u0026amp;amp;serverTimezone=UTC username: ruoyiuser password: your_password\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;先运行一遍确保没有报错，然后打开右侧的\u0026quot;maven\u0026quot;→\u0026quot;ruoyi\u0026quot;→\u0026quot;Lifecycle\u0026quot;→\u0026quot;package\u0026quot;，然后等待打包完成，就可以在左侧的\u0026quot;ruoyi-admin\u0026quot;→\u0026quot;target\u0026quot; 看到\u0026quot;ruoyi-admin.jar\u0026quot;了。\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E6%89%93%E5%8C%85%E8%8B%A5%E4%BE%9D.webp\u0026quot; alt=\u0026quot;打包若依.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后右键\u0026quot;ruoyi-admin.jar\u0026quot;→\u0026quot;Open in\u0026quot;→\u0026quot;Explorer\u0026quot;,你就看到若依的jar包啦\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E6%96%B0%E5%BB%BA%E9%83%A8%E7%BD%B2%E7%9B%AE%E5%BD%95\u0026quot;\u0026gt;新建部署目录\u0026lt;/h3\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;mkdir -p /opt/deploy/ruoyi/{api,web}\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;把你的若依jar包放进api里\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E5%90%AF%E5%8A%A8%E8%8B%A5%E4%BE%9D%E9%A1%B9%E7%9B%AE\u0026quot;\u0026gt;启动若依项目\u0026lt;/h3\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;在 target 目录下运行：\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;java -jar ruoyi-admin.jar\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span fontsize=\u0026quot;\u0026quot; color=\u0026quot;\u0026quot;\u0026gt;此时后端已经准备完毕，我们接下来准备前端\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;将ruoyi-vue里的ruoyi-ui打开，然后\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;1、安装依赖包，运行 RUOYI-VUE/bin/package.bat\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;2、打包构建，运行 RUOYI-VUE/bin/build.bat\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span fontsize=\u0026quot;\u0026quot; color=\u0026quot;\u0026quot;\u0026gt;（前提：已安装nodejs和yarn）\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E9%83%A8%E7%BD%B2%E5%89%8D%E7%AB%AF01.webp\u0026quot; alt=\u0026quot;部署前端01.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后把这web文件放这里边\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;/opt/deploy/ruoyi/web\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E9%83%A8%E7%BD%B2%E5%89%8D%E7%AB%AF02.webp\u0026quot; alt=\u0026quot;部署前端02.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E9%85%8D%E7%BD%AEnginx\u0026quot;\u0026gt;\u0026lt;span fontsize=\u0026quot;\u0026quot; color=\u0026quot;\u0026quot;\u0026gt;配置Nginx\u0026lt;/span\u0026gt;\u0026lt;/h3\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span fontsize=\u0026quot;\u0026quot; color=\u0026quot;\u0026quot;\u0026gt;创建新的配置文件：\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;sudo vi /etc/nginx/conf.d/ruoyi.conf\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span fontsize=\u0026quot;\u0026quot; color=\u0026quot;\u0026quot;\u0026gt;添加以下内容：\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;server { listen 80; server_name your_domain_or_ip; location / { proxy_pass http://localhost:8080; # 若依默认端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span fontsize=\u0026quot;\u0026quot; color=\u0026quot;\u0026quot;\u0026gt;然后Esc → ： → wq\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E9%87%8D%E5%90%AF-nginx\u0026quot;\u0026gt;\u0026lt;span fontsize=\u0026quot;\u0026quot; color=\u0026quot;\u0026quot;\u0026gt;重启 Nginx\u0026lt;/span\u0026gt;\u0026lt;/h3\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code class=\u0026quot;language-Shell\u0026quot;\u0026gt;sudo systemctl restart nginx\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;h3 style=\u0026quot;\u0026quot; id=\u0026quot;%E6%9C%80%E7%BB%88%E6%95%88%E6%9E%9C\u0026quot;\u0026gt;最终效果\u0026lt;/h3\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;最后，打开浏览器，访问 http://your_domain_or_ip，就可以看到若依的登录界面了。\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%89%8D%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;前端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%90%8E%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;后端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt; datasource: url: jdbc:mysql://localhost:3306/ruoyi?useUnicode=true\u0026amp;amp;characterEncoding=utf8\u0026amp;amp;serverTimezone=UTC username: ruoyiuser password: your_password\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;先运行一遍确保没有报错，然后打开右侧的\u0026quot;maven\u0026quot;→\u0026quot;ruoyi\u0026quot;→\u0026quot;Lifecycle\u0026quot;→\u0026quot;package\u0026quot;，然后等待打包完成，就可以在左侧的\u0026quot;ruoyi-admin\u0026quot;→\u0026quot;target\u0026quot; 看到\u0026quot;ruoyi-admin.jar\u0026quot;了。\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E6%89%93%E5%8C%85%E8%8B%A5%E4%BE%9D.webp\u0026quot; alt=\u0026quot;打包若依.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后右键\u0026quot;ruoyi-admin.jar\u0026quot;→\u0026quot;Open in\u0026quot;→\u0026quot;Explorer\u0026quot;,你就看到若依的jar包啦\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;新建部署目录\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;mkdir -p /opt/deploy/ruoyi/{api,web}\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;把你的若依jar包放进api里\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;启动若依项目：\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;在 target 目录下运行：\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;java -jar ruoyi-admin.jar\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span style=\u0026quot;font-size: 11ptpx\u0026quot;\u0026gt;此时后端已经准备完毕，我们接下来准备前端\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;将ruoyi-vue里的ruoyi-ui打开，然后\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;1、安装依赖包，运行 RUOYI-VUE/bin/package.bat\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;2、打包构建，运行 RUOYI-VUE/bin/build.bat\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span style=\u0026quot;font-size: 11ptpx\u0026quot;\u0026gt;（前提：已安装nodejs和yarn）\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E9%83%A8%E7%BD%B2%E5%89%8D%E7%AB%AF01.webp\u0026quot; alt=\u0026quot;部署前端01.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;然后把这web文件放这里边\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;/opt/deploy/ruoyi/web\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E9%83%A8%E7%BD%B2%E5%89%8D%E7%AB%AF02.webp\u0026quot; alt=\u0026quot;部署前端02.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span style=\u0026quot;font-size: 11ptpx\u0026quot;\u0026gt;配置Nginx\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span style=\u0026quot;font-size: 11ptpx\u0026quot;\u0026gt;创建新的配置文件：\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;sudo vi /etc/nginx/conf.d/ruoyi.conf\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span style=\u0026quot;font-size: 11ptpx\u0026quot;\u0026gt;添加以下内容：\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;server { listen 80; server_name your_domain_or_ip; location / { proxy_pass http://localhost:8080; # 若依默认端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span style=\u0026quot;font-size: 11ptpx\u0026quot;\u0026gt;然后Esc → ： → wq\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;span style=\u0026quot;font-size: 11ptpx\u0026quot;\u0026gt;重启 Nginx：\u0026lt;/span\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;pre\u0026gt;\u0026lt;code\u0026gt;sudo systemctl restart nginx\u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;最后，打开浏览器，访问 http://your_domain_or_ip，就可以看到若依的登录界面了。\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%89%8D%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;前端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt;\u0026lt;p style=\u0026quot;\u0026quot;\u0026gt;\u0026lt;img src=\u0026quot;/upload/%E5%90%8E%E7%AB%AF%E6%88%90%E5%8A%9F.webp\u0026quot; alt=\u0026quot;后端成功.png\u0026quot; width=\u0026quot;100%\u0026quot; height=\u0026quot;100%\u0026quot; style=\u0026quot;display: inline-block\u0026quot;\u0026gt;\u0026lt;/p\u0026gt; ","date":"2024-11-13T00:54:13.089050643Z","image":"https://qiuyingtyan.top/upload/87762152_p0_master1200.webp","permalink":"https://qiuyingtyan.top/p/ruoyi_bushu/","title":"关于如何在CentOS8上部署若依项目的思考"}]