☰
SSM在线视频网站项目实战:从架构设计到部署全流程解析
2026/10/17 7:26:05 网站建设 项目流程

“基于SSM的在线视频网站”,这个标题一出来,基本就知道是什么段位的项目了——既不是玩具级的纯JSP购物车,也够不着SpringCloud那套微服务大厂方案,它恰好卡在“课程设计、毕业设计、个人练手项目”最经典的那个位置。我见过太多人选这个题目,也带过几个师弟师妹在这套架构上踩坑,说实话,这题目做的事很清晰,但能把它做得完整、做得不露怯,很考验功底。这篇文章我就从一个老开发的角度,把这个SSM在线视频网站从框架选型、数据库设计、代码分层、视频上传与播放,到部署上线的整个链路都拆开讲一遍,顺带把那些坑我都标出来,给正在做类似系统的人当个地图用。

1. 项目架构设计与技术选型思路

1.1 为什么是SSM而不是别的

现在的技术圈,新框架一个接一个,SpringBoot几乎成了Java Web的默认答案,有些人看到SSM就说“老掉牙了”。但如果你是要拿这个项目来学东西、应付毕业答辩、或者作为第一份简历里的项目经历,SSM反而是更值得选的一条路。

原因很简单:SSM这三件套——Spring做容器管理和事务、SpringMVC做请求分发、MyBatis做数据访问——每一层都是显式配置、显式调用的,你能清楚地看见一个HTTP请求从浏览器到数据库再返回浏览器,整个完整路径上是哪些代码在干活。SpringBoot把这一切自动配置好了,用起来省事,但也把关键的机制藏起来了,面试官问“SpringMVC的DispatcherServlet是怎么分发的”、“MyBatis的一级缓存是什么时机失效的”,你反而接不上话。用SSM做一遍,这些知识点是自己一行一行撸过的,这是这个选型能带来的真正红利。

再具体一点选型:Spring 5.x、SpringMVC 5.x、MyBatis 3.5.x,开发工具用IDEA,数据库用MySQL 5.7或8.0,数据库连接池用Druid,构建工具用Maven,服务器用Tomcat 8.5或9。这套组合是一个保守但极其稳定的方案,网上资料多、案例多,遇到问题基本不会卡死。

1.2 功能模块怎么切才科学

在线视频网站看起来复杂,但拆成模块其实有固定的套路,我直接把经过验证的划分方式放出来:

  • 用户模块:注册、登录、个人信息查看与修改。登录可以做成单角色的普通用户即可,如果有精力,再加一个管理员角色,前后台共用一套登录逻辑,通过角色字段路由到不同首页。
  • 视频模块:视频列表展示、按分类筛选、关键词搜索、视频详情页、视频播放。注意,这里的视频指的是“视频条目信息”,不是视频文件本身,文件存储是另一套逻辑。
  • 分类模块:视频分类管理,比如按类型(电影、番剧、纪录片)或者按语言、年份分类,一般做成两级分类就够。
  • 评论模块:用户对视频进行评论,视频详情页展示评论列表。
  • 后台管理模块:管理员可以维护视频信息(上传新的视频条目、修改描述、删除违规内容)、管理分类、管理用户状态(启用/禁用)。后台管理既是实际需求,也是扩展性最好的加分模块,建议一定保留。

功能不需要贪多。贪多的后果是每个模块都做得半吊子,表结构乱、代码耦合、联调的时候到处是问题。把上面六个模块做扎实,功能上已经是一个“五脏俱全”的在线视频系统了。

1.3 数据库表设计的心法与关键字段

数据库设计是这个项目里最先决定成败的一环。表设计不对,后面所有代码都难受。按我常用的方式,核心表至少有这么几张:

用户表

字段名类型说明
idint/bigint主键自增
usernamevarchar(50)登录用户名
passwordvarchar(128)加密后密码,别存明文
phone/emailvarchar(50)联系方式,可为空
avatarvarchar(255)头像路径
roletinyint0普通用户,1管理员
statustinyint0禁用,1正常
create_timedatetime注册时间

密码加密我用的是MD5加盐或者BCrypt,虽然在答辩里“MD5加密存储密码”这个设计已经足够辩解,但建议直接用BCrypt,面对“为什么你的密码是明文”这种送命题时你能答得更硬气。

视频表

字段名类型说明
idint视频ID
titlevarchar(200)视频标题
category_idint所属分类
cover_urlvarchar(255)封面图路径
video_urlvarchar(255)视频文件访问路径
descriptiontext简介内容
play_countint播放次数
uploader_idint上传用户或管理员ID
statustinyint上架/下架状态
create_timedatetime发布时间

说明:视频文件的“存储路径”原则是数据库存相对路径或者虚拟路径,不存磁盘绝对路径。存绝对路径的问题是你换了服务器或者目录调整之后,库里的数据全部变成废数据,而相对路径配合Tomcat虚拟映射能灵活很多,这个后面实操章节我会展开讲。

分类表

字段名类型说明
idint分类ID
namevarchar(50)分类名
parent_idint父分类ID,0则是一级
sort_orderint排序权重

分类表这个parent_id字段是典型的多级分类设计,递归查询时用MyBatis的collection嵌套结果集一次拿回来,一次SQL搞定两级分类,代码也整洁。

评论表:重点字段是id、video_id、user_id、content、create_time。评论分页这几列够了,不需要冗余用户名(关联查询拿就行,多表Join的成本不高)。

关于表关联关系,视频表和分类表是多对一,视频表和评论表是一对多,用户和评论是一对多,用户和视频(管理关系)是一对多。完整的数据库设计可以直接拿去建库,不用再加冗余的业务表。

2. 三层架构下的代码核心细节

2.1 各层类的划分与职责边界

SSM项目的代码结构我在实战中验证下来最顺手的分包逻辑是:

  • entity/pojo包:放与数据库表对应的实体类,字段和表字段一一对应。注意这类对象不该加业务逻辑,它就是数据容器。
  • mapper/dao包:接口 + 对应的XML映射文件。接口定义方法,XML写SQL。MyBatis之所以用XML写SQL而不是注解,是因为XML可以处理动态SQL(if、foreach),复杂查询时优势明显。
  • service包:业务逻辑层。例如注册时校验用户名是否已存在、删除视频时同时清理评论数据,这些都是Service的工作范围。接口加实现类的拆分方式在答辩时很常见,工程上建议保留。
  • controller包:接收前端请求、调用Service、返回视图或JSON数据。Controller里不要出现业务SQL或复杂计算,保持薄Controller。

拿用户视频列表业务打个样。用户在前端点击“电影”分类,浏览器发一个GET请求到/video/list?categoryId=3&pageNum=1,Controller收到参数,调用Service层的方法getVideoPage(categoryId, pageNum),Service内部先用PageHelper分页插件设置分页参数,再调用Mapper接口里的selectByCategoryId(categoryId)方法,MyBatis执行XML里的SQL,把结果封装成PageInfo<Video>返回给Controller,Controller把PageInfo转成JSON返回到前端页面。这个链路清晰、可控、好DEBUG,也正是SSM面试题最爱问的“请求流转过程”。

2.2 Spring与SpringMVC配置文件的取舍

这部分基本上每周都有人在群里问“为什么我的项目起不来”,十有八九是配置文件的问题。我直接给出我常用的配置骨架,照着写不会出大错。

spring-mvc.xml是控制器和视图层的配置,核心是三件事:扫描Controller包、开启注解驱动、配置视图解析器。

<context:component-scan base-package="com.example.video.controller"/> <mvc:annotation-driven/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 静态资源放行 --> <mvc:resources mapping="/static/**" location="/static/"/>

applicationContext.xml(或spring-context.xml)是Spring的核心配置,管理Service、Mapper、DataSource、事务。它扫描的范围要排除Controller,避免和SpringMVC的扫描冲突。这是SSM整合里特别容易翻车的一点:两个容器都去扫同一个包,导致Bean重复定义或者事务失效。

<context:component-scan base-package="com.example.video"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>

DataSource配置用properties文件外置,Druid连接池,配置项包括初始连接数、最大活跃连接数、空闲时间等:

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/video_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=你的密码

URL里的serverTimezone=Asia/Shanghai是MySQL 8.0连接时的硬性要求,少了它一定报时区错误,这个坑新手几乎都得踩一次。字符编码用utf8而不是utf8mb4也能跑,但既然做视频网站,用户评论里可能有人发emoji,用utf8mb4更稳妥。

事务配置用注解驱动,在spring-context.xml里加上<tx:annotation-driven transaction-manager="transactionManager"/>,然后在Service实现类或方法上标@Transactional。不用配置一大串aop切面,注解足够清晰。

2.3 MyBatis映射文件里的三个高频坑

MyBatis在SSM里是数据访问的核心,灵活但坑也多,这里说三个最常见的:

第一个坑是实体类字段名和表字段名不一致引发的“列找不到”报错。MySQL的命名习惯是下划线风格,Java实体类是驼峰风格,比如数据库字段create_time对实体类属性createTime。如果MyBatis配置不对,运行时它找不到对应的setter就会报错。解决方式是在MyBatis全局配置里打开驼峰映射:

<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>

这样MyBatis自动把你的下划线字段映射到驼峰属性,不用在resultMap里一个个手动配。但注意,多表联查的查询结果如果想自动封装成VO对象,还是要写resultMap指定映射规则,这个便利是一等一的。

第二个坑是动态SQL里if判断不等于null还要不等于空串。很多新手只判断非null,结果前端传了一个空的搜索关键词,SQL里拼出一个WHERE title LIKE '%%',虽然能查出数据,但效率极低,而且在某些场景下会查错数据。写判断时统一用:

<if test="keyword != null and keyword != ''"> AND title LIKE CONCAT('%', #{keyword}, '%') </if>

第三个坑是resultMap、分页、count查询之间的配合。我用PageHelper做分页时发现,如果分页的SQL里有多表关联,它的count语句偶尔会生成错误,特别是用了LEFT JOIN且Join条件里有子查询的场景。解决方式就是给分页插件指定自定义的countSQL,或者在编写Mapper SQL时尽量避免太过复杂的Join,把复杂查询拆成两步在Service层组装。

3. 核心功能实操:视频上传与播放的实现

3.1 前端页面与后端交互的设计

在线视频网站的前端有两套思路:一套是直接JSP + JSTL + BootStrap,渲染在服务端,这是SSM项目最经典的搭配,不用解决跨域问题,简单直接;另一套是Vue或React做前后端分离,后端只出JSON接口。如果你这个项目是用来毕业设计的,我倾向于前者——JSP这套方案更贴合SSM的授课知识,答辩时老师也更好理解,而且不需要额外再讲一层Node环境。用Vue做的项目当然加分,前提是你能把“为什么不用JSP而用前后端分离”这个设计决策说清楚。

页面规划上,至少要有这几个页面:用户登录/注册页、视频列表页(含分类筛选和搜索)、视频详情页(含播放器和评论区)、后台视频管理页、后台用户管理页。

列表页和后台管理页都要做分页。分页我直接用PageHelper插件,引入依赖后一行代码就能拿到分页结果:

PageHelper.startPage(pageNum, pageSize); List<Video> list = videoMapper.selectByCategory(categoryId); PageInfo<Video> pageInfo = new PageInfo<>(list);

PageHelper底层是拦截器机制,它会把当前线程上下文里的分页参数绑定到下一次查询上,所以startPage后面必须紧跟Mapper查询语句,中间不要穿插其他数据库操作。这个顺序问题,我见N个同学踩过:startPage之后去查了别的表,分页就错乱了。

3.2 后端接收文件的关键步骤

视频文件和封面图的上传是MultipartFile机制,SpringMVC对文件上传的支持很好,步骤如下:

  1. 配置multipart解析器。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"/> <property name="maxUploadSize" value="104857600"/> <property name="maxUploadSizePerFile" value="104857600"/> </bean>

maxUploadSize限制了单次请求最大100MB、单个文件最大100MB。如果你打算录屏传视频,100MB可能不够,可以按自己的需求调整,但要注意Tomcat的请求体大小限制是2MB(默认),改大了需要同步修改Tomcat的配置。

  1. Controller层接收文件。
@PostMapping("/video/upload") @ResponseBody public Result upload(@RequestParam("file") MultipartFile file, @RequestParam("title") String title) { String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); String newFileName = UUID.randomUUID().toString().replace("-", "") + suffix; // 存到指定目录 String savePath = "E:/upload/video/" + newFileName; file.transferTo(new File(savePath)); // 回显路径 String virtualPath = "/upload/video/" + newFileName; return Result.success(virtualPath); }

用UUID重命名文件是我一直坚持的做法:中文文件名很可能造成URL编码和下载时的乱码问题,而且重名文件会互相覆盖。重命名后文件名唯一、不可猜解、不影响后缀,缺点是丢失了原始文件名,如果需要可以再存一份原名到数据库备用。

  1. 配置虚拟路径映射。

数据库里存的是虚拟路径/upload/video/xxx.mp4,但文件物理上在E盘或者服务器别的目录,需要在SpringMVC的配置里做个资源映射:

<mvc:resources mapping="/upload/**" location="file:E:/upload/"/>

这个配置的意思是,浏览器请求/upload/video/123.mp4时,SpringMVC会直接去E:/upload/video/123.mp4找文件。这样文件即使放在Tomcat外部目录,也能被访问到。好处是以后升级系统、重新部署war包时,上传文件不会丢失,这是实战经验。

3.3 播放功能怎么做不翻车

播放功能最稳妥的做法是HTML5的video标签直接播放MP4格式。现在浏览器对MP4/H.264的支持非常成熟,不需要额外引入任何播放器插件:

<video controls preload="metadata" style="width:100%;"> <source src="${video.videoUrl}" type="video/mp4"> 您的浏览器不支持HTML5视频播放。 </video>

如果非要花哨一点,可以集成一个开源的播放器(比如业内常用的HTML5播放器库),实现倍速播放、截图、弹幕等功能,但这些是需要花时间联调的。我最推荐的做法是:初期先把原生video标签跑通,比如把视频上传、路径回显、播放成功这一整条链路验证完毕,后续有时间再考虑加播放器界面。一个项目里,核心链路跑通比花里胡哨的功能重要得多。

还有个细节提醒一下:视频格式一定要在服务端做校验,别信前端。前端可以用accept属性过滤,但那是用户可绕过的。在后端拿到MultipartFile后,通过后缀名判断、甚至读文件的头部魔数判断真实格式,否则用户随便传一个exe文件改名为mp4,后端存下来再被访问,轻则播放失败,重则被人上传恶意文件,在那之后服务器的安全系数基本为零了。文件的类型校验是我绝对不会省的一步。

4. 联调、部署与常见问题速查

4.1 本地开发环境搭建步骤

环境搭建这关,基本上是SSM项目劝退率最高的一步,原因在于版本组合问题。我把自己习惯的组合列出来,这个组合我实际跑通过很多遍:

  • JDK 1.8(不要用JDK 17,Spring 5和旧Tomcat对高版本JDK支持有兼容问题)
  • Tomcat 9.0(8.5也行,9对应Servlet规范4.0)
  • Maven 3.6+(负责依赖管理)
  • MySQL 5.7或8.0均可
  • IDEA 2021及其以后版本

创建项目时注意,直接在Spring Initializr里选SpringBoot然后“撤回”SSM这种做法不现实。标准做法是新建一个Maven项目,手动引入三个框架的坐标依赖,打包方式要选“war”,因为要部署到Tomcat。依赖部分的核心坐标是spring-webmvc、mybatis、mybatis-spring、mysql-connector-java、druid、jstl、jackson-databind、pagehelper。

web.xml里的配置有三件套:ContextLoaderListener监听器加载Spring容器、DispatcherServlet配置SpringMVC、字符编码过滤器CharacterEncodingFilter。其中字符编码过滤器的Order要放在最前面,否则POST请求的中文参数到了后端就是乱码,这属于SSM项目里的经典疑难杂症。

4.2 我实测过的几个典型故障与排查思路

故障一:Tomcat启动时报ClassNotFoundException: DispatcherServlet。

产生原因基本都是缺少javax.servlet-api的依赖或者项目没有正确关联到Tomcat。排查思路:先看依赖里是否引入了spring-webmvc,再看部署结构里有没有lib目录。IDEA里常见操作是把依赖打进war包,直接在Project Structure -> Artifacts里把lib目录加上,这个问题就消失。

故障二:MyBatis提示Invalid bound statement (not found)

这个报错的意思是Mapper接口找到了,但对应的XML找不到。原因几乎只有一个:Mapper接口和XML文件放在同一个包路径下,但XML没有编译到classes目录。Maven默认只把resources目录里的文件打包,如果你把XML放在java目录下,就需要在pom.xml里显式声明:

<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build>

这个配置是我见过无数次的救火代码。放进去之后,XML就会老老实实被打包到target里。

故障三:JSON返回给前端时日期格式变成一串数字。

Java里日期序列化成JSON默认会是时间戳,前端拿到的createTime是一串毫秒数,根本没法展示。解决办法有两种,一是在字段上加注解:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime;

二是在SpringMVC配置里全局设置ObjectMapper:

<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.fasterxml.jackson.databind.ObjectMapper"> <property name="dateFormat"> <bean class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-MM-dd HH:mm:ss"/> </bean> </property> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>

从代码整洁度看,我更推荐注解方式,每个字段自己声明格式,直观且不会误伤其他类型。

4.3 部署上线的完整路径

开发完成后最终要部署。简单说一下标准流程:

  1. Maven打包:执行mvn clean package,生成war包。
  2. 上传服务器:这里泛指任何一台性能足够的云主机,配置Tomcat和MySQL环境。
  3. 部署war包:把war包放进Tomcat的webapps目录,启动Tomcat,它会自动解压项目。
  4. 数据库初始化:在服务器MySQL上导入建库建表的SQL脚本。
  5. 配置文件调整:修改jdbc.properties里的数据库地址为你服务器的内网或公网地址。
  6. 访问验证:浏览器访问http://服务器IP:8080/项目名/。

部署后常见的问题有一个:图片、视频上传到服务器通常没问题,但访问不到,这种10分钟能排查完——八成是/upload/**的虚拟路径映射在服务器上没有指定到真实路径,或者防火墙没有放开上传目录的访问权限。

4.4 项目还能怎么扩展加亮点

如果做完以上内容你还有余力,这里有几个性价比高的扩展方向:

  • 视频搜索走全文索引:现在LIKE查询也能搜,但视频标题一多,性能就开始下降,可以考虑引入Elasticsearch做索引或至少做MySQL全文索引,作为一个亮点写在文档里。
  • Redis做播放量缓存:视频详情页每次刷新都去UPDATEplay_count字段,数据库压力不小,用Redis做计数器,异步刷新到数据库,这是一个能拿得出手的答辩论据。
  • 对象存储代替本地存储:把视频和封面传到云存储中,数据库只存访问URL,这样做的好处是扩展性和并发访问能力大幅提升,也不用操心磁盘空间问题。
  • 用户收藏功能:视频表之外加一张收藏关系表,前端“收藏”按钮联动收藏状态的切换,这个功能的学习价值在于多对多关系在CRUD中的处理,也很适合展示。

我个人建议有精力就做两个:视频播放量的Redis缓存和用户收藏。这两个模块逻辑简单、表结构清晰,能快速落地,而且都能在答辩时自然引出“为什么用Redis”“怎么保证缓存和数据库一致性”这类高质量问题,展示你的设计思考比堆砌功能更受认可。

5. 写在最后的几句经验

做SSM在线视频网站这个项目,我的体会是:它的难点根本不在“代码量”或者“功能复杂度”上,而在于你能否把Spring、SpringMVC、MyBatis这三套东西真正串起来。很多同学在学框架的时候是分开学的,学Spring的时候不知道它怎么接管Service,学MyBatis的时候不知道SqlSession从哪里来,等到做项目才发现,原来把它们整合起来需要的配置和理念,比任何一个框架单独用都更考验理解。所以做这个项目的过程中,建议每一段配置都去查一下“这一行到底在扮演什么角色”,这比复制粘贴整整20个配置文件然后期待它跑起来更有收获。

另外,部署阶段如果有条件,尽量自己完整地走一遍“从本地打包到服务器运行”的流程,不要只在IDEA里点一下运行按钮就算完事。这个流程你毕业后的第一份工作前三个月一定还会碰到,提前熟悉,相当于把学费提前赚回来了。最后,别忘了学一下Git,项目初期就建仓,每个功能模块开发完提交一次,中间改崩了也有回退的余地。这个习惯,我从做这个项目开始一直保持到现在,真心建议你也养成。

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

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

立即咨询