Vue + SpringBoot + MyBatis 音乐网站

一、前言

本文梳理音乐网站从设计到落地的完整思路,重点讲清数据如何从数据库一步步流到前端页面。先建立整体流程感,再逐个击破接口、组件等细节——无论是照着做一遍,还是迁移到其他项目,都会顺手得多。

项目源码:github.com/Yin-Hongwei/music-website

音乐网站效果预览

二、项目架构

仓库分为三个子项目:前台 music-client管理端 music-manage后端 API music-server。前台与管理端均为 Vue 应用,通过 Axios 调用 Spring Boot 接口;后端按 Controller → Service → Mapper 分层访问 MySQL。结合下图,后文将从数据库到页面,沿这条链路逐层拆解。


三、开发环境

本节只列从零起步所需的基本开发环境,先把后端、数据库和前端跑通即可。随着项目后期功能增多、架构变复杂,Redis 等依赖再按需引入。

环境 版本
操作系统 macOS
JDK 8+(如 jdk-8u141)
MySQL 8.0+
Node.js 14+
IDE IntelliJ IDEA / VS Code

四、后端项目开发

1、创建数据库表

开发后端之前,先把业务里要持久化的东西想清楚。音乐网站至少需要这几类数据:

  • 用户侧:前台用户(user)、后台管理员(admin
  • 内容侧:歌手(singer)、歌曲(song)、歌单(song_sheet
  • 展示侧:首页轮播(banner
  • 互动侧:收藏(collect)、评论(comment

表之间的关系可以这样理解:

  • 歌手和歌曲是一对多:一首歌对应一个歌手,所以在 song 里放 singer_id
  • 歌单和歌曲是多对多:一首歌可以出现在多个歌单里,所以用 song_sheet_song 做中间表
  • 收藏和评论都可能是针对「歌曲」或「歌单」,所以在 collectcomment 里同时保留 song_idsong_sheet_id,再用 type 区分目标类型

音乐网站数据库表结构(图有点旧了)

2、创建项目

在 IntelliJ IDEA 中新建项目,选择 Spring Initializr,并按需勾选依赖:Web → Spring Web Starter,以及 SQL → MySQL DriverMyBatis Framework。一路 Next 完成后,将得到如下项目结构(典型三层架构):

Spring Initializr 创建后的项目结构

3、配置文件

3.1、application.properties

src/main/resources/application.properties 中配置数据库连接,起步阶段只需以下几项:

# 服务端口
server.port=8888

# 数据源(按本地 MySQL 账号密码修改)
spring.datasource.url=jdbc:mysql://localhost:3306/yin_music?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.typeAliasesPackage=com.example.demo.domain

3.2、generatorConfig.xml

MyBatis Generator(MBG) 可以根据已有数据库表,自动生成 实体类Mapper 接口Mapper.xml。项目起步阶段通常只需执行一次;生成完成后,建议在 pom.xml 里注释掉 MBG 插件,避免每次构建重复覆盖代码。

src/main/resources 下新建 generatorConfig.xml,需要配置三块内容:

  1. JDBC 连接:库地址、账号、密码(与 application.properties 保持一致)
  2. 生成路径:实体类、Mapper 接口、Mapper.xml 分别输出到哪个包/目录
  3. 目标表:用 <table> 列出要生成的表名

示例(包名、表名按项目修改):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="default" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/yin_music?serverTimezone=Asia/Shanghai&amp;useSSL=false&amp;characterEncoding=utf8"
userId="root" password="123456"/>
<javaModelGenerator targetPackage="com.example.demo.domain" targetProject="src/main/java"/>
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/>
<javaClientGenerator targetPackage="com.example.demo.dao" targetProject="src/main/java" type="XMLMAPPER"/>
<table tableName="consumer" domainObjectName="Consumer"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"/>
<!-- 其余表同理追加 <table> -->
</context>
</generatorConfiguration>

3.3、pom.xml

pom.xml 负责两块:依赖dependencies)和 插件plugins)。用 Spring Initializr 建项目时,Web、MySQL、MyBatis 等基础依赖已经带上了,再按需补充下面几项:

依赖 说明
mybatis-spring-boot-starter MyBatis 与 Spring Boot 集成
mysql-connector-java MySQL JDBC 驱动
fastjson JSON 与 JavaBean 互转

MBG 插件配合上一节的 generatorConfig.xml,在 plugins 中加入:

<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.2</version>
<configuration>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
</configuration>
</plugin>

执行 ./mvnw mybatis-generator:generate 生成 daodomainmapper 及对应文件:

MyBatis Generator 生成代码后的项目结构

生成完成后注释掉 MBG 插件,避免后续构建重复覆盖已有代码。

4、测试(以新建一个用户为例)

MBG 生成代码后,先写一条「插入用户」的链路,确认数据库连接和 MyBatis 映射都正常。下面用 MBG 生成的 Consumer 为例,包名与第三节配置保持一致(与 项目源码 最新代码可能不同,以教程为准)。

4.1、新建 Service 层

后端采用 Controller → Service → Mapper 分层(第五节会展开)。MBG 只生成 Mapper,Service 需手动创建。这里先补上 Service,让本节测试与后续接口走同一条链路;当前 addUser 只是转发到 Mapper,等业务复杂后再在 Service 里加校验、转换等逻辑。

src/main/java/com/example/demo/service 下新建:

service/
├── ConsumerService.java
└── impl/
└── ConsumerServiceImpl.java

ConsumerService 接口 — 声明 addUser 方法:

public interface ConsumerService {
boolean addUser(Consumer consumer);
}

ConsumerService 接口代码

ConsumerServiceImpl 实现类 — 调用 Mapper 写入数据库:

@Service
public class ConsumerServiceImpl implements ConsumerService {
@Resource
private ConsumerMapper consumerMapper;

@Override
public boolean addUser(Consumer consumer) {
return consumerMapper.addUser(consumer) > 0;
}
}

ConsumerServiceImpl 实现类代码

4.2、补充 Mapper

MBG 默认会为每张表生成 insertinsertSelective 等通用 CRUD 方法(接口 + XML 已有,无需手写)。本节为与 4.1 的 addUser 命名一致,额外在接口和 XML 里补一条同名映射——SQL 复制默认生成的 insertSelective 即可,只改 id

ConsumerMapper 接口src/main/java/com/example/demo/dao/ConsumerMapper.java):

int addUser(Consumer record);

ConsumerMapper 接口 addUser 方法

ConsumerMapper.xmlsrc/main/resources/mapper/ConsumerMapper.xml)— 复制 MBG 默认生成的 insertSelective 节点,把 id 改为 addUser

<insert id="addUser" parameterType="com.example.demo.domain.Consumer">
<!-- 字段与 MBG 默认生成的 insertSelective 保持一致 -->
</insert>

ConsumerMapper.xml 中 addUser SQL 映射

4.3、启动类扫描 Mapper

MBG 生成的 Mapper 接口不会自动注册到 Spring 容器,需要在启动类上用 @MapperScan 指定扫描包(与 3.2 中 javaClientGeneratortargetPackage 一致),否则注入 ConsumerMapper 时会报找不到 Bean。

src/main/java/com/example/demo/DemoApplication.java 上添加:

@MapperScan("com.example.demo.dao")
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

4.4、写单元测试

前面几步就绪后,用 @SpringBootTest 启动 Spring 容器,走一遍 Service → Mapper → 数据库 的完整链路(会连真实 MySQL,属于集成测试)。

src/test/java/com/example/demo/DemoApplicationTests.java 中编写:

@SpringBootTest
@RunWith(SpringRunner.class)
public class DemoApplicationTests {

@Resource
private ConsumerService consumerService;

@Test
public void addUser() {
Consumer consumer = new Consumer();
consumer.setUsername("yin");
consumer.setPassword("123456");
consumerService.addUser(consumer);
}
}

右键运行 addUser 测试方法:控制台无报错,且 consumer 表里能看到新记录,说明后端链路已跑通,可以开始写接口。

新建用户单元测试运行结果

5、开发接口(以返回所有用户信息为例)

第四节验证了「写入」,这里从「读取」入手,走一遍完整的 HTTP 请求链路。包名仍与第三节、第四节保持一致。

5.1、分层说明

后端按四层组织,请求自上而下、响应自下而上:

浏览器 → Controller → Service → Mapper(+ XML)→ 数据库
层次 目录 职责
实体类 domain 对应数据库表,MBG 已生成
Mapper / DAO dao + mapper 接口声明数据操作,XML 编写 SQL
Service service + impl 业务逻辑,调用 Mapper
Controller controller 接收 HTTP 请求,调用 Service 并返回结果

5.2、实战:返回所有用户

目标:访问 http://localhost:8888/allUser 拿到全部用户。按 Controller → Service → Mapper 逐层补充 allUser 方法。

Controller 层 — 监听 /allUser,交给 Service 处理:

@RestController
public class ConsumerController {
@Resource
private ConsumerService consumerService;

@RequestMapping("/allUser")
public Object allUser() {
return consumerService.allUser();
}
}

ConsumerController 返回所有用户信息

Service 层 — 接口声明方法,实现类调用 Mapper:

// ConsumerService.java
List<Consumer> allUser();

// ConsumerServiceImpl.java
@Override
public List<Consumer> allUser() {
return consumerMapper.allUser();
}

ConsumerService 接口 allUser 方法

ConsumerServiceImpl 实现 allUser 方法

Mapper 层 — 接口 + XML 查询数据库(SQL 写法与 4.2 类似,复制 MBG 默认生成的 select 并改 id 亦可):

// ConsumerMapper.java
List<Consumer> allUser();
<!-- ConsumerMapper.xml -->
<select id="allUser" resultMap="BaseResultMap">
select * from consumer
</select>

ConsumerMapper 接口 allUser 方法

ConsumerMapper.xml 中 allUser SQL 映射

启动验证 — 运行项目并在浏览器访问:

./mvnw spring-boot:run

浏览器打开 http://localhost:8888/allUser 查看 JSON 结果。

Spring Boot 项目启动日志

浏览器访问 /allUser 接口返回结果

5.3、延伸

其余接口按同样分层逐层添加方法即可。联调前端时还需注意两点:跨域(见 config 目录)和静态资源路径(歌曲、图片等文件的存放与访问)。

5.4、小结

一次 HTTP 请求的完整路径:Controller 接收请求 → Service 处理业务 → Mapper 访问数据库 → 结果沿原路返回前端

6、最终项目结构

.
├── avatorImages // 用户头像
├── img
│   ├── singerPic // 歌手图片
│   ├── songListPic // 歌单图片
│   └── songPic // 歌曲图片
├── song // 存放歌曲
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com.example.yin
│   │   │   ├── config // 配置(跨域)
│   │   │   ├── controller // 控制层,接收请求返回响应
│   │   │   ├── dao // 数据操作层
│   │   │   ├── domain // 实体类
│   │   │   ├── constant
│   │   │   ├── service
│   │   │   │ └── impl // Service 层的接口
│   │   │   └── HwMusicApplicationTests.java // 项目入口
│   │   └── resources
│   │   ├── mapper // mapper.xml文件,操作数据库
│   │   ├── static // 存放静态资源
│   │   ├── templates
│ │   ├── application.properties // 连接数据库
│ │   └── generatorConfig.xml // MyBatis Generator 自动生成代码
│   └── test
│   └── java
│   └── com.example.demo // 测试用的
├── pom.xml // 添加相关依赖和插件
└── target

五、客户端项目开发

前台项目位于仓库 music-client/,基于 Vue 3 + TypeScript + Pinia + Vue Router + Axios + Element Plus 实现。

1、创建项目

客户端的创建比后端简单,使用 Vue CLI 脚手架即可。进入 music-website 目录后,可参照如下方式初始化:

npm install -g @vue/cli

# 在 music-website 目录下创建
vue create music-client
# 建议选择:Vue 3 · TypeScript · Router · Pinia · ESLint

依赖安装完成后进入项目目录启动:

cd music-client
npm install
npm run serve

启动成功后,直接点击控制台输出的本地地址即可访问前端页面。

Vue 客户端项目运行效果

如果是新手,建议先阅读 Vue 3 官方文档,了解组件、路由、组合式 API 等基础概念后再动手。

2、开发思路

代码按职责拆分到不同目录,便于后期维护:

  • pages:页面级视图,负责路由对应的整页展示(首页、搜索、歌手、歌单、个人中心等)
  • components:可复用组件,按 auth(认证布局)、business(业务组件)、layouts(页头页脚、播放栏)、player(音频播放)分类
  • api:按模块封装后端接口(歌曲、歌手、歌单、评论、用户等),统一走 utils/request.ts 中的 Axios 实例
  • store:Pinia 状态管理,拆分 user(用户)、player(播放器)、song(当前歌曲)、search(搜索)、configure(全局 UI 状态)等模块
  • composables:抽取跨组件复用的逻辑,如导航、搜索关键词、全局操作等
  • router:路由配置,YinAppLayout.vue 作为带页头/页脚/播放栏的主布局,子路由挂载各页面

开发流程建议:先搭页面骨架(可写死数据),再通过 api 层请求后端数据;前端请求后端接口需在 vue.config.js 中通过 NODE_HOST 配置后端地址(如 http://localhost:8888);遇到跨域问题可在后端配置 CORS,或在 vue.config.js 中配置 devServer 代理。

前台组件以自研为主,第三方 UI 用得较少,适合练习组件封装与状态管理。

3、最终项目结构

music-client/
├── public/ # 静态资源(index.html 等)
├── src/
│ ├── api/ # 接口封装(auth、song、singer、songSheet、comment、user…)
│ ├── assets/ # 样式、图标、图片
│ │ ├── css/ # global.scss、var.scss 等
│ │ ├── icons/ # iconfont
│ │ └── images/
│ ├── components/
│ │ ├── auth/ # AuthLayout.vue 认证页布局
│ │ ├── business/ # Comment、SongList、CoverCardGrid 等业务组件
│ │ ├── layouts/ # YinAppLayout、YinHeader、YinFooter、YinPlayerBar、YinPlayQueue
│ │ └── player/ # YinAudio 音频播放
│ ├── composables/ # useAppActions、useNavigation、useSearchKeyword
│ ├── enums/ # 路由名、导航标签、地区等枚举
│ ├── pages/ # 页面视图
│ │ ├── auth/ # SignIn、SignUp、ForgotPassword
│ │ ├── error/ # 404
│ │ ├── home/ # Home 首页
│ │ ├── lyric/ # Lyric 歌词页
│ │ ├── search/ # Search、SearchSong、SearchSongList
│ │ ├── singer/ # Singer、SingerDetail
│ │ ├── song-sheet/ # SongSheet、SongSheetDetail
│ │ └── user/ # Personal、PersonalData、Upload、Setting、Password
│ ├── router/ # 路由配置
│ ├── store/ # Pinia:configure、player、search、song、user
│ ├── utils/ # request、validate、api-response-data
│ ├── App.vue
│ └── main.ts # 入口,注册 Pinia、Router、Element Plus
├── babel.config.js
├── package.json
├── tsconfig.json
└── vue.config.js # 构建配置,含后端 API 地址

六、管理端项目开发

后台项目位于 music-manage/,同样基于 Vue 3 + TypeScript,使用 Element Plus 快速搭建表格、表单、弹窗等管理界面,整体分层与前台一致(api / views / components / stores / router)。

1、创建与启动

cd music-manage
npm install
npm run serve

启动成功后,直接点击控制台输出的本地地址即可访问。

2、开发思路

管理端以 CRUD 为主:每个 views 页面对应一类资源(Banner、用户、歌曲、歌手、歌单、评论、收藏等),列表展示与增删改查通过 Element Plus 的 Table、Form、Dialog 等组件完成。api 层按资源拆分接口;components/layouts 提供侧边栏(YinAside)、顶栏(YinHeader)和整体布局(YinLayout);stores/app.ts 用 Pinia 管理侧边栏折叠等全局状态;mixins/mixin.ts 保留部分跨页面公共方法。

3、最终项目结构

music-manage/
├── public/
├── src/
│ ├── api/ # admin、banner、collection、comment、song、singer、songSheet、user…
│ ├── assets/ # 样式与图片
│ ├── components/
│ │ ├── dialog/ # YinDelDialog 删除确认弹窗
│ │ └── layouts/ # YinAside、YinHeader、YinLayout
│ ├── enums/
│ ├── mixins/ # mixin.ts 公共方法
│ ├── router/
│ ├── stores/ # app.ts 全局状态
│ ├── utils/ # request、emitter、routeQuery
│ ├── views/
│ │ ├── BannerPage.vue # Banner 管理
│ │ ├── CollectPage.vue # 收藏管理
│ │ ├── CommentPage.vue # 评论管理
│ │ ├── InfoPage.vue # 后台首页 / 信息概览
│ │ ├── ListSongPage.vue # 歌单-歌曲关联
│ │ ├── Login.vue # 管理员登录
│ │ ├── singer/
│ │ │ ├── SingerPage.vue # 歌手管理
│ │ │ └── SongPage.vue # 歌曲管理
│ │ ├── songSheet/
│ │ │ └── SongSheetPage.vue # 歌单管理
│ │ └── user/
│ │ └── ConsumerPage.vue # 用户管理
│ ├── App.vue
│ └── main.ts
├── babel.config.js
├── package.json
├── tsconfig.json
└── vue.config.js

七、贡献者

感谢所有为本仓库提交过代码与改进建议的贡献者。

Contributors

八、最后

希望本文对大家有所启发,如有问题欢迎交流。

微信打赏