ARTICLE DETAIL

资讯详情

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

Spring Boot音乐网站源码实战:前后端分离项目设计与部署解析

Spring Boot音乐网站源码实战:前后端分离项目设计与部署解析 很多人问学完 Spring Boot 基础之后到底做什么项目才能把框架真正串起来我的建议是别只盯着那些纯增删改查的后台管理模板最好找一个场景稍微丰富、有文件交互和真实业务链路的项目来练比如音乐网站。今天我会把一套编号 38382 的 Spring Boot 音乐网站源码拆开讲从技术选型、数据库设计到前后端交互、部署启动把值得复用的点都拎出来。这套项目包含用户端和管理端覆盖登录、歌手、歌曲、歌单、评论、收藏、播放器等常见功能适合做课程设计、毕业设计也适合想快速上手 Spring Boot Vue 这套前后端分离组合的人参考。1. 项目整体设计与技术选型1.1 这个项目能做什么为什么选音乐网站音乐网站是一个很典型的“业务边界清晰但功能链路完整”的项目。它不像博客只有文章增删改查也不像商城那样订单库存特别复杂。音乐场景里既有基础的用户体系又有文件上传歌曲、封面、歌词有资源的分类关联歌手、歌曲、歌单还有用户行为收藏、评论、评分、播放排行。这些模块放在一起几乎覆盖了日常开发中 80% 的常见问题。这套源码 38382 里已经包含了两套前端页面一套给普通用户听歌一套给管理员维护数据。后端则使用 Spring Boot 统一提供接口。也就是说拿到源码以后不是只能对着代码干看而是可以直接启动、注册账号、传歌、建歌单、评论把所有功能在浏览器里真实跑一遍。这种“能跑起来、能玩起来”的学习项目比抽象的 demo 有价值得多。1.2 技术栈选型与取舍先上一份这个项目里的核心技术栈清单层次技术说明后端框架Spring Boot 2.7.x稳定版本兼容 JDK 1.8资料多问题少持久层MyBatis-Plus 3.5.x单表 CRUD 不用写 SQL复杂查询保留 XML数据库MySQL 5.7经典组合初始化 SQL 直接导入鉴权方式JWT 拦截器适合前后端分离、无状态接口文件存储本地磁盘 静态资源映射学习成本低后期可替换为 OSS前端用户端Vue 2 Element UI Axios后台管理界面常见组合前端管理端Vue 2 Element UI与用户端共用一套组件风格构建工具Maven / npm后端依赖和前端依赖各自管理为什么没有用 Spring Cloud、微服务、Elasticsearch 这些更“高级”的东西因为这套项目的定位是让学习者快速理解业务系统是怎么跑起来的。一上来就拆一堆服务反而会把核心逻辑淹没在分布式各个组件里。Spring Boot 负责接口MyBatis-Plus 负责数据库操作JWT 负责身份识别文件走本地磁盘所有链路都清晰可见。等你把这条链路吃透了再往 Redis、对象存储、消息队列方向上扩展会顺手很多。1.3 源码目录结构解读拿到源码之后第一件事不是急着双击运行而是先看目录结构。这套项目的后端采用常见的分层结构src/main/java/com/music ├── common // 统一返回结果、全局异常、JWT工具 ├── config // 跨域配置、拦截器配置、静态资源映射 ├── controller // 接口层接收参数并返回结果 ├── service // 业务逻辑层处理具体规则 ├── mapper // 数据访问层MyBatis-Plus 接口 ├── entity // 数据库实体 ├── utils // 工具类 └── MusicApplication.java // 启动类resources 目录下除了application.yml还有mapper文件夹里面放的是联表查询这类复杂 SQL。前端源码通常分成vue-music-web和vue-music-admin两个目录分别对应前台听歌和管理后台。分层架构的好处是边界清楚。Controller 保持很薄只做参数接收、调用 service、返回 ResultService 里写业务规则比如注册时检查用户名是否重复、歌曲上传时校验文件类型Mapper 只负责数据查询。这样写的好处是你在答辩或者面试时可以很流畅地讲清楚每一层在干什么而不是说“所有代码都堆在 Controller 里”。2. 数据库设计与核心细节2.1 核心数据表与关系音乐网站的数据模型并不复杂但表和表之间的关联关系很典型。源码 38382 里主要包含下面这些表表名作用关键字段user前台用户id, username, password, avatar, create_timeadmin管理员id, username, passwordsinger歌手id, name, pic, introductionsong歌曲id, singer_id, name, pic, url, lyricsong_list歌单id, title, pic, introduction, stylelist_song歌单-歌曲关联表id, song_list_id, song_idcomment评论id, user_id, song_id, content, create_timerank评分id, user_id, song_id, score以歌曲表song为例核心结构大致如下CREATE TABLE song ( id bigint(20) NOT NULL AUTO_INCREMENT, singer_id bigint(20) DEFAULT NULL COMMENT 歌手id, name varchar(255) NOT NULL COMMENT 歌曲名, introduction varchar(1024) DEFAULT COMMENT 简介, pic varchar(255) DEFAULT COMMENT 封面图片URL, lyric text COMMENT 歌词文本, url varchar(255) NOT NULL COMMENT 歌曲文件URL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有一个值得注意的细节虽然song表有singer_id但项目里一般不建物理外键只保留逻辑关联。原因是物理外键在插入、删除时容易产生锁竞争而且做联表查询和迁移数据时会比较麻烦。实际开发里按 ID 关联、用 join 查询就够了外键约束交给应用层去保证。2.2 文件信息的存储方式很多新手在做这类项目时容易踩的一个坑是想直接把音乐文件塞进数据库里。这种做法非常不推荐。音频文件体积大数据库存二进制不仅拖慢查询速度还会让备份和迁移变得很痛苦。这套项目的做法是文件本体上传到服务器磁盘数据库里只存一个相对路径。比如上传完一首歌后库里url字段存的值大概是/music/2024/05/21/xxxxxx.mp3前端拿到这个 URL 后拼上服务器地址就能播放。图片、歌词也是同样的处理方式。为了避免上传文件重名上传时最好用 UUID 重新生成文件名。我自己一般会这样处理String originalFilename file.getOriginalFilename(); String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); String newFileName UUID.randomUUID().toString().replace(-, ) suffix;然后按日期分目录保存比如upload/2024/05/21这样每天的文件都分开后续清理和管理都很方便。2.3 用户密码与 Token 校验设计用户模块如果要放到真实环境密码一定不能明文存储。这套源码里可以使用 BCrypt 对密码做哈希处理注册时加密登录时用matches方法校验。登录成功后后端生成一个 JWT Token 返回给前端String token JwtUtil.createToken(user.getId(), user.getUsername());前端把 Token 存在本地之后发请求时在 Header 里带上Authorization: Bearer token。后端通过拦截器统一校验不需要在每个接口里重复写判断逻辑。拦截器配置时要放行登录、注册、歌曲列表这类公开接口。比如registry.addInterceptor(jwtInterceptor()) .addPathPatterns(/**) .excludePathPatterns( /user/login, /user/register, /song/list, /singer/list, /error );这样既能保护评论、收藏、后台管理等接口又不会给公开页面访问造成障碍。3. 核心功能实现与实操要点3.1 歌曲上传接口的完整流程歌曲上传是音乐网站的关键功能。管理员在后台选择音频文件、封面、填写歌曲名选择所属歌手点击提交后接口需要完成一系列工作文件校验、文件保存、字段入库。Controller 里的上传接口接收MultipartFile文件同时接收歌曲名、歌手 ID、简介、歌词等参数。核心逻辑大致如下PostMapping(/admin/song/add) public Result addSong(RequestParam(file) MultipartFile file, RequestParam(singerId) Long singerId, RequestParam(name) String name) { if (file.isEmpty()) { return Result.error(上传文件不能为空); } // 校验文件类型只允许 mp3、flac、wav 等后缀 String originalFilename file.getOriginalFilename(); if (!checkAudioSuffix(originalFilename)) { return Result.error(不支持的音频格式); } // 保存文件到本地磁盘 String url fileStorageService.store(file); // 写入数据库 Song song new Song(); song.setSingerId(singerId); song.setName(name); song.setUrl(url); songService.save(song); return Result.success(song); }这里面的fileStorageService.store就是封装了生成 UUID 文件名、按日期建目录、调用file.transferTo()保存文件的过程。前端管理端用el-upload组件配一个 action 指向这个接口并加上 Token 请求头。注意在application.yml里调整 Spring Boot 的文件上传限制默认 1MB 往往不够用spring: servlet: multipart: max-file-size: 50MB max-request-size: 100MB3.2 播放器与歌单展示的联动歌曲列表接口返回的数据里不能只有歌曲名和 URL。前端展示列表时需要封面、歌手名播放时需要能拿到url和lyric。所以这个接口往往会联查歌手表把歌手名一起返回。如果使用 MyBatis-Plus可以在自定义 SQL 里写select idselectSongWithSinger resultTypemap SELECT s.id, s.name, s.url, s.pic, s.lyric, g.name AS singerName FROM song s LEFT JOIN singer g ON s.singer_id g.id ORDER BY s.create_time DESC /select前端拿到列表之后用户点击某一行就动态更新audio标签的 src 和封面地址。播放列表、歌词、歌曲详情这些都可以通过当前选中行的数据来驱动不需要额外请求接口。这是播放器体验比较流畅的关键点。3.3 搜索、评论与排行榜的实现搜索功能比较常规前台搜索框把关键字传给后端后端对歌名做模糊查询。如果想让结果更好可以把歌手名也带上QueryWrapperSong wrapper new QueryWrapper(); wrapper.like(name, keyword) .or() .like(singer_id, keyword);更好的做法是联表查询后在内存里组合但作为第一版音乐网站直接用LIKE完全可以。等数据量到了一定程度再考虑引入搜索组件。评论功能要注意两点内容必须做转义防止 XSS 脚本注入返回评论列表时需要联查用户表把用户名和头像带给前端否则没法显示“谁说了什么”。评论分页使用 PageHelper 或 MyBatis-Plus 分页插件都可以我习惯在配置类里统一加一个 MybatisPlusInterceptor。排行榜可以直接用评分表做聚合SELECT song_id, AVG(score) AS avg_score FROM rank GROUP BY song_id ORDER BY avg_score DESC LIMIT 10要注意防止没有评分的歌曲不被聚合到也注意分数为空的情况用IFNULL(score, 0)处理一下。3.4 统一返回体与前后端分离联调前后端分离项目里接口返回格式如果不统一前端处理数据会非常痛苦。这套源码中有一个统一的Result类public class Result { private Integer code; private String message; private Object data; }成功时返回 code200业务错误返回 code400未登录返回 code401服务器异常返回 code500。全局异常处理器用RestControllerAdvice捕获异常避免把异常堆栈直接抛给前端RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(Exception.class) public Result handleException(Exception e) { return Result.error(500, 服务器异常); } }还有一个常见问题是跨域。前后端分离开发时后端跑在 8088 端口前端跑在 8080 端口如果不做处理浏览器会拦截请求。解决方式是在后端配置一个全局跨域配置类允许前端地址访问。也可以让前端用代理转发但我更喜欢后端统一配置生产环境再用网关或 Nginx 处理。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true); } }4. 源码部署与启动全过程4.1 环境准备与配置项修改本地跑这套源码 38382 之前先确认环境JDK 1.8 或更高版本Maven 3.6MySQL 5.7 或 MySQL 8.0Node.js 12/14用于前端拿到源码后首先到sql目录里找到初始化 SQL 脚本用 Navicat 或命令行导入数据库。导入完成后修改后端application.yml里的数据库连接信息spring: datasource: url: jdbc:mysql://localhost:3306/music_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 servlet: multipart: max-file-size: 50MB music: upload-path: /Users/yourname/music-data/如果使用 MySQL 8.0驱动类名要改为com.mysql.cj.jdbc.DriverURL 里最好加上allowPublicKeyRetrievaltrue否则可能报公钥检索错误。4.2 后端和前端分别如何启动后端启动比较简单。用 IDEA 打开项目根目录等待 Maven 下载完依赖之后直接运行MusicApplication启动类。启动成功后可以先在浏览器访问一个公开接口比如http://localhost:8088/song/list如果能返回 JSON 数据说明后端已经正常工作了。前端需要先安装依赖cd vue-music-web npm install npm run serve如果npm install过程中出现网络问题可以临时切换镜像源npm config set registry https://registry.npmmirror.com前端启动后浏览器访问http://localhost:8080用数据库里的用户账号登录或者直接注册一个新账号。管理端一般在http://localhost:8081默认管理员账号密码在初始化 SQL 里可以查到常见是admin / 123456。4.3 常见启动报错与解决办法很多新手第一次跑源码时会被几个固定问题卡住。我把最常见的几个整理成表格遇到问题可以直接对照排查现象原因解决办法APPLICATION FAILED TO START端口被占用杀占用进程或修改server.portAccess denied for user ‘root’‘localhost’数据库密码错误检查 application.yml 中的密码、用户名Unknown database ‘music_db’数据库没有创建先用 CREATE DATABASE music_db 建库再导 SQLInvalid bound statementMapper XML 没被扫描确认 XML 在 resources/mapper 下且 MapperScan 路径正确前端页面接口 404后端接口路径与前端不一致检查前端 api 文件里的 baseURL 和后端 Controller 路径歌曲、图片加载不出来静态资源映射不对检查上传路径和 addResourceHandlers 中的路径是否一致补充一个我自己的经验如果遇到歌曲上传成功后访问 404先看数据库里的url字段值再手动在浏览器里打开这个地址。如果 404基本就是虚拟路径映射没配置好重点检查file:前缀和目录末尾的斜杠。5. 从源码到独立项目的扩展建议5.1 给这个项目“加分”的几个优化方向这套音乐网站源码作为起步项目完全够用但要写到简历上或者作为毕业设计冲刺优秀还可以做几个方向上的扩展。第一加 Redis 缓存。把歌手列表、歌曲排行榜、热门歌单这类高频读接口缓存起来减少数据库压力。登录 Token 也可以改成 Redis 存储方便做主动失效和续期。第二把文件存储换成对象存储。学习阶段本地磁盘没问题但真实部署时磁盘扩容麻烦而且多台服务器之间文件不同步。接入 MinIO 或者云上 OSS代码改动其实很小只要把上传逻辑抽成一个 StorageService 接口本地和 OSS 各写一个实现。第三搜索功能升级。当前用 LIKE 已经够用但歌曲量上来以后可以用 Elasticsearch 做中文分词搜索把歌名、歌手名、专辑名都建立索引。这个扩展会显著提升搜索体验。第四增加用户行为分析。基于用户收藏和播放记录做一个“每日推荐”或者“相似歌曲”模块。最基础的协同过滤算法并不复杂关键是能把 Spark 或简单 Python 脚本产生的结果回写到推荐表里。5.2 动手改项目时的避坑提示我自己在给这套源码做二次开发时踩过几次坑分享几个比较实用的避坑提示。第一先跑通再动代码。很多同学拿到源码直接改数据表结构结果前端页面全部报错又说不清楚是哪里改坏了。正确做法是先原封不动跑一遍再逐步改每改一步都启动验证一次。第二不要随便删表。音乐网站涉及歌单-歌曲关联、评分、评论多张关联表删表前一定先看被哪些接口引用。尤其是中间表list_song删错会导致歌单页打不开。第三改前端接口地址时记得同步修改后端跨域配置。常见错误是前端页面换了个端口访问但后端CorsConfig还是只允许原来的端口结果接口被浏览器拦截。第四连接数据库之前确认表名、字段名大小写问题。MySQL 在 Linux 上对表名大小写敏感如果导入 SQL 时把表名写成大写而代码里查的是小写会立刻报表不存在。最后再说一个小的实操习惯开发时把 MyBatis 的 SQL 日志打开这样一旦接口数据不对可以直接在控制台看到实际执行的 SQL排查效率能提升不少。在application.yml里加上mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl我个人整理这份源码的经验是学项目不要急着把每个类都读完而是先用一条主线走通——“用户登录 - 看到歌曲列表 - 点击播放”。把这条链路里的 Controller、Service、Mapper 全部跟一遍再向外扩展去研究上传、评论、排行榜你会突然发现 Spring Boot 这层连接方式全通了。之后再动手加一个“最近播放”或者“每日推荐”功能把新接口加入原有前端页面这才算真正把源码变成了自己的东西。
返回列表