Spring Boot项目实战:从零搭建网易云音乐系统

发布时间:2026/8/30 18:04:32
Spring Boot项目实战:从零搭建网易云音乐系统 最近有不少读者私信问我Java 基础语法学完了Spring Boot 的教程也刷了好几套但简历上“项目经验”一栏还是不知道该写什么。面试官一问“你做过什么完整项目”就只能支支吾吾。如果你也卡在这一步这个基于 Spring Boot 的网易云音乐系统项目可能是你现阶段最该上手的那一个。先说我的判断这个项目的价值不在“网易云”这三个字上而在于它用音乐管理这样一个够熟悉、又不复杂的业务场景把一款 Web 系统从数据库设计、后端接口、管理员后台到运行调试的完整开发路径走了一遍。它不是那种只写了几个 CRUD 接口的“玩具项目”而是适合做毕业设计、也适合写进简历的参赛型实战项目。这篇文章我会站在手把手教学的角度拆解这个项目的真实价值、核心概念、环境搭建、代码实现、运行验证和常见坑位。如果你正准备做毕设或者想用最短时间跑通一个像样的 Spring Boot 项目建议先收藏再跟着操作。1. 这篇文章真正要解决的问题很多学习者的状态是单看 Spring Boot 知识点都能看懂RestController会加Autowired会写application.yml也会配。但一旦面对一个“完整项目”就完全不知道从哪里下手先建表还是先建工程接口路径怎么规划用户登录怎么办歌曲和歌单之间是什么关系最后做出来的项目要么只有几个孤立的增删改查要么结构混乱连自己都讲不清楚。这个网易云音乐系统项目解决的就是这个“项目经验断层”问题。它选的业务域非常合适业务模型够标准包含用户、歌手、歌曲、歌单、评论等实体能练到一对多、多对多关系难度梯度合理基础版本只需要做到用户登录、歌曲管理和歌单维护就能覆盖 Spring Boot 开发的大多数通用技能扩展空间大加入 Redis 缓存、文件上传、Elasticsearch 搜索、微信登录等都能让它变成简历上的亮眼项目。还有一点要说清楚。标题里写“一小时搞定”我的理解是如果你已经准备好了源码和数据库脚本照着文档把项目从环境配置到启动运行整个流程走通一小时是完全可以做到的。但如果你以为不用看代码、不用理解结构、不用排错就能把别人几天的工作量压缩成一小时那不现实。所以这篇文章的定位是帮你理解项目结构再带你走通整个流程。一句话总结读到这篇文章的以下读者最应该认真看完全文——正在准备 Java 后端毕业设计的在校生、学完 Spring Boot 但缺少项目经验的转行人员、以及想在简历里补充一个“有业务含义的完整项目”的初级开发者。2. 项目定位与核心概念2.1 Spring Boot 在项目中扮演什么角色Spring Boot 是当前 Java 后端开发中使用率极高的微服务开发框架。它的核心价值是“约定优于配置”过去用 Spring MVC 搭建一个 Web 项目要手动配置 web.xml、Spring 容器、数据源、事务管理器光配置就能劝退一批新手而 Spring Boot 通过自动配置和起步依赖让开发者只需要关注业务代码。在这个音乐系统里Spring Boot 承担的角色是整个后端服务的基础框架。它负责接收前端或接口调试工具发来的 HTTP 请求通过 Controller 层分发请求到对应的业务逻辑通过 Service 层处理业务规则调用数据库操作通过 Mapper/Repository 层访问 MySQL 中的数据统一处理异常、参数校验、文件上传等横切逻辑。一句话理解前端只负责“显示和交互”Spring Boot 负责“接收请求、处理业务、返回数据”。你写的核心代码基本上都跑在这条链路上。2.2 “网易云音乐系统”不是要复刻整个 App很多人一开始容易误解以为这个项目要做成一个能听歌、能看 MV、能推荐歌曲的完整 App。实际上这类教程项目通常定位在“后台管理系统 基础接口能力”上。它的目标是实现音乐平台的后端核心能力比如用户注册登录歌手和歌曲信息的管理歌单创建、收藏、查询评论功能管理员对内容进行维护。“网易云”在这里更像是一个业务场景标签真正锻炼的是你处理真实业务数据的能力。面试时能把这个项目讲清楚比项目名字叫什么都重要。2.3 项目应该具备的核心功能模块从业务建模的角度一个音乐系统至少会拆出下面这些模块。模块核心功能涉及的技术点用户模块注册、登录、用户信息维护参数校验、密码加密、Token/会话歌手模块歌手新增、修改、查询、删除分页查询、文件上传歌曲模块歌曲信息维护、歌曲列表搜索、分类、排序歌单模块创建歌单、收藏歌单、歌曲加入歌单多表关联、事务处理评论模块歌曲或歌单评论、评论列表一对多关联、分页管理员模块后台登录、数据统计拦截器、权限控制这些模块不是标准答案具体划分要以你拿到的源码为准。但无论项目怎么设计后端核心都不外乎实体类、Mapper 接口、Service 业务逻辑、Controller 接口四层。理解了这一条主线后面看任何 Spring Boot 项目都会轻松很多。3. 环境准备与前置条件在开始构建和运行项目前先把环境准备好。3.1 开发环境清单工具说明JDK 1.8 或更高建议 JDK 8 或 JDK 11多数教程项目以 JDK 8 为准IntelliJ IDEA推荐官方版社区版也足够做这类教学项目Maven用于依赖管理和项目构建IDEA 自带 Maven 插件MySQL推荐 5.7 或 8.0项目需要初始化数据库脚本Navicat / MySQL Workbench用于查看和管理数据库二选一即可Postman / Apifox用于接口测试也可以直接用浏览器或 curl这里提醒一点IDEA 建议从官网下载不要使用任何来路不明的破解工具。对于学生党可以使用社区版也可以通过版权渠道申请教育授权没有必要因为一个开发工具给电脑引入安全风险。3.2 验证环境是否就绪打开命令行依次执行下面的命令java -version mvn -version mysql --version理想输出分别是类似这样的结果java version 1.8.0_202 // 或更高版本 Apache Maven 3.8.x mysql Ver 8.0.x只要命令能显示出版本号说明基础环境没有大问题。如果java或mvn提示找不到命令需要先配置环境变量这一步就不在本文展开了网上有大量官方环境配置教程。3.3 创建数据库使用 MySQL 客户端执行下面的命令创建一个业务数据库并导入项目自带的 SQL 脚本。CREATE DATABASE IF NOT EXISTS music DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE music; SOURCE /你的路径/music.sql;注意SOURCE后面要用项目包里的 SQL 文件真实路径。不同版本的 MySQL 对字符集和时区的要求略有不同如果你用的是 MySQL 8.0建议在连接参数中加上serverTimezoneAsia/Shanghai。4. 项目结构拆解与核心流程4.1 后端目录结构拿到源码后先在 IDEA 中打开项目你会看到类似下面的结构src/main/java/com/example/music ├── MusicApplication.java // Spring Boot 启动类 ├── controller/ // 接口层 ├── service/ // 业务逻辑层 │ └── impl/ // 业务实现类 ├── mapper/ // 数据库操作层 ├── entity/ // 实体类 ├── config/ // 全局配置跨域、拦截器等 ├── common/ // 统一返回结果、异常处理、工具类 src/main/resources ├── application.yml // 核心配置文件 ├── mapper/ // MyBatis XML 文件如使用 XML src/test/java // 测试类这个结构是 Spring Boot 三/四层架构的标准缩略版。不要被目录数量吓到它的核心链路只有一条Controller接收请求→ Service处理业务→ Mapper操作数据库→ MySQL4.2 核心流程之一用户登录登录在几乎所有 Web 系统中都是最基础的功能也是面试常问的流程。以这个音乐系统为例用户登录的核心链路如下前端把用户名和密码提交到/api/user/login接口Controller 接收参数调用 UserService 的登录方法Service 从数据库查出用户记录比对密码验证通过后生成一个登录凭证可能是 Token也可能是 Session返回用户信息和凭证给前端。写代码之前先想清楚这个流程比直接复制代码更有用。因为面试时考察的就是你能不能把这个过程讲清楚。4.3 核心流程之二歌曲和歌单维护音乐系统区别于博客系统的关键点是它有实体关联关系一个歌手可以有多首歌曲一首歌曲可以出现在多个歌单中一个用户可以收藏多个歌单。这种“多对多”关系在数据库里通常通过中间表来实现。设计思路如下user表保存用户基本资料singer表保存歌手信息song表保存歌曲信息通过singer_id关联歌手song_list表保存歌单信息通过user_id关联创建者song_list_song表中间表字段包含song_list_id和song_id记录歌单里的歌曲user_favorite表用户收藏歌单的关系表。理解这些表的关系是理解整个项目代码的前提。如果你拿到的源码里表结构不同不要慌按源码里的 SQL 脚本和国际惯例去理解即可。5. 完整示例与代码实现到目前为止你已经理解了项目核心。这一节给出关键代码示例演示一个 Spring Boot 项目的标准写法。以下代码是教学演示代码具体类名和包名以你下载的源码为准。5.1 核心依赖配置pom.xml 片段!-- 文件路径pom.xml -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies !-- Web 开发支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus 持久层框架 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- Lombok 简化实体类 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这里用的 Spring Boot 2.7.18 是一个相对稳定的版本适合教学和毕业设计。如果你从源码里看到的版本不同优先使用源码中的版本因为不同版本之间的配置项可能存在差异。5.2 核心配置文件application.yml# 文件路径src/main/resources/application.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/music?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto配置说明server.port是后端服务端口默认 8080如果被占用可以改掉spring.datasource.url中的music是数据库名需要和前面创建的数据库保持一致map-underscore-to-camel-case开启后数据库字段create_time可以自动映射到 Java 属性createTimemultipart配置用于后续文件上传限制单文件大小为 10MB。5.3 实体类示例User.java// 文件路径src/main/java/com/example/music/entity/User.java package com.example.music.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; Data TableName(user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String password; private String nickname; private String avatar; private LocalDateTime createTime; }Data是 Lombok 的注解自动生成 getter/setter/toString 等方法能省掉大量样板代码。使用 Lombok 时IDEA 需要安装 Lombok 插件。新版本 IDEA 基本都内置了支持如果你在编译时遇到 Lombok 相关报错先检查插件是否启用。5.4 统一返回结果Result.java接口返回结构统一是后端工程化的基本要求。下面是一个最简单的返回结果封装。// 文件路径src/main/java/com/example/music/common/Result.java package com.example.music.common; import lombok.Data; Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }5.5 控制层接口示例AuthController.java// 文件路径src/main/java/com/example/music/controller/AuthController.java package com.example.music.controller; import com.example.music.common.Result; import com.example.music.entity.User; import com.example.music.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; RestController RequestMapping(/api/auth) public class AuthController { Autowired private UserService userService; PostMapping(/register) public Result? register(RequestBody User user) { userService.register(user); return Result.success(null); } PostMapping(/login) public ResultMapString, Object login(RequestBody User user) { User loginUser userService.login(user.getUsername(), user.getPassword()); MapString, Object data new HashMap(); data.put(user, loginUser); return Result.success(data); } }5.6 业务层与实现类UserService 和 UserServiceImpl.java// 文件路径src/main/java/com/example/music/service/UserService.java package com.example.music.service; import com.example.music.entity.User; public interface UserService { void register(User user); User login(String username, String password); }// 文件路径src/main/java/com/example/music/service/impl/UserServiceImpl.java package com.example.music.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.example.music.entity.User; import com.example.music.mapper.UserMapper; import com.example.music.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; import java.nio.charset.StandardCharsets; Service public class UserServiceImpl implements UserService { Autowired private UserMapper userMapper; Override public void register(User user) { // 演示代码实际项目中需要做用户名唯一校验、密码强度校验 String md5Password DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8)); user.setPassword(md5Password); userMapper.insert(user); } Override public User login(String username, String password) { LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); wrapper.eq(User::getUsername, username); User user userMapper.selectOne(wrapper); if (user null) { throw new RuntimeException(用户不存在); } String md5Password DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)); if (!md5Password.equals(user.getPassword())) { throw new RuntimeException(用户名或密码错误); } return user; } }注意这里用 MD5 对密码做加密只是为了演示一个最小闭环。真实项目中MD5 不够安全推荐使用 BCrypt 等加盐哈希算法后面“最佳实践”会再强调。5.7 文件上传示例FileController.java音乐系统肯定会涉及图片上传例如用户头像、歌曲封面、歌手写真等。下面是一个极简文件上传 Controller。// 文件路径src/main/java/com/example/music/controller/FileController.java package com.example.music.controller; import com.example.music.common.Result; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.util.UUID; RestController RequestMapping(/api/file) public class FileController { PostMapping(/upload) public ResultString upload(RequestParam(file) MultipartFile file) { if (file.isEmpty()) { return Result.error(上传文件为空); } String originalFilename file.getOriginalFilename(); String suffix ; if (originalFilename ! null originalFilename.contains(.)) { suffix originalFilename.substring(originalFilename.lastIndexOf(.)); } // 生成新文件名避免中文和重名问题 String newFileName UUID.randomUUID().toString().replace(-, ) suffix; // 实际项目中请把路径配置到配置文件里不要写死 String uploadDir D:/upload/; File dest new File(uploadDir newFileName); try { file.transferTo(dest); return Result.success(newFileName); } catch (IOException e) { e.printStackTrace(); return Result.error(上传失败); } } }这个文件上传示例解决了一个很容易被忽略的问题用户上传的文件名可能是中文也可能是../../xxx.jpg这样的恶意路径。使用 UUID 生成新文件名既能避免重名覆盖又能避免部分路径穿越风险。当然更稳妥的方式是把文件上传到云存储而不是本地磁盘。6. 运行结果与效果验证6.1 启动项目在 IDEA 中打开项目后找到启动类MusicApplication.java右键点击选择 Run。如果一切正常控制台会输出 Spring Boot 启动日志最后能看到Tomcat started on port(s): 8080 (http) with context path Started MusicApplication in 5.31 seconds表示项目启动成功。6.2 测试注册和登录接口启动项目后使用命令行 curl 测试接口是最快、最不影响桌面环境的方式。测试注册接口curl -X POST http://localhost:8080/api/auth/register \ -H Content-Type: application/json \ -d {username:testuser,password:123456,nickname:测试用户}预期返回{code:200,message:操作成功,data:null}测试登录接口curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:testuser,password:123456}预期返回{code:200,message:操作成功,data:{user:{id:1,username:testuser,nickname:测试用户}}}6.3 如何判断项目可用一个 Spring Boot 后端项目能不能算“跑通”可以从三个维度判断数据库表是否完整打开数据库工具确认user、song、singer等关键表已经导入接口是否可用注册、登录接口能正常返回统一结构的数据数据是否能落库注册成功后去数据库里查user表应能查到刚才注册的用户记录。如果第三步失败说明程序虽然不报错但业务链路有问题需要马上检查 Mapper 是否正确、数据库连接是否指向正确库。7. 常见问题与排查方法项目在运行时最容易遇到下面这些问题我按“现象—原因—排查方式—解决方案”整理成了表格方便你收藏备用。问题现象可能原因排查方式解决方案项目启动报端口被占用8080 端口已被其他程序占用查看启动日志中的端口异常命令行执行netstat -ano查看占用进程修改application.yml的server.port或关掉占用端口的进程启动报数据库连接失败数据库未启动、库名不一致、密码错误检查 MySQL 服务是否启动确认application.yml中的数据库名、用户名、密码用 Navicat 能连上数据库后再启动项目Maven 依赖下载非常慢使用的是 Maven 中央仓库查看 IDEA 的 Maven 设置在settings.xml中配置阿里云镜像等国内镜像源中文乱码数据库连接参数未指定 utf8mb4或项目文件编码不对检查application.yml的 URL确认 IDEA 右下角编码设置URL 添加characterEncodingutf8统一 IDEA 编码为 UTF-8Lombok 报错不生成 getter/setterIDEA 没有启用注解处理或缺少 Lombok 插件检查编译报错信息查看 Settings - Build - Compiler - Annotation Processors勾选 Enable annotation processing安装 Lombok 插件跨域问题前端调不通接口前后端分离项目没有配置跨域打开浏览器开发者工具查看请求是否被 CORS 拦截在后端增加跨域配置类或使用CrossOrigin文件上传后访问不到图片上传路径和静态资源映射不一致查看文件实际保存路径确认浏览器访问的 URL配置资源映射目录或改用云存储如果你的问题不在表里教大家一个通用的排查顺序先看控制台启动日志有没有红色报错定位是配置问题还是代码问题再看数据库是否能连通最后才考虑是不是代码逻辑写错。绝大多数问题都出在前两步。8. 最佳实践与工程建议一个项目能跑起来和能在简历上站得住脚是两回事。下面这些工程建议能帮你把这个项目从“能运行”提升到“值得写进简历”。8.1 统一返回结构和全局异常处理这个项目已经给出了Result统一返回结构这是很好的开端。但光有它还不够建议你再补一个全局异常处理器用RestControllerAdvice处理业务异常。这样 Controller 里不需要到处写try-catch代码会干净很多。你在面试时可以主动讲这个点它体现的是工程化思维。8.2 密码存储不要用明文和简单 MD5源码教学里为了演示方便可能使用 MD5。但在实际项目中密码必须使用 BCrypt、SCrypt 等加盐哈希算法必要的话还要加入登录失败次数限制。把密码做 MD5 后直接把密文放数据库已经属于不安全做法。你可以在简历和面试中强调“使用 BCrypt 加密存储”这是一个加分点。8.3 文件上传和静态资源映射文件上传尽量不要写到本地磁盘。如果你只是做毕设本地存储可以接受但要注意三点路径不要写死在代码里要放到配置文件中要对文件类型做白名单校验访问上传文件时要配置静态资源映射而不是开放所有目录。如果有余力建议把文件上传接入云存储或云对象存储这是简历里的另一个含金量点。8.4 数据库脚本和初始化数据好的项目必须提供完整的数据库脚本包括建库语句、建表语句和测试数据。这不仅是项目可运行的前提也是团队协作的基本规范。在做毕设时建议把music.sql放到项目根目录的sql文件夹并在 README 中写清楚导入步骤。面试官拿到项目后第一件事通常就是导入数据库、启动项目。这一步不顺后面再好的功能也展示不出来。8.5 日志规范项目里不要只靠System.out.println()打印信息建议使用 SLF4J 的 Logger 输出关键业务日志至少包含用户登录成功/失败、文件上传成功/失败、数据库操作异常。日志级别可以灵活调整本地调试用DEBUG生产环境用INFO或WARN。8.6 简历上怎么描述这个项目写简历时不要只写“开发了一个音乐系统”建议用项目背景 技术栈 核心职责 项目亮点的结构项目背景设计并实现一个音乐管理系统包含用户、歌手、歌曲、歌单、评论等核心模块技术栈Spring Boot、MyBatis-Plus、MySQL、IDEA、Maven核心职责负责后端接口设计、数据库表设计、登录注册模块、歌曲歌单模块开发实现了统一返回结果和全局异常处理使用 BCrypt 对用户密码做加密存储项目亮点通过分页查询解决歌曲列表大数据量问题通过文件上传模块完成歌曲封面图片管理使用 Postman 完成接口测试与调试。这个描述方式比“本人负责了 XX 系统的开发”要具体得多。8.7 不要无脑复制网上的源码网上流传的同类源码质量参差不齐。有些项目的代码里存在明显漏洞比如 SQL 拼接注入、明文密码、select *全表查询。拿到源码后建议先通读一遍关键代码再改成自己的工程风格。这既是对自己负责也能让你在面试时真的讲得清楚。别人问“这个接口怎么写的”你至少能说出它的实现思路。9. 总结与后续学习方向到这里这个基于 Spring Boot 的网易云音乐系统从业务定位、环境准备、项目结构、核心代码到运行验证和常见问题已经完整过了一遍。你现在应该能回答几个关键问题后端项目为什么分层登录注册接口的流程是什么歌曲和歌单之间是什么关系项目启动失败第一步查哪里这些问题的答案比单纯“跑通项目”更有价值。如果你还没有动手我的建议是先拿到项目源码照着本文第 3 到第 6 节的步骤把项目从环境配置到接口测试完整跑通跑通之后再回到第 4 节的表结构自己画一遍核心模块之间的关系图。做到这一步这个项目就可以作为你面试时最有底气的一段项目经历了。下一步如果想继续精进方向很清晰一是给项目加 Redis 缓存把热门歌单和用户会话缓存起来二是给系统加权限控制区分普通用户和管理员三是把项目部署到服务器或 Docker让面试官能直接访问演示地址四是把本地文件上传改为云存储。每完成一项这个项目的含金量都会再上一个台阶。希望这篇文章能帮你少走弯路。如果你也正在做 Java 毕设或准备面试建议收藏这篇文章操作的时候随时回来看。