news 2026/9/9 17:37:31

SpringBoot+Vue前后端分离教学辅助系统:从JWT鉴权到部署实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue前后端分离教学辅助系统:从JWT鉴权到部署实践

从教学管理痛点出发:我把这套SpringBoot+Vue教育辅助系统完整拆给你看

如果你在学校信息中心、培训机构或者自己做独立开发,大概率遇到过这样的场景:排课靠Excel,选课靠人工统计,成绩录入要手动汇总,学生请假还要纸质审批流转到教务处。一套能真正跑起来的教学辅助系统,往往比想象中的更刚需。最近我基于SpringBoot+Vue的前后端分离架构,整理并重建了一套教学辅助系统,功能覆盖用户登录认证、课程管理、选课退课、成绩录入、公告发布、班级管理等核心教育场景。这篇文章不是简单的项目介绍,而是把我研究这套JavaWeb源码时的设计思路、核心实现、部署过程和踩坑记录全部整理出来,希望对正在做毕业设计、课程设计或者刚接触前后端分离项目的开发者有帮助。

这套系统最大的价值在于它的工程结构完整且克制:后端用SpringBoot搭建RESTful API,前端用Vue实现单页应用,数据库采用MySQL,鉴权用JWT,没有引入太多花哨的中间件,非常适合用来理解一个真实JavaWeb教育项目从0到1的构建脉络。如果你正在找springboot和vue结合的项目案例,或者想学习javaweb项目完整案例mysql版本如何组织表结构和接口设计,这套系统是一个很好的参考样本。下面我从设计思路、核心实现、前端细节、部署实操和问题排查五个维度,逐层拆解。

1. 项目整体设计与技术选型思路

1.1 为什么选择SpringBoot+Vue而不是传统JSP模式

早几年的JavaWeb教学项目,主流方案是JSP+Servlet+Tomcat,所有页面由后端渲染,前端和后端代码耦合在一个war包里。这种模式对小型Demo来说确实简单,但一旦业务复杂起来,痛点非常明显:前端工程师没法独立开发,后端每次改页面都要重新打包部署,接口和页面逻辑纠缠在一起,维护成本极高。

SpringBoot+Vue的前后端分离方案,正好解决这些问题。SpringBoot负责提供纯JSON接口,Vue负责页面渲染和数据交互,两者通过HTTP通信,互不干扰。开发阶段用Vue自带的devServer做代理转发,生产阶段将Vue打包后的dist目录交给Nginx或者SpringBoot静态资源映射来托管,整个链路非常清晰。我在实际使用中最大的感受是:前后端分离之后,调试接口和调整页面变成了两条独立的流水线,后端可以用Postman测接口,前端可以Mock数据调页面,效率提升非常明显。

1.2 教学辅助系统的核心业务模块拆解

在动手写代码之前,我习惯先把业务模块画清楚。这套系统我拆成了六个核心模块,每个模块对应一组独立的Controller、Service、Mapper接口:

  • 用户认证模块:登录、注册、JWT签发与校验、退出登录,角色分管理员、教师、学生三种。
  • 课程管理模块:课程信息维护、课程列表查询、课程状态管理(可选/已满/已结课),管理员有全部操作权限,教师可以维护自己名下的课程。
  • 选课退课模块:学生查看可选课程、提交选课、退课,选课名额校验,防止超选。
  • 成绩管理模块:教师录入成绩、修改成绩,学生查询自己的成绩,管理员查看整体成绩统计。
  • 公告管理模块:发布教学通知、置顶公告、按时间线展示公告列表。
  • 班级与学生管理模块:管理员维护班级信息、导入学生账号、重置密码等。

这样一个模块化设计的好处是,每个功能点都有明确的分层归属,新人拿到源码后能很快定位到相关的Java类和Vue组件,学习成本大幅降低。如果你自己在设计类似系统,我也建议先把角色和权限矩阵画出来,再动工写代码,不然后面改起来会很痛苦。

1.3 开发环境与版本选型参考

我在本地复现这套系统时,使用的环境如下,也推荐你用相近的版本组合,能少踩很多版本兼容性的坑:

组件版本说明
JDK1.8SpringBoot 2.x的稳定基础
SpringBoot2.7.x稳定版,社区资料丰富
MyBatis-Plus3.5.x增强ORM,自带分页插件和自动填充
MySQL5.7 或 8.0生产常用版本均可
Vue2.6.x 配合Vue CLI 4.x本源码采用Vue2,生态成熟
Element-UI2.15.x配合Vue2使用最稳妥
Node.js14.x 或 16.x版本过高会导致依赖安装报错
Maven3.6.x后端依赖管理
IDEA2022.x 以上开发IDE

其中SpringBoot版本是一个很值得注意的点,如果你使用springboot 3.x,那么javax包会全部改为jakarta,很多旧源码直接运行会报包找不到。我建议先按项目的原始版本跑通,再去考虑升级,这样能省掉不少排查环境问题的时间。

2. 后端核心实现:从登录鉴权到业务接口

2.1 JWT登录鉴权的完整闭环

登录鉴权是绝大部分JavaWeb项目的第一道门槛,这套系统采用JWT方案。JWT的机制可以理解为:用户登录成功之后,后端生成一串包含用户身份信息的加密字符串返回给前端,前端后续每次请求都把这个字符串放在请求头里,后端通过拦截器解析并验证用户身份,整个过程不需要在服务端保存会话数据,非常适合前后端分离的场景。

后端的关键实现在于两点:一是JWT工具类负责生成和解析Token,二是拦截器负责拦截需要认证的请求。以登录接口为例,用户提交用户名和密码后,UserService会先调用MyBatis-Plus的LambdaQueryWrapper去数据库比对密码,若匹配则生成Token返回,否则抛出自定义业务异常。

生成Token的核心代码如下:

public String generateToken(Long userId, String username, String role) { // 设置过期时间,这里设置24小时 Date expireDate = new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000); return Jwts.builder() .setSubject(username) .claim("userId", userId) .claim("role", role) .setIssuedAt(new Date()) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }

解析Token的代码如下:

public Claims parseToken(String token) { return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody(); }

有个容易被忽略的细节:JWT的secretKey一定不要硬编码在代码里,我实际开发中是放在application.yml配置文件中,并通过@Value注解注入,这样不同环境的密钥可以独立配置,避免安全隐患。还有一点是Token过期处理,前端axios拦截器中如果收到401状态码,应自动跳转到登录页并清除本地用户信息,这个联动逻辑我会在后面的前端部分详细讲。

2.2 拦截器实现与角色权限控制

有了JWT解析能力之后,还需要有拦截器来统一校验,不然每个接口都写一段Token解析代码会非常冗余。我在这套系统中定义了一个JwtInterceptor,实现HandlerInterceptor接口的preHandle方法:

@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录、注册等白名单接口 if (handler instanceof HandlerMethod) { String token = request.getHeader("Authorization"); if (token == null || token.isEmpty()) { throw new BusinessException(401, "未登录或Token已失效"); } // 校验并解析Token Claims claims = jwtUtil.parseToken(token.replace("Bearer ", "")); // 将用户信息存入ThreadLocal,方便后续获取当前用户 UserContext.set(claims); } return true; } }

为什么这里要加handler instanceof HandlerMethod的判断?因为如果没有这个判断,当请求的是一个静态资源或者对应用路径的Resource处理器时,拦截器也一样会试图解析Token,造成不必要的报错。这个小坑我踩过一次,排查了半天才明白是拦截器拦截了静态资源导致的。

更细的权限控制我这里建议配合自定义注解来做,比如定义@RequireRole("ADMIN"),在拦截器中读取HandlerMethod上的注解,再对比JWT中的role字段,不一致就返回403。这套方案比单独写三个拦截器要灵活得多,新增加接口时只要在方法上加上对应注解,权限控制就完成了。

2.3 基于MyBatis-Plus的表结构与自动建表技巧

数据库设计这部分,我使用的是MySQL,数据库名定义为edu_assistant。核心表包括:sys_user(用户表)、sys_role(角色表)、course(课程表)、course_selection(选课表)、score(成绩表)、notice(公告表)、classes(班级表)。这里重点说下选课表,它其实是一个中间关联表,字段包含选课ID、学生ID、课程ID、选课时间,同时设置了唯一索引(student_id, course_id),从数据库层面杜绝重复选课。

关于建表,一个很实用的技巧是配合MyBatis-Plus的自动建表能力。虽然MyBatis-Plus本身不直接支持自动建表,但可以通过在配置中扫描实体类,结合TableName注解和TableField注解,在系统启动时执行DDL语句。如果你的表还不存在,可以写一个简单的SchemaInitializer,在ApplicationRunner中判断表是否存在,若不存在则执行建表SQL。这个小功能对于快速部署项目非常友好,我经常在演示项目中使用。

@Component public class DatabaseInitializer implements ApplicationRunner { @Autowired private JdbcTemplate jdbcTemplate; @Override public void run(ApplicationArguments args) { String checkTableSql = "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='edu_assistant' AND TABLE_NAME='sys_user'"; Integer count = jdbcTemplate.queryForObject(checkTableSql, Integer.class); if (count == null || count == 0) { // 执行建表脚本 jdbcTemplate.execute("CREATE TABLE sys_user (...)"); } } }

这个属于锦上添花的方案,实际项目中更常用的做法是维护一份schema.sql脚本,在部署时手动导入数据库。源码中一般会附带的editor.sql就是这种脚本,直接导入就可以了。但自动建表适合给不懂数据库的人演示用,省去导入步骤。

2.4 选课与成绩模块的业务逻辑边界

选课模块有一个常见的坑就是超选问题。假设一门课的容量是50人,两个学生同时提交选课请求,如果没有做并发控制,可能两个请求都查到了当前选课人数为49,然后都执行插入,就变成51人。解决方式有两种:一种是悲观锁,在事务中SELECT ... FOR UPDATE锁定课程记录;另一种是乐观锁,在课程表中加一个version字段,更新时比较version是否一致。

这套系统里的实现相对简单,在选课Service中加入事务,并在插入前先做一个update操作来占住名额:

@Transactional public void selectCourse(Long studentId, Long courseId) { // 先扣减课程剩余名额,返回受影响的行数 int updated = courseMapper.reduceRemainCount(courseId); if (updated == 0) { throw new BusinessException(500, "课程已满,选课失败"); } // 插入选课记录 courseSelectionMapper.insert(new CourseSelection(studentId, courseId)); }

这里的关键点是:先执行update再插入,而不是先查询再插入,因为update语句本身会被行锁保护,在高并发下能有效避免超卖问题。这也是我在实际处理库存类业务时比较推荐的做法,简单有效,不用引入分布式锁。

成绩模块相对简单,但要注意的一个设计是成绩等级的自动划分。教师录入的是百分制分数,系统可以根据分数段自动映射为优秀、良好、中等、及格、不及格几个等级,这个逻辑在Service层做一个工具方法即可,不需要前端参与。如果你后续要扩展教师端成绩分析功能,比如班级平均分、最高分、各分数段占比,直接基于成绩表做聚合查询就行。

3. 前端Vue实现细节:从环境搭建到组件封装

3.1 Vue环境配置与项目初始化

前端部分,我先说说环境配置,因为这里是最容易让新手卡住的地方。Vue项目的开发依赖Node.js环境,我强烈建议安装Node.js 14或16版本,不要用最新的20+,否则安装依赖时很容易遇到node-sass编译失败、依赖版本不兼容等问题。如果你用的是nvm管理Node版本,切换起来会非常方便。

# 查看当前Node和npm版本 node -v npm -v # 安装Vue CLI脚手架 npm install -g @vue/cli@4.5.15 # 创建项目 vue create edu-frontend

创建项目的时候,建议选择Manually select features,然后勾选Router、Vuex、CSS Pre-processors。如果直接用默认的Default预设,后续可能需要自己手动安装router和vuex,反而多一步操作。项目创建完成后,别忘了安装UI组件库和HTTP请求库:

npm install element-ui axios

Vue安装依赖时,我遇到过不少次因为镜像地址访问缓慢导致安装失败的情况,解决办法是切换到npm淘宝镜像源:

npm config set registry https://registry.npmmirror.com

这样速度会快很多,安装依赖的体验会好不少。如果你还是遇到卡在安装某个依赖上,可以删除node_modules和package-lock.json,再重新执行npm install。

3.2 基于Vue Router的前端路由与权限控制

前端路由权限控制,是很多初学者容易忽略但又非常重要的部分。这套系统的菜单是根据用户角色动态渲染的,学生登录后看不到成绩管理菜单,教师登录后看不到用户管理菜单,管理员则拥有全部权限。

实现思路是在Vue Router中定义路由时给每个路由增加meta信息,比如:

{ path: '/admin/users', name: 'UserManage', component: () => import('@/views/admin/UserManage.vue'), meta: { title: '用户管理', roles: ['ADMIN'] } }

然后在路由守卫beforeEach中判断当前用户的角色是否在路由meta.roles中,如果不在则跳转到401页面或首页。

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (!token && to.path !== '/login') { next('/login'); return; } const userRole = store.getters.role; if (to.meta.roles && to.meta.roles.indexOf(userRole) === -1) { next('/403'); return; } next(); });

路由懒加载也值得提一下,() => import('@/views/xxx.vue')这种方式会让每个页面单独打包成一个chunk,首屏加载速度明显更快,推荐所有项目都用这种写法。

前端路由参数传递也是高频使用点,比如课程详情页需要知道点击的是哪门课,可以用this.$route.params.id或者this.$router.push({ path: '/course/detail', query: { id: courseId } })。query方式的参数会出现在URL上,刷新后参数不丢失;params方式如果不用动态路由匹配,刷新后参数会丢失,这也是一个容易踩到的坑。

3.3 axios请求封装与统一异常处理

axios封装是我的Vue项目中必做的一个环节,没有封装直接在每个页面里写axios.get会导致代码大量重复,而且后端返回结构不统一时,每个页面都要做异常处理,极容易漏处理。

我习惯这样封装:创建api/request.js,通过axios.create创建一个配置了baseURL和超时时间的实例,然后添加请求拦截器和响应拦截器。请求拦截器把本地存储的token加在请求头上:

service.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } return config; });

响应拦截器统一处理后端返回的错误码,比如401跳转登录页、500弹出错误提示等:

service.interceptors.response.use( response => { const res = response.data; if (res.code === 200) { return res; } Message.error(res.msg || '请求失败'); return Promise.reject(new Error(res.msg || '请求失败')); }, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token'); router.push('/login'); } Message.error('网络请求异常'); return Promise.reject(error); } );

这样一来,业务页面里只需要调用封装的api方法,不用再关心错误处理,整体代码会清爽很多。另外建议把每个模块的接口单独建一个js文件,比如api/course.js、api/score.js,按模块维护URL,避免所有接口都堆在页面里。

3.4 课程视频播放场景:用video.js实现m3u8流播放

这套教学辅助系统里有一个和课程内容展示相关的功能:在线播放教学视频。在实际项目中,教学视频往往以HLS流媒体格式提供,也就是说视频地址是一个.m3u8的索引文件。在Vue中播放m3u8,推荐使用video.js配合videojs-contrib-hls插件,这样兼容性比较好。

npm install video.js videojs-contrib-hls

然后在组件中引入并初始化播放器:

import videojs from 'video.js'; import 'video.js/dist/video-js.css'; import 'videojs-contrib-hls'; mounted() { this.player = videojs(this.$refs.videoPlayer, { sources: [{ src: this.videoUrl, type: 'application/x-mpegURL' }], controls: true, autoplay: false, preload: 'auto' }); }, beforeDestroy() { if (this.player) { this.player.dispose(); } }

需要特别注意的是,播放m3u8时视频源必须允许跨域访问,否则浏览器会直接拦截视频请求,页面会一直黑屏。如果视频服务器是单独的域名,记得在视频服务端加好CORS响应头,或者通过后端代理转发视频请求,这是实际部署中非常容易踩坑的地方。

还有一个细节是播放器销毁,在组件销毁时一定要调用player.dispose()释放播放器实例,否则切换到其他页面后,视频的音频可能还在后台继续播放,这个小问题排查起来让人很抓狂。

4. 本地部署与服务器发布完整实操

4.1 后端环境准备与数据库初始化

拿这套源码在本地跑起来,我建议按顺序操作。先处理数据库,打开Navicat或者命令行客户端,创建一个edu_assistant数据库,设置好字符集为utf8mb4,然后将源码中附带的SQL脚本导入。为什么要用utf8mb4而不是utf8?因为utf8mb4能完整支持中文和emoji表情,而且MySQL 8.0默认字符集就是utf8mb4,保持一致能避免不少乱码问题。

导入完成后,修改后端application.yml配置文件,核心配置有三块:数据库连接信息、Redis连接信息(如果用到了缓存)和JWT密钥。数据库配置示例:

spring: datasource: url: jdbc:mysql://localhost:3306/edu_assistant?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

用IDEA打开后端项目后,点击Maven侧边栏的Reload All Maven Projects,等待依赖下载完成。如果下载速度慢,可以在Maven的settings.xml中配置阿里云镜像。依赖加载完毕后,直接运行主启动类Application,确认控制台输出SpringBoot启动成功的日志,并且没有报数据库连接错误,后端就算是跑通了。

4.2 前端本地启动与跨域代理配置

前端部分,在项目根目录执行npm install安装依赖,然后执行npm run serve启动开发服务器。但是这里有个关键问题:Vue开发服务器默认运行在localhost:8080,后端接口运行在localhost:8081,跨域问题会直接拦截请求,所以需要配置vue.config.js中的devServer代理:

module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true, pathRewrite: { '^/api': '' } } } } }

这里的规则是:前端所有以/api开头的请求,都会被代理转发到后端8081端口,同时去掉/api前缀。比如前端请求/api/user/login,实际后端收到的是/user/login。这样在开发阶段就绕过了跨域限制,不需要后端单独开启CORS。

4.3 云服务器部署的整体思路

部署到云服务器,其实核心就三件事:后端打包、前端打包、配置Nginx反代。

后端打包用Maven的package命令,执行mvn clean package -DskipTests,在target目录下生成一个jar包。然后用java -jar edu-assistant.jar启动。如果想把启动过程放到后台,可以用nohup命令:

nohup java -jar edu-assistant.jar --server.port=8081 > app.log 2>&1 &

前端打包执行npm run build,生成dist目录,把dist目录上传到服务器。然后配置Nginx,将80端口的请求分发到前端静态资源和后端接口:

server { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

这里说一下try_files的作用,Vue是单页应用,路由切换是通过前端history模式实现的,如果用户直接访问/course/detail这样的路径,Nginx需要把请求重新导向到index.html,让Vue自己解析路由,try_files就是干这个事情的。不加这一行,刷新非首页路径时经常会报404,开发部署时十个人有八个会踩这个坑。

4.4 Windows服务器上Tomcat或Apache的搭配发布

除了Linux服务器,有些学校或者单位的Windows服务器也会用来部署JavaWeb项目。虽然SpringBoot内嵌了Tomcat,直接用java -jar可以跑,但如果你的服务器上已经部署了其他JavaWeb应用,有独立的Tomcat,也可以把SpringBoot项目打成war包发布到Tomcat的webapps目录下。

打成war包需要两步:一是pom.xml中把打包方式改为<packaging>war</packaging>,二是主启动类继承SpringBootServletInitializer并重写configure方法:

@SpringBootApplication public class EduApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(EduApplication.class); } }

演示一下Windows Server上Apache+Tomcat搭配发布的方式:用Apache作为前端入口处理静态资源(比如Vue打包后的dist目录),动态请求转发给Tomcat的8080端口,Tomcat负责执行SpringBoot的war包。Apache通过mod_proxy模块进行反向代理,配置上比Linux Nginx稍显繁琐,但在老牌Windows服务器环境里还是很常见的。

至于Redis,如果系统用到了Redis做缓存或Token存储,在Windows服务器上需要手动安装Redis的Windows版本,或者使用Memurai这类兼容方案。我给这个系统做扩展时,就在后台接入了Redis来缓存课程列表热点数据,设置10分钟过期时间,实测接口响应从200ms降到了30ms以内,效果非常明显。

5. 常见问题与排查技巧实录

5.1 前端安装依赖报错处理

跑Vue项目中,npm install阶段大概率会遇到问题,最常见的有node-sass安装失败、模块版本不兼容、peerDependencies冲突等。

我遇到最多的是node-sass的安装失败,因为node-sass需要根据Node版本编译原生模块,Node版本太新或者太旧都会导致安装时报错。解决办法是改用sass(dart-sass),在package.json中替换依赖,然后重新安装:

npm uninstall node-sass npm install sass --save-dev

另外如果npm install报ERESOLVE错误,说明依赖树有冲突,可以试试npm install --legacy-peer-deps,这个参数会按照npm旧版本的解析逻辑跳过peerDependencies检查,很多时候能一次性装通。

5.2 SpringBoot启动报数据库连接失败的排查

后端启动时最常见的错误是Access denied for user 'root'@'localhost'或者Communications link failure。第一种是用户名密码错误或者用户没有远程访问权限,本地测试时建议直接使用root账号并确认密码无误;第二种是数据库地址无法访问,先把jdbc:mysql://localhost:3306/edu_assistant中的localhost和端口号核对一遍,然后确认MySQL服务有没有启动。

一个很容易被忽略的点是MySQL驱动版本和MySQL服务版本的匹配。如果你的MySQL是8.0版本,但pom.xml中引用的mysql-connector-java是5.x的,启动时会有时区相关的报错。这时候应该使用MySQL官方的Connector/J 8.x,同时把驱动类改为com.mysql.cj.jdbc.Driver,URL参数中加上serverTimezone=Asia/Shanghai,问题就能解决。

5.3 接口请求404或跨域问题的快速定位

联调阶段经常遇到前端访问接口返回404或者被CORS策略拦截。如果是404,先确认请求的URL路径和后端Controller的RequestMapping是否完全一致,特别要注意路径上有没有斜杠差异,比如/user/login/user/login/在SpringBoot中虽然能兼容,但在某些严格配置下可能不匹配。

如果是CORS跨域错误,开发环境优先检查vue.config.js中的代理配置是否正确,因为一旦配置了代理,浏览器看到的请求是同源的,不应该出现CORS错误。如果出现了,多半是代理没有生效,npm run serve之后没有重启,或者路径没有以/api开头。生产环境则需要在Nginx中正确配置proxy_pass,让前端路径和后端路径的映射关系对应上。

还有一个WebSocket相关的坑,如果某些功能用到了WebSocket(比如在线考试防切屏提示),代理配置需要额外处理proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";,否则WebSocket连接建立失败,前端会一直报连接错误。

5.4 内存溢出和接口超时的排查思路

在服务器上跑了一段时间后,如果发现系统变慢或者抛出OutOfMemoryError,优先考虑做两点:一是给JVM设置合理的堆内存参数,比如-Xms256m -Xmx512m;二是排查代码中是否有慢SQL或者数据量大的查询没有做分页。如果教学辅助系统里学生选课记录超过几万条,查询选课列表的时候用全表扫描,速度会慢到让人崩溃,一定要给表加上合适的索引,比如选课表的(student_id, course_id)联合索引。

线上环境排查CPU飙高的问题,我一般会先用top命令看哪个进程占用CPU高,再用jstack导出线程快照,看是否有线程长时间处于Runnable状态。很多时候都是死循环或者某个接口在频繁执行大查询导致的,定位到具体代码后再优化。

5.5 一套实用的问题排查速查表

现象可能原因快速排查方法
前端npm install卡住网络问题/镜像慢切换npm源为国内镜像
后端启动报驱动类找不到pom中依赖版本问题改用mysql-connector-j 8.x
接口返回401Token过期/未携带检查请求头Authorization
部署到服务器后前端白屏静态资源路径错误检查dist上传位置和Nginx root
刷新页面404缺少try_files配置在Nginx中配置fallback到index.html
视频无法播放跨域或m3u8地址不可达检查CORS和视频服务状态
选课人数超限并发问题改用update扣减名额方案

这套速查表是我实际开发中逐步积累下来的,能覆盖大多数JavaWeb前后端分离项目的常规问题。遇到问题先对照排查一遍,大部分情况都能解决,剩下的再通过日志深入了解。

写在最后:我的实际体会与扩展建议

我复盘这套SpringBoot+Vue教学辅助系统的源码时,最大的感受是:结构清晰的项目比功能堆砌的项目更有学习价值。它没有引入过多复杂的中间件,核心链路是SpringBoot+MyBatis-Plus+JWT+Vue+Element-UI,每一环都是JavaWeb技术栈中最主流、资料最多的选择。如果你是一名学生,可以用它完成毕业设计并借此讲清楚前后端交互原理;如果你是一名初级开发者,可以用它理解真实项目中的用户角色、事务处理和部署流程。

如果后续要扩展,我建议优先考虑两个方向:一是引入Flowable工作流引擎来替换现在的公告审批和请假审批,让流程可视化,这部分在热词中也提到了springboot使用flowable,是一个进阶好方向;另一个是接入Redis缓存课程列表和验证码,顺便学习缓存穿透、缓存击穿这些经典问题的应对方案。教学辅助系统的业务边界还可以扩大到在线考试、作业提交、学生考勤等场景,底层这套用户角色和课程体系已经具备了较好的扩展基础。

实际动手做一遍,踩过几个坑之后,你对SpringBoot+Vue这套组合的理解会远超看一百篇教程的效果。项目不算大,但五脏俱全,把它吃透,足以应付绝大多数教育类JavaWeb项目的开发需求。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 17:37:16

击破数据孤岛,主数据管理系统打造企业内部统一数据语言

击破数据孤岛&#xff0c;主数据管理系统打造企业内部统一数据语言摘要&#xff1a;企业多套业务系统独立运行、各存一套数据&#xff0c;同一客户在不同系统中名称不同、编码各异&#xff0c;形成数据孤岛&#xff1b;标准缺失导致数据重复错漏&#xff0c;跨部门对账困难、决…

作者头像 李华
网站建设 2026/9/9 17:36:43

2026外贸线上获客难?靠谱服务商推荐助力出海

摘要&#xff1a;面对询盘流失、内容产能瓶颈、渠道运营分散及转化效率低下等共性挑战&#xff0c;B2B制造类外贸企业亟需数字化转型。星谷云深耕B2B出海领域近16年&#xff0c;依托自研AI智能体矩阵平台&#xff0c;构建“人机协同”的全链路出海营销体系&#xff0c;精准解决…

作者头像 李华
网站建设 2026/9/9 17:36:02

陌生RAR压缩包安全解压与归档:从验真到防坑全流程

简介&#xff1a;针对黄河流域复杂水系&#xff0c;这份GIS矢量数据集整合了1-5级支流及1:25万二级、三级流域分级图层&#xff0c;适合地理信息系统、水利水文与生态保护方向的科研人员、学生和从业者&#xff0c;用于快速摸清流域河网层级和水系结构。压缩包共49个文件&#…

作者头像 李华
网站建设 2026/9/9 17:35:13

Excel + Python 实现合同文档自动化批量生成

做合同有多烦&#xff0c;做过的人都知道。销售部门发来一份合同评审单&#xff0c;里面客户名称、产品型号、金额、付款方式、交期&#xff0c;密密麻麻几十个字段&#xff0c;要一个个复制到标准合同模板里。复制粘贴倒还好说&#xff0c;最怕的是漏填、填错位置、格式不统一…

作者头像 李华
网站建设 2026/9/9 17:34:39

GPT-6 Astra 画 PCB,硬件工程师的护城河还剩多少?

说实话&#xff0c;看到 GPT-6 Astra 开始画 PCB 这条消息&#xff0c;我第一反应不是膜拜&#xff0c;而是低头看了看自己桌上那堆 Gerber 文件和还没焊完的样机。过去几年&#xff0c;硬件工程师一直拿一句话安慰自己&#xff1a;软件能被 AI 写&#xff0c;硬件总得有人焊。…

作者头像 李华
网站建设 2026/9/9 17:32:15

Day6攻克HDFS:分布式存储核心原理与实操指南

1. Day6到底该学什么&#xff1a;找准学习路上的关键锚点 1.1 前5天走完了什么路 很多刚开始接触大数据开发的人&#xff0c;最容易犯的一个毛病就是&#xff1a;今天翻到一篇讲Spark的文章&#xff0c;觉得挺酷&#xff1b;明天又看到Flink的实时计算案例&#xff0c;又开始纠…

作者头像 李华