☰
Spring Boot+Vue学生管理系统源码:从部署到二次开发全攻略
2026/10/28 8:46:45 网站建设 项目流程

简介:这是一份基于 Spring Boot 与 Vue 前后端分离架构的学生管理系统源码,适合正在学习 Java 后端、Vue 前端,或需要课程设计、毕业设计参考的开发者。后端以 Spring Boot 为主体,可通过入口类直接启动;前端按 Vue 工程组织,且已打包为静态资源放入后端 static 目录,即使不单独运行前端也能访问,便于快速查看整体效果。压缩包共 141 个文件、约 6.26 MB,包含 74 个 Java 源码、12 个 Vue 页面、8 个 JavaScript、9 个 XML 配置,以及 SQL 数据脚本、Maven 相关脚本、配置文件、说明文档等,覆盖后端接口、前端页面、构建脚本与基础配置,目录结构较完整。当前已有 390 人浏览学习。对想跑通学生信息管理常见功能、了解前后端分离项目打包部署逻辑的读者,这份体积小巧的源码包能提供直观的实践参考。

1. 学生管理系统源码这东西,真不是能直接跑起来就完事的

拿到“Springboot+vue学生管理系统源码”这个标题,大多数人的第一反应是“下下来、解压、跑起来、交作业”。但真正做过几个校园项目的人都知道,这套技术组合的源码,坑不在“跑不起来”,而在“跑起来之后你根本不知道怎么改”。Springboot负责后端接口和数据库交互,vue负责页面渲染和用户操作,两者靠JSON数据沟通,学生管理系统则把CRUD、选课、成绩、权限这些经典业务全塞进了一个工程里。适合谁?适合正在做课程设计、毕业设计,或者刚进公司被安排维护一个老旧前后端分离项目的开发者。这篇笔记的目标很直接:让你从拿到源码到能改需求、能部署、能排查问题,而不是对着黑匣子干瞪眼。

2. Springboot后端:先让服务起来,再谈看懂结构

2.1 从源码压缩包到本地启动的完整步骤

拿到源码后的第一件事不是看代码,是确认环境。大多数学生管理系统源码用的是Springboot 2.x系列,对应JDK 8或JDK 11,少数新一点的用了Springboot 3.x要配JDK 17。怎么判断?看pom.xml里spring-boot-starter-parent的版本号。老版本用<java.version>1.8</java.version>,新版本可能是17甚至21。环境不匹配是启动翻车的第一大原因,而且报错信息往往很误导,比如“org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'”,实际原因是JDK版本太新不兼容旧版Tomcat内嵌容器。

# 解压源码后,进入后端工程目录 cd student-management-backend # 如果你用的是Maven wrapper,直接这样启动 ./mvnw spring-boot:run # 如果是普通Maven项目 mvn clean install -DskipTests mvn spring-boot:run

这里有个坑,很多国产课程设计源码根本没带Maven Wrapper,需要你本地装Maven 3.6+。启动成功后看日志,看到“Started Application in x.xxx seconds”就说明后端起来了。默认端口一般是8080,如果被占用,看application.yml里的server.port配置。启动过程如果报数据库连接错误,先别慌,这说明服务本身没问题,是数据库还没接上。

2.2 数据库脚本到底跑没跑,这是后端能不能用的分水岭

学生管理系统最核心的数据表大概有这几张:学生表、教师表、课程表、选课表、成绩表、用户表。源码包里通常有个sql文件夹,里面放着初始化脚本。但“有脚本”和“脚本能跑”是两回事——很多老系统的脚本是MySQL 5.7时代的写法,包含ENGINE=InnoDB DEFAULT CHARSET=utf8mb4这种没问题,怕的是里面有utf8mb4_0900_ai_ci这种排序规则,MySQL 5.7根本不认,一执行就报错。

-- 先建库,字符集和排序规则要选对 CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_db; -- 执行源码自带的sql文件 source /path/to/your/sql/student_init.sql; -- 验证是否导入成功 SHOW TABLES;

执行完source之后如果显示一堆Query OK,说明表建好了。常见的翻车现象是执行到一半报错,表建了一部分但数据没导完。这种时候别重复执行整个脚本——有些表因为没有DROP TABLE IF EXISTS语句,会报“Table already exists”。务实的做法是进MySQL里把库删掉重建,然后再执行一遍。之后修改application.yml里的数据库连接信息:

spring: datasource: url: jdbc:mysql://localhost:3306/student_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver

这里有个老生常谈但必须提的配置:serverTimezone=Asia/Shanghai不写,连接MySQL 8.0以上版本会报时区错误。useSSL=false是因为本地开发没必要开SSL握手,性能也更好。改完配置重启后端,登录接口能返回token或用户信息,说明数据库这关过了。

2.3 后端源码的核心结构,摸清这几个包就够用了

Springboot项目的包结构看起来复杂,但学生管理系统的套路很固定,无非是controller、service、mapper/dao、entity/model、config这几个层。Controller层管HTTP接口,Service层写业务逻辑,Mapper层用MyBatis或MyBatis-Plus操作数据库,Entity层对应数据库表。

src/main/java/com/example/student/ ├── controller/ # 接收前端请求,返回JSON │ ├── StudentController.java │ ├── CourseController.java │ └── AuthController.java ├── service/ # 业务逻辑层 │ ├── impl/ │ └── StudentService.java ├── mapper/ # MyBatis接口,配XML或注解SQL │ ├── StudentMapper.java │ └── StudentMapper.xml ├── entity/ # 数据库实体类 │ ├── Student.java │ └── Course.java ├── config/ # 配置类,如跨域、拦截器、Swagger └── common/ # 统一返回结果、异常处理

看源码时别一行行读,先看StudentController和AuthController这两个类,把接口请求路径和参数类型记下来。前端vue页面里调的就是这些接口,理解前后端的契约关系是后面联调的基础。MyBatis-Plus的项目会更省事,BaseMapper里已经封装了增删改查,你不用看懂每个SQL是怎么写的,只要知道哪个Service方法对应哪个业务操作就够了。

3. Vue前端:安装依赖、跑起来、看懂页面怎么调接口

3.1 前端工程的两种形态,决定了你的启动方式完全不同

学生管理系统的Vue前端源码,在压缩包里可能以两种形态存在:一种是完整的Vue工程,有node_modules的package-lock.json和src目录;另一种是已经打包好的dist目录,只等放进Springboot的static文件夹里。前者需要你用Node.js环境去跑,后者直接拷贝就行。怎么区分?看根目录有没有package.json。有package.json就是开发态工程,没有的话就找dist文件夹。

# 进入前端工程目录(以Vue 2.x项目为例) cd student-management-frontend # 安装依赖,这一步最容易出问题,见下文排查 npm install --registry=https://registry.npmmirror.com # 启动开发服务器,默认端口8080,如果和后端冲突可以在vue.config.js里改 npm run serve

启动成功的标志是终端出现Compiled successfully,浏览器访问localhost:8080能看到登录页面。但这一步失败率极高。常见错误是Node Sass does not yet support your current environment,这是因为老项目用的是node-sass这个包,它对Node版本敏感,Node 16以上基本都会编译失败。解决办法是把node-sass替换成dart-sass,在package.json里改一行,或者在项目里安装sass包:

# 卸载node-sass并安装sass npm uninstall node-sass npm install sass@1.32.13 --save-dev --registry=https://registry.npmmirror.com

为什么要装这个特定版本?因为sass版本过高时,对/deep/这种旧写法会报错,Vue 2.x项目里深度选择器用的是/deep/,sass的高版本只认::v-deep。版本是玄学,但1.32.x是兼容Vue 2.x的稳妥选择。

3.2 记住vue项目的src目录长什么样,改需求才不会找半天

Vue前端源码的核心在src目录,通常包含views(页面)、components(组件)、router(路由配置)、store(Vuex状态管理)、api(接口封装)这几块。学生管理系统的页面不会太多——登录页、学生管理页、课程管理页、选课页、成绩页,最多再有个管理员后台。

// src/api/student.js —— 接口封装的核心文件 import request from '@/utils/request' // 获取学生列表 export function getStudentList(params) { return request({ url: '/api/student/list', method: 'get', params }) } // 新增学生 export function addStudent(data) { return request({ url: '/api/student/add', method: 'post', data }) } // 删除学生 export function deleteStudent(id) { return request({ url: `/api/student/delete/${id}`, method: 'delete' }) }

这个文件是前后端接口的“契约书”。如果后端接口路径改了或者参数变了,你直接改这里就行。很多新手拿到源码后,前端登录不了,第一反应是改页面代码,其实问题往往出在@/utils/request里的baseURL配置上。打开这个文件看一下:

// src/utils/request.js import axios from 'axios' const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API || 'http://localhost:8080', timeout: 5000 }) export default service

baseURL指向后端地址。如果你的后端跑在8080端口,这个配置没问题;如果后端改了端口,或者将来部署到服务器上,这里必须同步修改。还有一种情况是后端接口统一加了/api前缀,而baseURL里也写了/api,结果请求路径变成了/api/api/xxx,这种重复前缀的坑遇到过的人不少。

3.3 前端代理:开发环境下绕开跨域的常规打法

前端页面跑在8080(或8081),后端接口在8080,严格来说跨域了。课程设计级别通常用Vue CLI的devServer.proxy来搞定,比后端写CORS配置更常见、更灵活。

// vue.config.js module.exports = { devServer: { port: 8081, // 前端独立端口,避开后端8080 proxy: { '/api': { target: 'http://localhost:8080', // 后端接口地址 changeOrigin: true, pathRewrite: { '^/api': '/api' // 如果前后端路径一致,保持原样 } } } } }

配置好之后,前端所有/api开头的请求都会自动转发到后端的8080端口,前端开发时不用关心跨域问题。这里有个容易搞混的地方:pathRewrite的'^/api': '/api'看起来像废话,但如果你后端接口实际路径不含/api前缀,需要改成'^/api': '',把前缀剥掉再转发。搞不清的时候,浏览器按F12看Network面板,看看请求路径到底是/api/student/list还是/student/list,对比一下就知道该怎么写了。

4. 把Vue打包进Springboot:从前后端分离到“一个jar包搞定”

4.1 为什么要把dist目录放进Springboot,以及直接拷贝的坑

学习阶段的源码,前后端分离跑两个服务没问题。但是项目要部署、要展示、要交给老师验收的时候,维护两个进程很麻烦。常见的做法是npm run build生成dist目录,把里面的静态资源扔进Springboot的src/main/resources/static,这样启动后端一个jar包就能同时提供接口和页面。但直接拷贝会踩坑——dist目录里的index.html引用的JS、CSS路径写死为根路径/,如果你的Web服务根路径不是/,页面就会白屏。

# 在vue工程目录执行构建,生成dist目录 npm run build # 检查dist/index.html里的资源引用路径 cat dist/index.html # 如果看到src="/js/app.js"这种绝对路径,说明根路径模式 # 如果看到src="./js/app.js"这种相对路径,说明相对路径模式

如果资源路径是绝对路径,而打包后的Springboot服务部署在http://ip:8080/根路径下,那没问题。但如果你的后端配置了server.servlet.context-path=/student,访问地址变成http://ip:8080/student/,那绝对路径的/js/app.js会请求到http://ip:8080/js/app.js,404。

4.2 vue.config.js里的publicPath配置是打包的关键参数

正确的做法是在打包之前调整vue.config.js,把publicPath改成相对路径或者跟后端context-path匹配的路径。

// vue.config.js module.exports = { publicPath: './', // 使用相对路径,部署到任何子目录都不会白屏 outputDir: 'dist', assetsDir: 'static', // 静态资源放static子目录 productionSourceMap: false, // 不生成source map,减小体积并避免源码泄露 devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

publicPath: './'是课程设计里最稳妥的选项。相对路径下,Vue Router要用createWebHashHistory()模式而不是createWebHistory()模式,否则在浏览器里刷新子路由页面会404。这个坑非常经典:打包后部署,登录页正常,进入管理系统后按F5刷新,结果白屏报404。原因就是路由用的history模式,刷新时浏览器按URL路径去请求服务器资源,后端没有对应的路由处理。改法很简单:

// src/router/index.js import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const router = new VueRouter({ mode: 'hash', // 改成hash模式,刷新不再404 routes })

打包后把dist目录下的文件拷贝到Springboot的src/main/resources/static/目录下,重新mvn package,然后java -jar target/xxx.jar启动,浏览器访问http://localhost:8080就能直接看到系统页面。前后端从两个服务变成了一个服务,省心多了。

4.3 打包前后常见的前端报错排查

最常见的是net::ERR_CONNECTION_REFUSED,意思是前端页面上发请求时,请求打到的地方没有服务在跑。如果是开发环境,检查vue.config.js里的proxy target对不对;如果把前端静态资源放在Springboot里了,检查dist目录是不是最新构建的——很多时候改了后端代码或者接口路径,但忘了重新npm run build,页面调的还是旧的JS包。解决方式是重新构建,然后清空static目录再拷贝,别把旧文件留着,否则会因为缓存问题出现一些诡异的页面状态。

还有一个容易忽略的点:后端拦截器或过滤器拦截了静态资源请求。SpringSecurity或自定义拦截器经常把/index.html、/static/**给拦截了,导致页面加载出来了但没有样式。在拦截器的放行列表里加上静态资源路径即可。

// 后端拦截器配置中放行静态资源 registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/", "/index.html", "/static/**", "/js/**", "/css/**", "/img/**", "/api/login");

5. 学生管理系统源码避坑指南:启动、联调和二次开发的六个高频问题

5.1 Lombok没配置好,编译期报错找上门

启动后端时如果报一堆java: Cannot find symbol错误,比如symbol: method getUsername()或symbol: class Slf4j,十有八九是Lombok没生效。Lombok是学生管理系统源码里最常用的简化工具,用它来自动生成getter、setter和日志对象。但很多人只装了JDK,没在IDE里装Lombok插件并开启注解处理。解决办法:IDEA里安装Lombok插件,在Settings → Build → Compiler → Annotation Processors里勾选Enable annotation processing。血泪经验是,这个勾选框不打开,Lombok的注解就是黑匣子,一点用都没有。

5.2 MySQL连接配置正确但连不上,多半是驱动版本在捣乱

pom.xml里引用的MySQL驱动版本要和MySQL服务器版本匹配。MySQL 8.x用com.mysql.cj.jdbc.Driver对应8.0.x驱动,MySQL 5.7用com.mysql.jdbc.Driver对应5.1.x驱动。如果版本不匹配,启动时日志会出现Communications link failure或Public Key Retrieval is not allowed。后者很典型——MySQL 8.0默认认证插件是caching_sha2_password,旧驱动不支持。要么换成新驱动,要么在JDBC连接串上加allowPublicKeyRetrieval=true参数。直接用最新8.0.x驱动连接MySQL 5.7也能跑,Java代码里如果用了Class.forName("com.mysql.jdbc.Driver")这种老写法,记得改成com.mysql.cj.jdbc.Driver。

5.3 Vue安装依赖时卡死不动,网络和缓存哪个在背锅?

npm install卡在某个包上超过五分钟,常见原因是registry源太慢。国内开发者用默认的npm源拉取大包时非常折磨。另一个原因是npm缓存有损坏的二进制包,即使更换了registry源还是会重新下载损坏文件。综合解法是:

# 清理缓存 npm cache clean --force # 切换国内镜像源,直接写进npm配置 npm config set registry https://registry.npmmirror.com # 重新安装 npm install

node_modules删掉重装是常规操作,别怕慢。装完后如果还是报错,注意看报错信息里的具体包名——node-gyp或node-sass相关错误是最大的坑,按前文的方案换sass即可。另外,package-lock.json文件建议保留,它锁定了依赖版本,避免不同机器上装出不同版本导致行为不一致。

5.4 前端传参和后端接收总是对不上?先把类型和名字对齐

学生管理系统中,典型的联调问题是后端接口接收的字段名是stuName,前端传的是studentName,结果接口能调通但数据为空。这种问题最隐蔽,因为不报错。打开浏览器F12,看请求负载里的字段名,再对比后端实体类的属性名或@RequestParam注解的value值。另一个常见问题是前端传的是字符串"1",后端用@PathVariable Integer id接收时偶尔会类型转换失败。前端传递日期字段时,如果后端用@DateTimeFormat(pattern = "yyyy-MM-dd"),前端格式必须是2024-06-01而不能是2024/06/01。总之,先抓包看请求内容,再对比后端参数定义,多半是名字或格式不匹配,这个方法论比一切代码调试都好用。

5.5 系统能跑但删数据报外键错误,数据库设计的锅

学生选课、成绩这些表之间别忘了外键约束。很多源码设计时为了省事,只建了表没建外键关系。但如果你要关联删除,比如删除一门课程,成绩表里还有这门课的记录,就会报Cannot delete or update a parent row: a foreign key constraint fails。解决思路有两种:一是在数据库里加上外键并设置ON DELETE CASCADE,这样删课程时自动清掉相关成绩记录;二是在Service层手动先删子表数据再删主表数据。我一般推荐第二种——不用改数据库结构,逻辑更可控,也适合新手理解。选择哪种取决于要不要保留历史数据,如果成绩需要审计痕迹,那就得做逻辑删除,给数据表加is_deleted字段,而不是物理删除。

5.6 接口返回数据正常,但前端页面表格是空的?

这种“数据通但页面白屏”的问题,十有八九是字段名大小写匹配不上。后端JSON返回的字段是studentId,而前端表格列定义里写的是studentid或StudentID。JavaScript对象的属性访问是区分大小写的,所以页面拿不到值。解法是打开前端控制台,看network里的响应内容,检查字段名,然后同步修改前端表格的prop属性。另外,如果后端返回的字段名是下划线风格(如student_id),而前端用的是驼峰风格(如studentId),需要在后端配置开启驼峰映射:

mybatis-plus: configuration: map-underscore-to-camel-case: true

这是新手最容易在这套系统上卡住半天的原因之一,其实改动只要一行配置。

6. 二次开发进阶:不满足于跑通,把这几个改造做完才是自己的

好不容易把源码跑通了,别急着换下一个项目。学生管理系统这类CRUD源码,最有价值的不是它能跑,而是你能在它基础上做多少次改造。我的建议是,按下面几个方向依次去动刀。

第一件值得做的事,是把密码从明文改成加密存储。很多课程设计源码的user表里存的是明文密码,这是安全硬伤。用Spring Security的BCryptPasswordEncoder或Apache Commons Codec的DigestUtils.md5Hex都行,推荐BCrypt,同密码不同盐值,安全性更高。

// 在UserService的register方法里加上加密逻辑 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public boolean register(User user) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String encryptedPassword = encoder.encode(user.getPassword()); user.setPassword(encryptedPassword); return userMapper.insert(user) > 0; } // 登录时用encoder.matches(rawPassword, encodedPassword)校验

改完之后,记得把数据表里已有的旧密码数据先初始化成加密后的值,或者在登录接口里兼容一下——这步不做,原有账号全部登录不了。

第二件值得做的事,是给列表页加上分页和条件筛选。很多学生管理系统的列表是一次性把所有数据查出来塞给前端,数据量几百条感觉不明显,一旦上千就开始卡了。MyBatis-Plus的分页插件是标准解法:

// 配置MyBatis-Plus分页插件 @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } // Service层使用分页查询 public Page<Student> findStudentPage(int pageNum, int pageSize, String keyword) { LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>(); if (StringUtils.hasText(keyword)) { wrapper.like(Student::getName, keyword); } return studentMapper.selectPage(new Page<>(pageNum, pageSize), wrapper); }

数据库量大的时候,MySQL分页的深翻页问题会暴露出来——OFFSET太大的查询会越来越慢。务实的做法是限制最大查询页数,或者用游标分页的方式,但这一般是毕设之外的考量,点到为止。

第三件值得做的事,是写一个文件导出功能。学生管理系统的成绩导出是需求里最常见的扩展点,用Hutool的ExcelWriter写一个接口即可:

import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.ExcelWriter; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.net.URLEncoder; public void exportScores(HttpServletResponse response) throws IOException { List<ScoreVO> scores = scoreMapper.selectScoreList(); ExcelWriter writer = ExcelUtil.getWriter(true); writer.write(scores, true); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("学生成绩.xlsx", "UTF-8")); ServletOutputStream out = response.getOutputStream(); writer.flush(out, true); writer.close(); }

前端调用时用window.open或者location.href直接请求导出地址就行,不需要axios。这个功能做出来,系统在老师那儿的完成度评价会上一台阶。

验证你改完的东西有没有问题,别只靠“点一下看通不通”。务实的验证方式是:先把后端打包跑起来,用Postman或Apifox把登录、列表、新增、删除、导出五个核心接口各调一遍,确认返回结果符合预期;再构建前端打包进Springboot里,启动一个jar包,从头到尾走一遍用户流程;最后开浏览器控制台,确认没有红色的报错和404请求。这个过程走完,这套源码才算真正变成你手里能拿出去交付的东西。也希望这篇笔记能帮你把学生管理系统这套经典组合吃透。项目源码千千万万,跑通不是目的,能改、能维护、敢接需求才是。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询