SpringBoot+MySQL实现视频网站:上传、转码与播放实战
2026/9/16 18:05:54 网站建设 项目流程

简介:基于SpringBoot框架与MySQL数据库的视频网站设计与实现,是一套面向课程设计、毕业设计的完整Java Web项目源码,覆盖视频网站常见业务场景,并针对高并发、可扩展、运维成本等问题给出工程化实现。压缩包总大小约7.3MB,共221个文件:101个Java文件构成后端核心,25份JavaScript、16份CSS与13份HTML负责前端界面,另有XML、properties等配置,以及SQL数据库脚本、mp4演示视频和说明文档。导入IDE即可运行调试,有助于从源码层理解SpringBoot与MySQL的整合方式、分层架构、数据表设计与接口组织;SQL脚本可快速构建初始数据,演示视频能辅助梳理业务流程,对二次开发、课设答辩和论文撰写有直接帮助。已有182人学习下载,适合具备Java基础、正在规划视频类Web应用的学生与开发者。

1. 视频网站项目的真实复杂度在哪里

一个能看视频的网站,九成工作量往往不在视频本身。分类、用户、评论、播放记录这些业务模块,和普通管理系统没有本质区别;真正让基于SpringBoot+MySQL的技术方案复杂起来的是另外三件事:视频文件怎么落盘、上传后怎么变成可播放的流媒体、用户拖动进度条时后端能不能接住Range请求。标题里的SpringBoot+MySQL能撑起业务数据的骨架,却定义不了视频文件的存取链路。这篇文章沿着工程初始化、MySQL表设计、上传转码、播放响应到缓存与安全收尾的顺序展开,把这套技术栈里被反复踩的坑和可以照抄的参数一并讲清楚,适合正在做毕设、中小团队从零起步接视频业务,或者打算把旧CMS改造成视频站的开发者。

2. 用SpringBoot搭出可运行的视频网站骨架

2.1 SpringBoot版本选型:3.x 与 2.7.x 的分界线

视频网站项目的技术选型,第一件事不是选数据库,是定SpringBoot版本。SpringBoot 3.x 要求 JDK 17 起步,包名从javax.*换成了jakarta.*,连接器、AOP、验证框架的坐标全部跟着变;而 2.7.x 是 3.x 之前最后一个长期维护分支,支持 JDK 8/11。对毕业设计、课程项目和大多数存量服务器来说,JDK 8 仍然是主力环境,“SpringBoot版本太高”就是最常撞的墙:IDEA里新建项目默认拉最新版,本机却是JDK 8,启动直接报UnsupportedClassVersionError,很多人卡在这一步就以为依赖没下全。

组合JDK 要求包名适合场景
SpringBoot 3.2.xJDK 17+jakarta.*新项目、容器化部署、有升级诉求的团队
SpringBoot 2.7.xJDK 8/11javax.*存量环境、毕业设计、中小网站起步

我的建议是用 SpringBoot 2.7.x + MySQL 8.0 作为基准,原因很直接:网上可复现的资料最多,MyBatis-Plus、Shiro、JWT 这些周边库的兼容方案全;除项目本身需要JDK17特性或团队明确要上容器化,不建议在视频网站上当SpringBoot版本升级的试验田。本地如果还没装MySQL,用Docker起一个最省事:docker run --name mysql8 -e MYSQL_ROOT_PASSWORD=root -e TZ=Asia/Shanghai -p 3306:3306 -d mysql:8.0,容器内的时区参数要和后面连接串的时区保持一致。

2.2 IDEA创建SpringBoot项目后的第一份配置

IDEA创建SpringBoot项目超时是另一个高频问题,常见做法是切到阿里云镜像再初始化,但项目生成后切镜像作用就有限了。Maven 的settings.xml里配好 mirror,后续依赖下载才会稳。先看工程里最核心的依赖坐标:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> </dependencies>

这里有两个容易出错的点。第一,MyBatis-Plus 的 starter 分 boot2 和 boot3 两套包,SpringBoot 3.x 要用mybatis-plus-spring-boot3-starter,用错版本会直接报 Bean 创建失败;第二,MySQL 8 之后的驱动坐标从mysql:mysql-connector-java改成了com.mysql:mysql-connector-j,旧坐标虽然还能解析,但依赖管理里的版本号未必匹配。对应的application.yml里,数据源配置是视频网站项目启动的关键:

spring: datasource: url: jdbc:mysql://localhost:3306/video_site?useUnicode=true&characterEncoding=utf8&serverTimeZone=Asia/Shanghai&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 20MB max-request-size: 200MB

连接串里的serverTimeZone=Asia/Shanghai和后端hibernate/jdbc里的时区设置配套,否则用NOW()写入的发布时间会和本地差 8 小时。max-file-size控制单文件上限,max-request-size控制单次请求总大小,这两个参数在后面的分片上传里要重新核对:如果单片文件 10MB,max-request-size至少要大于单片大小加上表单字段的开销,设成 200MB 是为了给多文件或异常重试留余量。

2.3 连上MySQL:驱动类名、时区与字符集

视频网站项目里,MySQL 安装配置完成后最常见的启动报错有两类:一是驱动类找不到,二是时区错误。驱动类名在 MySQL 8 下必须写com.mysql.cj.jdbc.Driver,老写法com.mysql.jdbc.Driver在 8.0 版本里已经被移除;时区错误会直接抛The server time zone value ... is unrecognized,解决方法就是上面 yml 里serverTimeZone=Asia/Shanghai那种写法。

字符集方面,建库语句建议直接把默认字符集钉死:

CREATE DATABASE IF NOT EXISTS video_site DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;

utf8mb4 和 utf8 的差别,核心是能不能存表情符号,以及避免生僻字在评论、视频标题里变成问号。评论表、视频表、用户表一律用 utf8mb4,索引列的长度计算也要按 utf8mb4 重新算;比如VARCHAR(120)在 utf8mb4 下最大能建索引的长度会比 utf8 短,别等建完表再回头改字段。

3. 视频网站的MySQL表设计:从建表到索引

3.1 视频业务的核心表:video、comment、play_record

视频网站的表设计,第一原则是“文件与业务分离”。video 表只存元数据和文件路径,不存二进制内容;转码后的切片文件落在磁盘目录里,数据库里只留一个 m3u8 或分片目录的引用。视频资源表的核心结构长这样:

CREATE TABLE video ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, title VARCHAR(120) NOT NULL, description TEXT, cover_url VARCHAR(255), category_id INT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, raw_path VARCHAR(255) NOT NULL COMMENT '原始文件路径', hls_dir VARCHAR(255) COMMENT '转码后HLS目录', duration BIGINT COMMENT '时长(毫秒)', size BIGINT COMMENT '文件大小(字节)', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0上传中 1转码中 2已发布 3已下线', hot_score INT NOT NULL DEFAULT 0 COMMENT '热度分', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

status 字段用 TINYINT 而不是字符串枚举,存储开销小是一方面,更重要的是状态流转能在 Java 层用常量类约束,不会出现“published”和“Published”这种脏数据。hot_score 是冗余字段,专门服务于首页排序,由定时任务根据播放量、评论数、发布时间加权算出,避免每次列表查询都去聚合大表。

配套的评论表和播放记录表可以精简一些:

CREATE TABLE comment ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, video_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, content VARCHAR(500) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_video_created (video_id, created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE play_record ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT UNSIGNED NOT NULL, video_id BIGINT UNSIGNED NOT NULL, progress_sec INT UNSIGNED NOT NULL DEFAULT 0, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_video (user_id, video_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

play_record 用唯一键(user_id, video_id)约束“一个用户对一部视频只有一条进度”,每次观看更新 progress_sec,这个设计在续播功能里非常省事,不需要先查再插。这个表会比评论表大很多,因为每次拖动进度条都可能触发更新,后面章节会讲专门的索引策略。

3.2 列表查询的MySQL排序与索引设计

视频网站的列表页基本都长一个样子:按分类筛、按时间或热度排序、分页取 20 条。这类查询写起来不难,难的是在数据量上来后索引还走得上。先看最典型的一条:

SELECT id, title, cover_url, duration, hot_score FROM video WHERE status = 2 AND category_id = 5 ORDER BY hot_score DESC, created_at DESC LIMIT 20;

分析这条SQL要回答两个问题:筛选条件能不能用上索引,排序能不能避免 filesort。给表加上联合索引,注意字段顺序不能乱:

ALTER TABLE video ADD INDEX idx_category_status_hot (category_id, status, hot_score);

联合索引的最左前缀原则决定了范围查询字段要放在等值查询字段后面。这里 category_id 和 status 都是等值条件,hot_score 是排序字段,所以把 hot_score 放在索引的最后一列;当索引覆盖了排序字段,MySQL 可以直接按索引顺序取数据,Using filesort 就不会出现。如果反过来,把 status 写在 category_id 前面,查询条件里只有 status 时索引也能用,但 category_id 和 hot_score 的组合路径就断了。

另一个高频坑是隐式类型转换导致索引失效。video 表里 user_id 是 BIGINT,如果Java侧把参数当字符串传进去,MySQL 会做一次类型转换,索引就失效了。还有对时间列套函数,比如WHERE DATE(created_at) = '2024-01-01',这会让 created_at 上的索引完全用不上,正确写法是created_at >= '2024-01-01' AND created_at < '2024-01-02'。查询是否走索引,用EXPLAINkey字段和rows字段,rows远大于实际返回行数时基本可以判断索引没生效。

3.3 MySQL存储过程与Event在视频网站里的取舍

视频网站项目里要不要用存储过程,我的判断是:不要为了“数据库能搞定”就把业务逻辑塞进存储过程。上传转码的流程里有文件操作、回调通知、状态流转,这些逻辑跨了文件系统和数据库两个域,存储过程只能处理其中一半;而且一旦业务规则变化,Java 代码可以改完立即重启,存储过程的版本管理却经常形同虚设。

但有一个场景适合交给数据库自带的 Event,就是定时清理。上传分片时如果用户中途放弃,chunk 目录会残留一堆.part文件,数据库里的分片记录也永远停留在一半状态。用 MySQL Event 每小时扫一次很顺手:

SET GLOBAL event_scheduler = ON; CREATE EVENT clean_expired_chunks ON SCHEDULE EVERY 1 HOUR DO DELETE FROM video_upload WHERE updated_at < NOW() - INTERVAL 1 DAY AND status = 0;

注意 Event 需要数据库有 EVENT 权限,云数据库上有时默认关闭 event_scheduler,要先去控制台开启。日志记录、任务调度这类功能,把时间周期写进 Java 侧的定时任务里更利于统一监控;只有清理类、报表预聚合类操作,我会倾向于放进 MySQL Event。

4. 视频上传、转码与存储:SpringBoot里的文件链路

4.1 三种存储方式:本地磁盘、NFS与对象存储

视频文件的落盘方式直接决定了网站能扛多大并发、是否需要多机部署。刚开始做视频站,最常见的是直接存本地磁盘,缺点是应用扩容后文件不在同一台机器上,负载均衡后面的节点互相看不到对方的文件。NFS 能解决多机共享目录,但NAS本身的吞吐会成为瓶颈,而且挂了就是全局故障。对象存储(比如阿里云OSS、腾讯云COS)是视频网站最稳妥的选项,成本高一点,换来的是 CDN 分发、断点续传、不用自己维护文件集群。

存储方式实现成本扩展性可靠性适用场景
本地磁盘最低单机、毕设、内网演示
NFS中低多机共享、无强制高可用
对象存储线上业务、移动端+Web端

如果只是做毕业设计或本地跑通流程,本地磁盘完全够用,代码里用application.yml的配置项切换根目录就行:

video: storage: root-dir: D:/video-site/data # 本地目录,可改成 /data/video-site

把存储路径做成配置项而不是硬编码,等需要切换到NFS或对象存储时,只需要把该路径替换为挂载点,业务代码基本不用动。

4.2 分片上传接口与合并落盘

视频文件动辄几百 MB,直接一个 POST 请求传完,中间断一次就前功尽弃。分片上传的常见做法是前端把文件切成固定大小(比如 10MB)的块,每块独立上传,后端全部收齐后按序号合并。后端接口核心只有两个,一个是接收分片,一个是合并触发。分片接收接口这样写:

@PostMapping("/upload/chunk") public String uploadChunk(@RequestParam("file") MultipartFile file, @RequestParam("identifier") String identifier, @RequestParam("chunkIndex") int chunkIndex, @RequestParam("totalChunks") int totalChunks) throws IOException { Path chunkDir = Paths.get(uploadRootDir, identifier); if (Files.notExists(chunkDir)) { Files.createDirectories(chunkDir); } file.transferTo(chunkDir.resolve(identifier + "_" + chunkIndex + ".part")); if (uploadService.isAllChunksUploaded(identifier, totalChunks)) { uploadService.mergeChunks(identifier, totalChunks); } return "ok"; }

identifier 建议用 UUID 或时间戳加用户ID生成,避免多用户同时上传同名文件时互相覆盖。chunkIndex 从 0 开始计数,totalChunks 由前端根据文件总大小和分片大小算好一起传,后端不猜分片数量。isAllChunksUploaded遍历目录统计.part文件数量,等于 totalChunks 才触发合并,数量不够就返回,等最后一个分片到位再合并。

合并落盘的时候,用 FileChannel 顺序写比流式拼接更稳:

public void mergeChunks(String identifier, int totalChunks) throws IOException { Path chunkDir = Paths.get(uploadRootDir, identifier); Path mergedPath = Paths.get(uploadRootDir, identifier + ".mp4"); try (FileChannel out = FileChannel.open(mergedPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) { for (int i = 0; i < totalChunks; i++) { Path part = chunkDir.resolve(identifier + "_" + i + ".part"); try (FileChannel in = FileChannel.open(part, StandardOpenOption.READ)) { long position = 0; long size = in.size(); while (position < size) { position += in.transferTo(position, size - position, out); } } } } // 合并完成后清理分片目录,避免磁盘堆积 }

transferTo在高版本 JDK 上会走零拷贝优化,比read/write循环效率高。合并后一定要清理分片目录,否则传了一半的文件会一直占着磁盘;清理动作也可以放在定时任务里兜底。

4.3 FFmpeg转码与异步状态流转

原始视频直接拿来在线播放,兼容性很差,尤其是移动端浏览器对 MP4 编码格式很挑剔。通用做法是用 FFmpeg 转成 H.264 + AAC 编码,再切成 HLS 切片,输出 m3u8 播放列表。后端不需要引入Java库,直接调系统命令:

String[] cmd = { "/usr/bin/ffmpeg", "-y", "-i", sourcePath, "-c:v", "libx264", "-preset", "veryfast", "-c:a", "aac", "-hls_time", "10", "-hls_list_size", "0", "-hls_segment_filename", hlsDir + "/segment_%03d.ts", hlsDir + "/index.m3u8" }; Process process = new ProcessBuilder(cmd).redirectErrorStream(true).start(); process.waitFor();

-hls_time 10表示每个切片 10 秒,-hls_list_size 0表示播放列表里保留所有切片,不生成滚动窗口。直播场景才需要列表滚动,点播场景这个参数务必设成 0,否则用户播放到一半刷新页面,旧切片已经从列表里消失了。-preset veryfast牺牲一点压缩率换转码速度,视频网站对首播速度的敏感度远高于存储成本。

转码是耗时长、失败率高的操作,绝不能放在上传请求的线程里同步执行。没有消息队列时,用@Async+ 线程池就能把状态流转做起来:上传合并完成先把 video 表 status 置为“转码中”,异步任务转码成功后更新为“已发布”,失败则把错误信息记到 log 表并置为“已下线”。定时任务扫描转码中超过2小时的记录,补一次告警或重试,这样链路才闭环。

5. 视频播放的HTTP Range与签名防盗链

5.1 拖进度条背后的Range请求处理

视频播放器拖进度条,本质是向服务器发起一个带 Range 头的 HTTP 请求,告诉服务器“我只要这个文件的第 N 到第 M 个字节”。SpringBoot 的静态资源处理器本身支持 Range,但走自定义存储路径或需要鉴权时,还是得自己处理。接口示例:

@GetMapping("/video/stream/{resourceId}") public void stream(@PathVariable Long resourceId, @RequestHeader(value = "Range", required = false) String range, HttpServletResponse response) throws IOException { VideoResource resource = resourceService.getById(resourceId); Path file = Paths.get(resource.getPath()); long fileSize = Files.size(file); long start = 0; long end = fileSize - 1; if (range != null && range.startsWith("bytes=")) { String[] parts = range.replace("bytes=", "").split("-"); start = Long.parseLong(parts[0]); if (parts.length > 1 && !parts[1].isEmpty()) { end = Math.min(Long.parseLong(parts[1]), fileSize - 1); } } response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize); response.setContentType("video/mp4"); try (RandomAccessFile raf = new RandomAccessFile(file.toFile(), "r"); OutputStream out = response.getOutputStream()) { raf.seek(start); byte[] buffer = new byte[64 * 1024]; long remaining = end - start + 1; int len; while (remaining > 0 && (len = raf.read(buffer, 0, (int) Math.min(buffer.length, remaining))) != -1) { out.write(buffer, 0, len); remaining -= len; } } }

核心点有两个:无 Range 时返回完整文件,状态码 200;有 Range 时返回 206 Partial Content,并带Content-Range头,格式是“起始字节-结束字节/总字节”。输出用 64KB 缓冲区边读边写,避免把整个视频加载进内存;remaining控制只写出当前 Range 需要的字节,读多了播放器也不会用,反而浪费带宽。

如果忽略 Range 直接返回 200 全量文件,播放器多半也能播,但拖动一次就重新下整个文件,流量开销和缓冲等待都会让用户体验很差。这个实现里没有用 Spring 的ResourceRegion,是为了把 Range 解析逻辑完全显式化,方便后续加 Token 校验。

5.2 签名播放URL与防盗链校验

视频网站的防盗链,常见做法是校验 Referer,但这个方案太弱,Referer 在浏览器端可以被任意伪造,CSP 里也能被关掉。正确做法是生成带过期时间的签名 URL,播放地址由后端动态签发,URL 里带过期时间和签名,播放器拿到的是一段“临时有效”的地址。签名算法可以很简单:

public static String buildSign(String path, long expireAtSeconds, String secret) { String raw = path + "|" + expireAtSeconds + "|" + secret; return DigestUtils.md5Hex(raw); }

校验时先判过期,再算签名,顺序不要反过来:

if (expireAtSeconds < System.currentTimeMillis() / 1000) { throw new ExpiredUrlException("播放地址已过期"); } String expected = buildSign(path, expireAtSeconds, secret); if (!expected.equals(signFromUrl)) { throw new InvalidSignException("签名校验失败"); }

expireAtSeconds 用 Unix 秒级时间戳,生成 URL 时取System.currentTimeMillis() / 1000 + 3600表示一小时后过期。密钥不要写死在代码里,放到环境变量或配置中心;生产环境把 MD5 换成 HmacSHA256,密钥管理要复杂一截,安全性也上一个台阶。签名 URL 还可以带上用户ID和IP,后续做更细的权限控制。

前端拿到的是类似/video/stream/1024?expire=1735689600&sign=1a2b3c...的地址,这个地址只在有效期内可用,别人复制出去也会过期,比 Referer 校验靠谱得多。

5.3 签名过期时间的单位坑

这个坑特别隐蔽:签名 URL 生成时经常有人用System.currentTimeMillis()取毫秒,校验时又用System.currentTimeMillis() / 1000取秒,两边对不上,导致“刚生成的链接立刻过期”。统一规则是传输和比较都用秒级时间戳,生成时除 1000,校验时也除 1000,签名原文里拼接的也是秒级值,这样 Java、Nginx、前端JS 计算时间戳时不会出现单位错位。

另外一个常见问题是 HLS 播放时,m3u8 里的每个 ts 切片地址是相对路径,如果签名只签了 m3u8 地址,切片请求没有签名,盗链者拿到 m3u8 文件后可以直接下载切片。处理办法是切片地址改走同一个鉴权入口,或者把 m3u8 内容动态改写,给每个切片段拼接上同样的签名参数。小规模网站用动态改写简单直接,量大以后就考虑 Nginx 层的鉴权模块,但核心思路没变:每个资源请求都要过校验。

6. 缓存、深分页与SpringBoot安全收尾

6.1 视频列表热点缓存:缓存什么、缓存多久

视频列表页的并发远高于播放接口本身,首页前几页的数据更是热点中的热点。用 Redis 做二级缓存是低成本方案,直接用 Spring Cache 注解就能接上:

@Cacheable(cacheNames = "video:list", key = "#categoryId + ':' + #page") public List<VideoVO> listByCategory(Long categoryId, Integer page) { return videoMapper.selectPaged(categoryId, page); }

缓存时间不要设太长,5 到 10 分钟合适。视频状态变更时调用@CacheEvict清掉对应页,避免新上架视频迟迟不出现。还有版本号兜底方案:列表缓存 key 里带上version,上下架操作就version + 1,所有页全部失效,适合需要强一致的运营场景。

6.2 深分页:limit 100000,20 为什么慢

视频表数据量过了百万级,列表接口最容易出问题的是深分页。LIMIT 100000, 20不是跳过十万条取出二十条,而是读取十万零二十条再丢弃前十万条,行数越多越慢。延迟关联是立竿见影的解法:

SELECT v.* FROM video v INNER JOIN ( SELECT id FROM video WHERE status = 2 ORDER BY created_at DESC LIMIT 100000, 20 ) t ON v.id = t.id ORDER BY v.created_at DESC;

子查询只查主键,走覆盖索引不需要回表;外层再用主键回表取完整数据,IO 次数大幅下降。配合上一章的联合索引,深分页的耗时能从秒级降到百毫秒级。

6.3 yml密文与heapdump敏感信息泄露

SpringBoot配置里最不该明文出现的是数据库密码和密钥,但很多项目就把它们躺在 application.yml 里。两个改动就能大幅提升安全性:一是密码改成环境变量引用,password: ${DB_PASSWORD},部署时通过环境变量注入;二是引入 jasypt-spring-boot-starter,把连接串里的密码整体加密成ENC(xxxxx)的形式,启动时用环境变量传入解密密钥。记住解密密钥本身也必须走环境变量,不能写在 yml 里,否则相当于给保险箱配了把插在锁上的钥匙。

另一个容易被忽略的是 Actuator 的 heapdump 端点。Spring Boot 项目只要引入了 actuator,/actuator/heapdump就会把堆内存整个导出来,数据库连接对象里的明文密码、Token、业务数据全在里面,等于给攻击者发了一份现场快照。收尾动作很简单:

management: endpoints: web: exposure: include: health,info

把 expose 限定到最小集合。验证命令放在部署脚本里:

curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/actuator/heapdump

返回 404 正常,返回 200 就说明端点还裸奔着,结合签名校验、Redis 缓存、深分页改写,这套基于SpringBoot+MySQL的视频网站才算真正可上线。

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

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

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

立即咨询