ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

后端补全栈:三个月学习路线与AI模型部署实战

后端补全栈:三个月学习路线与AI模型部署实战 这两年只要打开技术社区“全栈开发”“ai全栈”“java全栈学习路线”这几个词就像约好了一样反复刷屏。我看过不少路线图也点过无数篇大纲帖结果是一个收藏夹从入门到吃灰。这次我不打算再收藏了直接把自己从后端补齐全栈的过程写成一本“全栈学习日记”从这篇开篇写起。这篇文章主要交代三件事我现在处在什么位置、未来三个月打算按什么路线补全栈、第一个全栈项目怎么做。顺手把“边缘AI部署”这条进阶支线也讲清楚就是大家最近常说的 yolov11 接入 Web 应用那一套。如果你是后端程序员想补前端或者还在纠结全栈路线怎么定这篇应该能给你一个相对完整、能落地的参照系。哪怕完全零基础跟着后面几篇日记一步步来也能慢慢摸到门道。1. 我理解的全栈不是“全会”而是“闭环”1.1 先说说我对“全栈”的真实感受在开始定路线之前我想先讲清楚一个事现在大家搜“全栈开发”这个词大部分人其实不是想“什么都会”而是想“一个人也能把一件事从头做到尾做成”。这个词被培训机构用滥了好像全栈就是前端、后端、数据库、运维、算法全都精通这根本不符合现实。我自己在传统行业写了好几年后端接口Spring Boot 用得算顺手数据库设计也能扛得住但一提到前端页面就头大。早期我花过两个周末死磕 Vue看完语法又忘了生命周期拿着组件文档不知道从哪下手。后来我慢慢想明白一个道理全栈不是把所有技术栈学完再干活而是干一个具体项目时把缺的那几块知识补齐端到端地跑通。所谓全栈开发落到实际工作中就是一条能力链路用户界面、业务逻辑、数据存储、部署上线加上现在的 AI 模型接入全都能自己连起来。连不起来的地方才是学习日记里最该记的东西。1.2 用开店类比理解全栈的本质我经常跟同事说做全栈开发很像开一家小餐馆。你既要是后厨负责炒菜也就是后端业务逻辑又要是前台负责点单上菜也就是前端界面还得当采购管理食材库存对应数据库和缓存偶尔收银系统坏了也得自己动手修那就是部署运维。一个人如果能把这家店从进货、做菜到上桌完整跑通哪怕每道菜不是顶级大厨水平也完全有能力撑起一个小生意了。这个类比特别适合做“全栈”的目标定位。你不需要在每一个环节都做到专家级别前端有偏科、后端有短板这都正常。但你必须清楚每个环节之间是怎么衔接的接口怎么约定、数据怎么流转、部署在哪台机器上跑。干项目时遇到不会的环节能靠文档和搜索快速补上来这就已经远超“只会写接口”或者“只会切页面”的单一角色。1.3 全栈角色能力拆分表那具体要补哪些能力我自己整理了一张全栈能力地图正好用来做学习日记的检查清单。角色板块核心职责常见技术栈我现在的水平后端开发业务逻辑、接口设计、数据建模Java、Spring Boot、MyBatis、MySQL、Redis熟练属于舒适区前端开发用户界面、交互状态、接口调用Vue 3、React、Vite、Element Plus能看代码独立写还费劲数据库与缓存表结构设计、索引优化、数据一致性MySQL、Redis、Flyway熟练需要巩固分布式场景工程化与部署打包、环境变量、容器编排、反向代理Docker、Docker Compose、Nginx用过但不够体系化AI 工具链模型调用、推理服务、前后端联动ONNX、FastAPI、yolov11刚接触是最想突破的支线软技能需求拆解、进度管理、问题排查文档、看板、复盘模板日常在用这次打算固化下来这张表我打印出来贴在工位旁边。以后每篇学习日记的结尾都会对照这张表打个勾看自己到底补了哪一块能力。2. 学习路线设计一条主线、两条辅线2.1 为什么起点要放在“后端补前端”上学习路线网上到处都是但大多数是“Java 从零到架构师”那种超长清单看到一半就失去动力。我这次刻意采用“一条主线、两条辅线”的结构主线不贪多全程围绕 Java 后端全栈走辅线是前端工程化和 AI 模型部署。我不打算从 HTML 标签重新学起因为那是浪费之前的经验。后端程序员补全栈最大优势是已经懂接口、懂数据、懂业务逻辑缺的只是“怎么把接口的数据用好看的页面展示出来”以及“怎么把整套东西部署到服务器上”。所以主线的第一步是把 Spring Boot 接口能力重新梳理一遍确保有一个能稳定运行的项目作为实验基地。主线周期我定在三个月以内目标是产出三个可演示的小项目。不是三个月就精通而是三个月能独立做出一个有登录、有增删改查、有页面、能部署的全栈应用。这个目标足够具体学起来不会散。2.2 主线Java 后端全栈的骨架这条主线不是我原创的但顺序踩过很多坑之后值得再强调一遍先夯实 Java 基础和 Spring Boot 核心再学数据访问和缓存最后做接口设计和认证授权。很多人一上来就啃微服务、分布式、消息队列结果连 Spring 的注入都说不清楚这是最典型的弯路。我给自己定的学习单元是这样切的学习单元核心内容验证成果单元 ASpring Boot 3 基础、MVC 分层、依赖注入写一个带 REST 接口的员工管理模块单元 BMySQL 表设计、MyBatis-Plus 使用、事务管理给员工模块加上部门表和级联查询单元 CRedis 缓存接入、JWT 登录与权限拦截把登录状态改成 token 校验单元 D前端 Vue 3 基础、组件通信、axios 封装用表格页面展示员工列表并支持搜索单元 EDocker Compose 部署、Nginx 反向代理把整套应用部署到一台云服务器每个单元结束都必须有可运行的东西绝不把“看了个视频”当成学会。我学习日记里每一篇都会记录这个单元里踩的坑比如 MyBatis-Plus 的字段映射问题、Redis 序列化方式不一致问题这些细节写下来才是真正的资产。2.3 辅线一前端的选型到底学 Vue 还是 React所有后端转全栈的人都要面对一个灵魂拷问Vue 还是 React我的答案是看团队和环境。如果之后想找传统企业信息化项目Vue 在国内的生态、中文文档和组件库更友好如果想去互联网产品团队React 的岗位量更大。两条路都不错最怕的是今天学 Vue 明天换 React最后哪个都不熟。我选择 Vue 3 加 Element Plus原因很简单上手曲线平缓模板语法直观组件库开箱即用。后端思维的人写模板引擎出身看 Vue 的单文件组件会特别有亲切感。我用了一段时间之后发现Vue 的响应式原理其实是很多业务页面最需要的那层包装不用手动操作 DOM数据变页面就跟着变效率体感非常明显。不过我也要提醒一句别把前端只当成“写页面”。组件拆分、状态管理、路由守卫、接口错误处理这些才是全栈里“全”字的含金量。我日记里的前端部分不会停留在静态页面而是从登录态管理开始让前端真正参与业务闭环。2.4 辅线二AI 全栈这条支线为什么值得提前铺最近“ai全栈”这个词热度很高我之前不理解直到自己试着把一个图像识别模型集成到 Web 项目里才明白AI 全栈不是让你从零训练模型而是让你把现成的模型包装成产品能力。比如别人已经把 YOLOv11 训练好了能识别几十类物体但你得让它接受图片上传、返回检测结果、前端把框画出来这本身就是一天完整的全栈链路。所以我的第二条辅线是“模型部署与推理服务化”。主线学到一半时我会用一个周五下午专门研究 YOLOv11 的 ONNX 导出、FastAPI 接口封装和前端 Canvas 画框。这个支线对后端开发来说没有想象中难核心是把张量维度搞清楚把 NMS 逻辑接对。这条辅线也是我写“全栈项目”时最能拿得出手的差异化内容。3. 开篇项目实操从零搭一个任务进度看板3.1 项目选题与需求拆解第一篇文章就得配第一个项目否则“学习日记”就成了空谈。我选的第一个全栈项目是“团队任务进度看板”名字听着普通但它几乎覆盖了全栈的经典问题需要登录、需要用户角色、需要任务的增删改查、需要上传附件、需要看板状态流转、需要按人员和时间筛选。为什么选它因为这是我工作中每天都要用的东西需求我完全清楚不用凭空想象。一个自己天天用的功能写起来才有真实的动力。我拆解出的核心功能有四块登录注册、任务管理、状态看板、统计图表。第一版不追求美观也不做花哨的拖拽交互先把流程跑通。数据库设计也很简单三张主表和一张关联表。用户表放用户名和密码摘要任务表放标题、描述、负责人、优先级、状态和截止日期项目表放项目名和描述用户与项目做多对多关联。这个体量对新手完全友好但对全栈链路已经足够丰满。3.2 技术栈选型与目录结构规划这个项目我用的是 Spring Boot 3.2 MyBatis-Plus MySQL 8 Redis 7 Vue 3 Vite Element Plus Docker Compose。所有组件都用当前稳定版本不用尝鲜版本避免踩到不熟悉的坑。前后端分离是必须的目录结构从一开始就分离清楚。前端目录叫frontend后端叫backend数据库脚本和部署配置各占一个目录。很多人喜欢把前后端塞在一个仓库里乱放后面打包部署时全是眼泪。我这次直接在根目录放一个docker-compose.yml统一把 MySQL、Redis 和后端服务编排起来前端构建产物交给 Nginx 托管。前端初始化用 Vite 官方脚手架比 Webpack 简单太多。执行npm create vitelatest frontend -- --template vue-ts创建 TS 版本然后手动装 Element Plus、Axios、Vue Router 和 Pinia。后端用 Spring Initializr 生成包名一律用com.luckybug这样在学习日记里提起来比较统一。3.3 核心代码实现与关键参数解释先说后端统一返回结构。以前我写接口都是各返回各的前端处理起来特别乱。这次先定义了一个R类型把所有接口都包成“状态码 消息 数据”三段式前端一个拦截器就能全局判断。public record RT(int code, String msg, T data) { public static T RT ok(T data) { return new R(0, ok, data); } public static T RT fail(int code, String msg) { return new R(code, msg, null); } }登录认证用的 JWT。流程是这样的用户提交用户名密码后端校验通过后生成一个 token过期时间设成 24 小时前端每次请求在请求头里带上Authorization: Bearer xxx。后端用拦截器统一解析 token解析失败直接返回 401。public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token null || !JwtUtil.verify(token.replace(Bearer , ))) { throw new BizException(401, 登录状态已过期请重新登录); } return true; } }Redis 在这里的职责是缓存用户信息和任务列表热点数据。我给任务查询接口加了一个缓存注解设置过期时间 30 分钟。这里有一个细节Redis 的 key 必须包含用户维度否则不同用户会看到别人的缓存数据。这些问题只有实际写的时候才会意识到看教程永远踩不到。前端部分我用 axios 实例封装了一个request工具统一从后端拿返回结构里的data字段。响应拦截器里判断状态码不是 0 就直接弹错误消息遇到 401 就清空登录态并跳转登录页。const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.response.use( (resp) { const body resp.data if (body.code ! 0) { ElMessage.error(body.msg) return Promise.reject(new Error(body.msg)) } return body.data }, (err) { if (err.response?.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(err.response?.data?.msg || 网络异常) return Promise.reject(err) } )3.4 联调、部署时最容易踩的五个坑我这次项目是边写边记录坑提前列几个大概率会遇到的问题给正在复现的人排雷。第一个是跨域问题前后端分离开发时前端跑在 5173 端口后端跑在 8080 端口必须用 Vite 代理解决而不是在后端暴力开启 CORS。我在vite.config.ts里配了proxy把/api转发到http://localhost:8080这个方案最干净。第二个是 JWT 密钥太短的问题。早期图省事写了一个很短的密钥结果启动直接报错后来才知道 HS256 要求密钥至少 32 字节。我换成了 32 位以上的随机字符串并放到环境变量里避免硬编码。第三个坑是 MyBatis-Plus 更新时间不生效。我给实体加了updateTime字段但每次更新都没动静排查半天才发现少了自动填充处理器需要实现MetaObjectHandler接口并在更新时手动填充。还有两个部署层面的坑。Docker Compose 里 MySQL 首次启动时初始化脚本执行顺序不确定解决办法是挂载/docker-entrypoint-initdb.d目录并保证 SQL 脚本名有序。另外后端容器如果连不上 MySQL大多是因为配置文件里的 host 写了localhost容器间通信必须写服务名mysql而不是localhost。问题现象直接原因解决办法前端请求 401拦截器要求 token但登录接口没放行在 WebMvcConfig 里 addExcludePatterns 排除 /api/auth/login更新记录时间不变缺少 MetaObjectHandler 自动填充实现自动填充处理器容器连不上数据库host 配错改成 docker-compose 里的服务名跨域报错前后端端口不一致Vite 配 proxy不靠后端 CORS图片上传失败请求头没带文件类型用 FormData 发请求别手动加 Content-Type4. 今年的进阶支线把 YOLOv11 接成全栈应用4.1 从“用模型”到“部署模型”的认知转变“脑机yolov11全栈实战”这个热词组合看起来挺科幻但拆开看其实是一条非常清晰的工程链路。脑机接口是数据源YOLOv11 是视觉模型全栈实战是产品化外壳。对绝大多数学习者来说不需要碰神经信号硬件而是可以把它理解成人机数据链的完整流程采集端、处理端、模型端、展示端。我们要掌握的是从模型到产品的最后一公里。这最后一公里的东西恰恰是全栈开发里最容易被忽略的。很多 AI 教程只教训练模型给出几张可视化图片就结束了。可现实是模型训练出来是放在 Jupyter Notebook 里的用户不可能用代码去调。你要做的是把模型封装成一个 HTTP 接口前端通过接口上传图片或视频帧后端完成推理再把坐标画回图片上。我找了一个 YOLOv11n 模型做试点模型很小只有几 MBCPU 也能跑。整个调用链路由想到通Python FastAPI 加载 ONNX 模型接收前端上传图片执行推理返回目标框坐标、类别和置信度前端在 Canvas 上绘制结果。4.2 模型推理服务化的核心步骤第一步把 YOLOv11 的 PyTorch 模型导出成 ONNX 格式。这一步是为了脱离 PyTorch 环境让部署更轻。我用的命令大致是yolo export modelyolov11n.pt formatonnx dynamicTrue导出后能得到一个通用的 ONNX 文件。注意动态尺寸和固定尺寸的选择我这里直接用 640x640 输入减少动态维度带来的复杂度。第二步写一个 FastAPI 服务加载模型。关键点是输入图片要经过 letterbox 处理把原始图片等比缩放到 640x640周围补灰边推理完再把坐标映射回原图尺寸。import cv2 import numpy as np import onnxruntime as ort sess ort.InferenceSession(yolov11n.onnx, providers[CPUExecutionProvider]) def preprocess(img): scale min(640 / img.shape[1], 640 / img.shape[0]) nw, nh int(img.shape[1] * scale), int(img.shape[0] * scale) resized cv2.resize(img, (nw, nh)) canvas np.full((640, 640, 3), 114, dtypenp.uint8) canvas[(640 - nh)//2 : (640 - nh)//2 nh, (640 - nw)//2 : (640 - nw)//2 nw] resized return canvas, scale, (640 - nw)//2, (640 - nh)//2 outputs sess.run(None, {sess.get_inputs()[0].name: blob})这里我踩过一个坑ONNX 输出的维度是[1, 84, 8400]84 表示 4 个坐标加 80 个类别概率8400 是不同尺度特征图的数量。第一次拿到这个输出时完全懵了后来才意识到需要先转置成[8400, 84]再做 NMS。这个维度转换如果不写清楚基本上每个人都要卡一晚上。第三步是 NMS 筛选。YOLOv11 原始输出会有大量重叠框必须按置信度过滤再按 IOU 阈值去掉重复框。我用的是 OpenCV 的cv2.dnn.NMSBoxes阈值设成置信度 0.25、IOU 0.45效果比较稳。这一步千万别省直接画所有框页面根本没法看。第四步把封装好的推理函数挂到 FastAPI 路由上。接口设计成POST /api/detect接收multipart/form-data的图片返回 JSON 数组每个元素包含x1/y1/x2/y2/class/confidence。前端拿到这些数据后在 Canvas 上画框。4.3 前端展示与性能调优的一些心得前端展示部分我用 Vue 写了一个简单的识别页面。用户上传图片后先通过本地URL.createObjectURL预览原图同时把图片文件发给后端拿到返回的检测坐标后利用 Canvas 在固定尺寸的画布上绘制矩形框和标签。这里有个细节要提醒图片上传组件预览图和 Canvas 画框图必须用同一张原图否则坐标会对不上。我第一版是先压缩再上传结果画框位置偏得离谱后来直接把原图传给后端自己只负责展示。后端返回的坐标本身已经映射回原图尺寸前端拿到后直接用 canvas 坐标即可。性能方面CPU 上跑一个 YOLOv11n单张图片推理大约 300 到 500 毫秒请求排队多了会变慢。我给前后端之间加了一个简单的“识别中”状态避免重复提交。如果后续要做实时视频流就得考虑用 GPU 或者把输入帧率降到 10 FPS但这不是开篇阶段需要解决的事。模型跑通的那一刻还是比较有成就感的。我前端页面弹出检测结果时能明显感觉到“全栈”这个边界被自己推宽了一块。以后遇到新的 AI 模型基本都可以沿用这套“封装接口、前端可视化”的思路。5. 学习日记怎么记才不会烂尾5.1 我采用的周记复盘模板写这条路最怕三分钟热度。我这次不打算靠意志力而是用一个能坚持的机制强制自己每周产出。我给自己定了每周一篇学习日记每篇固定结构本周目标、实际操作记录、遇到的问题、下周计划。字数不要求多但必须把“卡住的地方”写详细因为卡住的地方才是最值钱的复盘素材。模板我直接固化到仓库里每次都复制改。大致是这样的模块内容本周目标从路线图里选一个可交付成果例如完成用户登录与 JWT 拦截实际进展列出改了哪些代码、跑通了哪些流程踩坑记录记录报错信息、排查过程、最终的根因下周计划明确下一周的可交付成果状态标记用“顺利/卡住/放弃”三选一原因写一句话这套模板最大的作用是逼我面对“为什么没进展”。很多时候学不动不是懒而是目标太大。写日记时一旦把目标缩小到“今天完成登录接口”行动阻力立刻就小了。5.2 心态调整与常见问题速查学习过程中一定会遇到“学了就忘”。我的办法是每周至少有一个晚上把上一周项目里的关键代码重新看一遍把报错记录归类。不要追求记住所有 API只要记住“我做过这个”就行细节可以临时查文档。下面这张速查表是我给自己准备的以后卡住先看这个表再乱翻资料状态应对方法某框架版本升级带来的教程失效先查官方迁移文档再搜 GitHub issue最后才看第三方文章接口调通但前端数据不显示打开浏览器 Network 面板先看响应体再看控制台报错后端报 500 但日志没有明细开启 MyBatis 的 SQL 日志和异常堆栈打印学了一天感觉什么也没记住把当天改过的代码 diff 导出来逐行回顾组件库文档看不懂直接复制官方示例改字段看效果比硬读文档快不想写日记允许这一周只写三句话做了什么、卡在哪、下步做什么最后再分享一个我正在用的小技巧把学习日记和项目仓库绑定每一周的日记都对应一个可运行的提交记录。这样三个月后回头翻不再是“我好像学过”而是能直接看到某一天的提交信息写着“修复 token 刷新逻辑”。这些提交记录就是最好的进度条比任何计划表都真实。
返回列表