ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue+MySQL选课系统:源码解析与并发控制实践

SpringBoot+Vue+MySQL选课系统:源码解析与并发控制实践 每年选课季都能在教学楼走廊里听到类似的抱怨选课系统又卡了、想选的课一瞬间就满了、刷新一下课程列表直接白屏。我自己也经历过这种痛后来在课程设计里干脆自己做了一套选课系统用的是 SpringBoot 后端 Vue 前端 MySQL 数据库这套组合。最近把这套源码重新整理了一遍跑通整个流程后感慨挺多的这套东西看起来简单但里面的用户权限、选课并发控制、前后端联调每一个环节都有值得细说的坑。这篇就把从解读源码到成功运行的完整过程写下来给正在做课设、毕设或者想学全栈的同学做个参考。这套选课系统解决的是高校教务里最典型的需求学生在规定时间内选课、退课、查成绩教师维护课程并录入成绩管理员统一管理用户与课程数据。涉及三类角色、五个以上核心业务模块是一个非常标准的管理信息系统案例。不管是想快速交差拿一个能演示的项目还是想认真理解前后端分离开发的全流程这套 SpringBoot Vue MySQL 的源码都非常有参考价值。我尽量把每一步都拆开讲清楚包括数据库怎么设计、接口怎么对、遇到报错怎么排查保证你跟着做下来就能跑。1. 项目整体设计与技术选型思路1.1 为什么选择 SpringBoot Vue MySQL 这套技术组合先说选型。现在做一个管理信息系统网上能搜到的方案其实很多比如纯 JSP Servlet、Spring Boot Thymeleaf、或者 SpringBoot Vue 前后端分离。但这套源码选了 SpringBoot Vue MySQL属于目前校园项目里最主流、也最适合拿来学习的组合。SpringBoot 的核心优势是“开箱即用”。内嵌 Tomcat依赖用 Maven 管理配置集中在 application.yml 里不需要像 SSM 时代那样手写一大堆 XML。对学生来说最难的部分往往不是写业务代码而是搭建环境的过程SpringBoot 刚好把这块成本压到了最低。Vue 则是前端框架里上手门槛最低的一个组件化开发思路很直观配合 Element UI 之类的组件库两天时间就能拼出一个像模像样的管理后台界面。MySQL 更不用多说免费、轻量、资料多中小型系统的数据量完全够用而且大学数据库课程教的就是它。再有就是就业层面的考虑。打开招聘网站看后端岗位Java SpringBoot 是最常见的要求Vue 也是前端入门必会技能。做这个课设的过程本质上就是在提前演练真实项目开发的工作流简历上写“熟悉前后端分离开发模式”面试官问起来也有具体项目可以聊。技术不是越新越好而是越“合适”越好。这套组合胜在生态成熟、社区资料多踩坑时一搜就有答案——对一个课程设计项目来说这比什么都重要。1.2 系统核心模块与功能边界划分翻开源码先看目录结构会发现前后端完全分离后端只负责提供接口返回 JSON前端负责页面渲染和交互。这种架构下先理解功能模块的划分比看代码本身更重要。整套系统围绕三种角色来设计学生student、教师teacher、管理员admin。学生端能做的事情围绕“选课”这个动作展开。登录后可以浏览所有可选课程点击选课、退课查看自己当前学期已经选上的课程列表等教师录完成绩后还能看到自己的课程成绩。教师端主要负责开课和给分。教师登录后维护自己名下的课程信息查看每门课的选课学生名单录入和修改成绩。管理员端是权限最大的负责用户管理新建学生账号、教师账号、课程管理审核开课、调整容量、学期管理创建新的学期并设置当前学期还有选课时段的控制。边界划分的关键在于权限隔离。比如学生接口不接受教师账号调用教师接口不接受学生账号调用管理员可以做跨模块操作。这些在代码里通过两件事落地前端根据角色渲染不同菜单后端每个接口做权限校验。很多课程设计只做了前端菜单隐藏接口却不设防这是安全意识不足。从源码看这套系统后端也做了拦截校验这才是一个完整项目的正确姿势。另外还有一个容易被忽略的模块是学期管理。选课不是一个随时都能进行的动作通常教务处会规定一个时间段比如“9月1日到9月5日可以选课”。所以系统里有当前学期的概念非选课时间段即使学生点选课也会被拒绝。这个业务限制在设计上融入了接口逻辑分析需求时要留意。2. 核心功能拆解与数据库设计要点2.1 权限模型设计三类角色如何落地权限设计是这套系统的地基。代码里实现权限控制的思路大概是这样的用户表里有一个 role 字段值分别是 0、1、2对应管理员、教师、学生。登录成功后后端生成一个 Token 返回给前端前端把它存在本地每次请求接口时放到请求头里。后端通过拦截器读取 Token解析出当前用户是谁、什么角色然后再判断这个角色能不能访问目标接口。这个设计看起来直白但里面有几个关键细节值得注意。第一密码不能明文存储。源码里能看到密码经过加密后才入库的推荐的做法是 BCrypt 加密即使数据库被拖走攻击者也很难还原出真实密码。第二Token 要设置过期时间避免一个账号长时间有效。第三接口不仅要校验“是否登录”还要校验“有没有权限”两者缺一不可。前端这边的权限控制在 Vue Router 路由守卫里实现。用户登录后拿到角色信息前端根据角色动态生成可访问的菜单。学生看不到“用户管理”菜单教师看不到“选课”菜单。但这里必须强调一个原则前端隐藏菜单只是提升用户体验真正的安全防线在后端接口校验。因为任何人都可以通过浏览器控制台直接调用接口如果后端不加校验前面的功夫全白费。部分源码里有一个让我印象深刻的细节学生退课时接口校验了“这个课程的学生记录是不是当前登录用户本人的”。这就是典型的越权访问防御考虑得比很多课设工程都周到。作为课程设计这个点完全可以作为面试时讲的亮点点出来。2.2 选课核心流程与并发场景设计选课是整个系统里业务逻辑最核心的部分也是面试官最爱追问的地方。一个正常的选课流程是这样的学生从课程列表中选中一门课点击选课按钮后前端向后端发送选课请求。后端拿到请求后做四层校验一是当前时间是否在选课时间段之内二是这个课程是否存在且处于可选状态三是学生是否已经选过这门课防止重复选课四是课程当前已选人数是否达到容量上限。四层校验全部通过才真正写入选课记录并同步更新课程已选人数。难点在最后一层校验。假设一门课容量是 50 人只剩下最后 1 个名额此时有两个学生同时点击选课如果程序只是先查一下已选人数发现是49然后写入记录那两个人都会选课成功最后已选人数变成 51超选了。这个问题在真实选课系统里非常典型。源码给出了一个很聪明的优化方案不先查再写而是直接在更新语句里带上容量条件。用类似UPDATE course SET selected_count selected_count 1 WHERE id ? AND selected_count capacity这样的方式去更新课程数据如果受影响的行数是 0说明课程已满选课失败受影响行数是 1说明更新成功此时再插入选课记录。这两个操作放到一个事务里事务要么全成功、要么全失败不会出现“人数更新了但选课记录没插进去”的脏数据。这个方案没有用到复杂的分布式锁却能在并发场景下保证不超选很多生产环境里也在用类似思路。理解透这个点面试时基本不会被问倒。2.3 数据库表结构设计与关键字段说明数据库设计是这套系统里最值得学习的一部分。整个项目一共有四张核心表用户表、课程表、选课记录表、学期表。表与表之间通过外键逻辑关联不一定要在数据库层面真的建外键约束但业务上它们的关系非常明确。用户表里核心字段有id 主键、username 登录名、password 密码加密后的密文、real_name 真实姓名、role 角色标识、student_no 学号、teacher_no 工号。教师和学生两个身份都放在一张表里用 role 区分管理员则复用一条固定记录。这张表的设计简化了登录逻辑——登录时只需要查一次用户名和密码然后根据 role 决定跳转到哪个页面。课程表里有id、course_name 课程名、course_code 课程编号、teacher_id 任课教师 ID关联用户表、credit 学分、capacity 容量上限、selected_count 已选人数、class_time 上课时间、location 上课地点、term_id 所属学期 ID。其中 selected_count 这个字段是系统能用上一节提到的并发方案的关键——它相当于一个冗余列把实时人数直接挂在课程记录上避免每次查询都去数选课记录表。选课记录表是连接学生和课程的桥梁id、student_id 学生 ID、course_id 课程 ID、selected_time 选课时间、status 状态、score 成绩。这张表上建议加一个唯一约束 (student_id, course_id)从数据库层面保证同一个学生不能重复选同一门课。成绩字段初始为空教师录入后才有值。学期表则很轻量id、term_name 学期名称、is_current 是否当前学期、start_date 和 end_date 学期起止时间。每次开学管理员新建一条学期记录把旧学期的 is_current 改成 0新学期的改成 1所有课程和选课记录都挂在具体的学期 ID 下实现数据按学期隔离。建表时优先把逻辑主键和唯一约束设计好比后期加维护简单得多。有一个容易被忽视的坑字段名不要用 MySQL 保留字比如 order、group 这些词建表阶段就避开省得到时候写 SQL 报错。3. 实操过程从源码到跑通前后端3.1 环境准备与版本匹配建议拿到源码第一步不是急着打开代码而是先把运行环境准备好。这套系统我用的是 JDK 1.8、Maven 3.6、Node.js 14、MySQL 5.7 这个组合跑通的整体非常稳定。但不同同学电脑上装的版本可能不一样这里要给一个版本匹配建议SpringBoot 2.x 版本最适合搭配 JDK 8 或 JDK 11如果电脑装的是 JDK 17很多网上找的 SpringBoot 2.3 或者 2.4 老项目直接启动会报错建议把 JDK 降到 8或者换一套 SpringBoot 3.x 源码。SpringBoot 3.x 需要 JDK 17 起步这是一道硬门槛。Node.js 的版本也要注意。Vue CLI 4 支持 Node 8.9 到 16.xVue CLI 5 相对宽松如果用的是 Vite 构建的项目则要求 Node 14.18 以上。如果 npm install 时报了一堆关于 engines 的警告大概率就是 Node 版本不匹配直接用 nvm 切换版本最省事。MySQL 这边差别不大5.7 和 8.0 都能跑但连接驱动的写法有区别。5.7 用 com.mysql.jdbc.Driver8.0 用 com.mysql.cj.jdbc.Driver源码里如果用 8.0 驱动本地 MySQL 是 5.7 也能兼容反过来则不行。3.2 数据库初始化与配置文件修改数据库初始化是整个运行流程里的第一道关卡。源码包里一般会带一个 .sql 脚本文件里面包含了建库、建表和默认数据的 INSERT 语句。步骤很简单打开 MySQL 命令行或者 Navicat执行这个脚本数据库就建好了。执行成功后会看到 database 里出现选课系统的库里面有前面说的那几张表还能看到预设的管理员账号、测试学生和教师账号。这些默认账号和密码在文档里写明第一次登录验证系统是否正常时非常好用。脚本执行完真正需要动手改的地方是后端配置文件 application.yml。我习惯打开这个文件逐个字段检查server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/course_selection_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver这里要注意三个点一是数据库名 course_selection_system 要跟脚本里创建的库名一致二是数据库密码要改成自己本地的密码三是 URL 里尽量把 characterEncodingutf8 和 serverTimezoneAsia/Shanghai 这两个参数加上否则容易出现中文乱码和时区报错。MySQL 8 默认时区跟中国差八小时不加 serverTimezone 参数启动时可能会爆“The server time zone value”之类的错误。3.3 后端启动步骤从 IDEA 导入到端口占用排查后端服务的启动流程不复杂但第一次跑容易在环境上卡壳。正确步骤是打开 IDEA用 Import Project 方式选择源码里的后端文件夹等待 Maven 把依赖全部下载完。这一步是整个流程里最磨人的环节因为要下载大量 jar 包网络不好可能要等十几分钟。如果 Maven 仓库在国外源下载慢改到阿里云镜像能快很多。依赖下载完成后找到主类通常是一个带 SpringBootApplication 注解的类文件名形如 CourseSelectionApplication在 IDEA 里直接右键 Run 就能启动。启动日志里看到 “Started CourseSelectionApplication in xx seconds” 就说明后端已经起来了。最常见的启动失败原因是端口被占用。默认端口是 8080如果本机已经有其他程序占用启动日志会报 “Port 8080 was already in use”。处理方式有两种一是命令行执行 netstat -ano | findstr 8080 找到占用进程并结束二是改 application.yml 里的 server.port 换个端口比如 8081。改完端口后前端所有请求的地址也要跟着调整所以我个人更推荐第一种方式保持 8080 不动毕竟前端配置里写的大概率是 8080。3.4 前端启动步骤与依赖安装细节后端起来后接着处理前端。前端是一个标准的 Vue 项目进入前端根目录命令行依次执行两条命令npm install npm run serve第一条命令是安装项目依赖也就是根据 package.json 里列出的依赖清单把 node_modules 文件夹装好。这个过程也同样可能遇到慢的问题解决办法是给 npm 设置淘宝镜像npm config set registry https://registry.npmmirror.com设置完再跑 npm install速度会有质的飞跃。第二条命令是启动开发服务。默认端口通常是 8080但既然后端占了 8080Vue CLI 会自动往后顺延用 8081浏览器访问地址以启动日志里显示的为准。打开页面后如果能看到登录界面说明前端已经成功跑起来了。这里有一个前后端联调的经典问题浏览器访问的是 http://localhost:8081但后端接口在 http://localhost:8080两个端口不一样浏览器出于同源策略会拦截跨域请求。源码里前端一般会配一个代理来解决在 vue.config.js 里设置 devServer.proxy把前端所有 /api 开头的请求转发到 http://localhost:8080。这个代理配置是整个联调过程里的关键点没有它前端能打开但永远调不通接口。3.5 启动顺序与联调自检清单前后端都启动后还不能算大功告成至少要验证一遍核心流程。我习惯遵循一个固定的自检顺序省时省力先用管理员账号登录确认能进入后台管理页面能看到用户列表和课程列表。然后切换学生账号登录尝试选一门课再退掉这门课。最后用教师账号登录找到这门课确认选课记录里能看到刚才的数据录入一个成绩再用学生账号查看成绩是否显示。这几个动作覆盖了系统的主要业务链路只要环环通畅整个系统基本就是健康的。如果验证过程中有一步卡住先打开浏览器开发者工具F12看 Network 面板里请求的状态码和返回信息这是定位前后端问题的第一手资料。一个通用的判断逻辑是如果请求发出去了但返回 404检查接口路径是否正确返回 500问题大概率在后端看 IDEA 控制台的异常日志请求压根没发出去那就是前端路由或请求逻辑的问题。4. 常见问题与排查技巧实录4.1 后端启动报错速查表跑源码的过程里最耗时间的就是解决各种报错。我把常见的后端启动报错整理成一个速查表照着排查能省下大量时间报错信息原因分析解决方案Access denied for user rootlocalhost数据库用户名或密码不对检查 application.yml 里的账号密码是否与本地一致Communications link failure数据库没有启动、端口不对或时区问题确认 MySQL 服务已启动检查 URL 是否加了 serverTimezoneFailed to configure a DataSource数据源配置缺失或配置错误检查 spring.datasource 段配置是否完整Port 8080 was already in use8080 端口被其他进程占用结束占用进程或改用 8081 端口Invalid bound statement (not found)Mapper XML 文件没被扫描到检查 Mapper 接口与 XML 的 namespace、方法名是否一致java.lang.UnsupportedClassVersionErrorJDK 版本与项目编译版本不匹配给项目设置对应的 JDK 版本或升级/降级 JDK这里的每个问题我都在实际运行过程中踩过尤其是数据库连接的那几个几乎每次换一台电脑重新部署都会碰到。解决思路就一条先把报错堆栈里第一行看清楚它指向哪里就查哪里不要一上来就怀疑代码写错了大概率还是配置问题。4.2 SpringBoot 版本过高与 JDK 版本冲突这个坑出现的频率非常高因为很多同学电脑上默认装的是最新版 JDK比如 JDK 17 或 JDK 21而网上下载的课设源码大多基于 SpringBoot 2.3 或 2.4 开发。SpringBoot 2.x 要求 JDK 8 或 11用 JDK 17 启动时可能会直接报错即使能启动也会出现各种诡异的反射异常和依赖问题。处理方式有一个优先级判断如果只是想把项目跑起来优先降 JDK。在 IDEA 里下载一个 JDK 8注意是 1.8 版本然后在 Project Structure 里把 Project SDK 和 Modules 的 language level 都改成 8再把 pom.xml 里如果指定了 Java 版本也要一起改。如果是自己新写的项目准备配全套那就反过来直接用 SpringBoot 3.x JDK 17只要能找到配套版本的依赖问题也一样能解决。这里要说一个判断的小技巧看 pom.xml 里 spring-boot-starter-parent 的版本号。如果版本号以 2.x 开头建议 JDK 8 或 11以 3.x 开头就必须 JDK 17 以上。版本号本身一般不会有问题问题几乎都在环境不匹配上。4.3 MySQL 连接与中文乱码问题中文乱码是管理系统里特别容易出现的“老大难”问题。正常情况下一套配置到位是不该乱码的但如果没注意字符集就会出问题。我的建议是三层防御缺一不可第一层建库时指定字符集SQL 脚本里写成CREATE DATABASE 数据库名 DEFAULT CHARACTER SET utf8mb4;第二层后端连接 URL 里加上 useUnicodetruecharacterEncodingutf8第三层数据库连接工具里也统一设置为 utf8mb4。三层都做了中文基本不会乱。MySQL 8 还有一个身份认证的坑。MySQL 8 默认使用 caching_sha2_password 认证插件而某些版本的 JDBC 驱动不支持这种认证方式导致连接报错。解决办法是在数据库里执行一条命令把用户的认证方式改回 mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;这个操作本身很简单但排错时很难想到如果你 MySQL 8 遇到了连不上的情况优先试这个方法。4.4 Vue 启动与跨域问题排查前端的坑主要集中在 npm install、路由跳转和接口调用上。npm install 失败十有八九是网络问题换镜像源能解决 80%剩下一部分可能是 node-sass 这类依赖编译失败那是 Node 版本不匹配造成的用 nvm 切换 Node 版本或者把 node-sass 换成 sass 的 dart-sass 实现问题就消失了。跨域问题是前后端分离项目里最容易让新手懵圈的现象。具体表现是前端页面打开正常登录时提示网络错误或者 F12 控制台报 “CORS” 相关红字。解决方式有两种。一种是在后端接口类上统一加 CrossOrigin 注解允许跨域请求加完后用 CORS 跨域验证通过的接口来联调。另一种是在前端 vue.config.js 里配置代理通过 devServer 把请求转发给后端这样从浏览器角度看请求只发给了前端自己的地址不存在跨域。两种方案里我推荐代理方案因为它更接近线上部署形态。Nginx 反代的时候用的也是同样的思路。另外要注意修改了 vue.config.js 必须重启前端服务才会生效这个问题经常有人踩。5. 项目二次开发与扩展方向5.1 给这个系统新增业务模块的思路整套系统跑通以后如果时间充裕完全可以基于现有代码做一些扩展开发。最常见的扩展方向是消息通知模块当学生选课成功、退课成功或者教师录入成绩后系统自动生成一条站内通知。实现思路也不复杂建一张 message 表收到文字内容的字段、接收人 ID、是否已读在现有代码的选课成功逻辑后面加一段插入 message 记录的函数即可。这个功能加分点很明显而且代码量不大适合课设冲刺阶段加。另一个更容易出效果的方向是数据统计大屏。选课数据已经存在数据库里可以统计热门课程 top 10、各专业选课人数、课程容量利用率用 ECharts 图表展示在管理员首页。这个扩展只需要写一个统计接口返回聚合后的数据前端引入 ECharts 画图就行视觉效果非常好答辩时放出来很能撑场子。第三个值得做的扩展是成绩导出功能。教师录完成绩后可以一键导出当前课程的学生成绩为 Excel 文件。后端用 Apache POI 生成文件前端通过一个下载链接拿到文件。这个功能虽然小但在实际使用里非常频繁作为项目亮点写在简历上也是可以讲的。5.2 从课程设计到生产级系统的差距在哪里前面讲的都是功能层面但代码从“能跑”到“能扛住真实场景”中间还有一段不小的距离。能意识到这段差距本身就是学习收获。首先是并发能力。真实的选课场景高峰时可能几千人同时操作现有的数据库层面控制虽然能保证数据不错但会有性能瓶颈。生产级系统会用 Redis 做课程热度缓存把热门的课程信息提前放到缓存里用消息队列削峰把选课请求先放入队列后端按一定速率处理数据库做读写分离把查询压力从主库分离出去。其次是安全性。现在系统里已经做了密码加密、权限校验这些都是基础。生产环境还要考虑接口限流、防 SQL 注入的预编译、日志审计、以及更复杂的越权测试。安全是一个持续对抗的过程但基础一定要打牢。最后是部署方式。本地开发用 IDEA 启动、npm run serve 跑前端没问题上线则要把后端打成 jar 包、前端 build 出静态文件用 Nginx 托管再用 Docker 把各个环节容器化。如果能把项目部署到一台云服务器上在简历上写“熟悉 Linux 部署流程”会比只会在本地上跑更有说服力。5.3 这套系统在简历和面试中的正确打开方式最后聊点实际的。如果这套选课系统是你的课设或毕设项目它是完全可以写进简历的但怎么写、怎么聊有讲究。简历上的项目描述不要只写“开发了选课系统实现了选课退课功能”这种描述太弱了。更好的思路是用 STAR 法则拆解项目突出数据规模和核心难点。举一个例子“设计并实现了基于 SpringBoot Vue 前后端分离的选课系统系统支撑三种角色、六个业务模块负责选课核心流程开发通过数据库条件更新与事务机制解决了选课超卖的并发问题数据库层面设计了唯一索引防止重复选课并通过定期缓存策略优化课程列表查询性能”。这段话里没有一句虚的每一条都是实际做过的功能面试官一听就知道你有真实思考。面试官大概率会追着并发控制、权限设计这两块深挖。数据库条件更新怎么保证不超选事务的隔离级别设置了吗角色权限存在哪里前端隐藏菜单和后端接口校验的关系是什么选课提高了已选人数但插入失败怎么办这些问题如果做完项目后认真复盘回答起来不难。最怕的是代码跑通了但没深入想过为什么面试一追问就露怯。最后分享一些实际操作中的体会整套系统从拿到源码到彻底跑通我前前后后花了一个周末加几个晚上。最大的感受是这种 SpringBoot Vue MySQL 的经典管理类项目代码本身并不难难点几乎都集中在环境的适配和配置细节上。所以如果你第一次跑就卡住了不要急着怀疑代码有问题先认真看报错信息再对着配置检查一遍八成能解决。我个人的建议是不要只满足于“能跑”。这个项目里包含的知识密度其实很高数据库设计、权限模型、并发控制、前后端交互、事务机制、跨域处理每一个点展开都能说上一阵。把每个点吃透写进简历、拿下面试比单纯交一个课设作业有价值得多。选课系统这类管理信息系统永远是入行练手的好素材。等你吃透了这套代码再回头看任何一个后台管理系统都会有一种“套路我都熟”的感觉从学生到开发者的距离就这样一点点缩短了。
返回列表