
做毕业设计选了这个“在云端”在线音乐平台用Java和SSM框架搭前后折腾了两个月。写这篇东西不是给你贴代码而是把当时做系统设计、写代码、攒论文的时候踩过的坑和想明白的道理梳理一遍。如果你也选了类似题目或者正在纠结SSM还能做什么这篇文章应该能帮你节约不少时间。1. 项目整体设计与方案选型1.1 为什么选SSM而不是Spring Boot先说结论SSM在毕业设计里仍然有存在价值尤其当你需要展示自己对框架整合原理的理解时。我选SSM不是因为它比Spring Boot更先进恰恰相反是因为它“老”得足够透明。Spring Boot把大量配置自动完成了这对企业开发是好事但对毕业设计来说很多关键机制会被掩盖掉。比如Spring容器怎么启动、MyBatis的SqlSessionFactory怎么创建、事务管理器怎么接入这些在SSM里都需要你亲手配置一遍写论文的时候就有实打实的内容可写。常有人问现在企业都用Spring Boot做毕设还用SSM会不会显得过时其实答辩老师更在意的是你能不能讲清楚请求从浏览器到数据库的完整链路以及每个环节解决了什么问题。SSM的配置虽然繁琐但配置文件的每一行你都知道在干什么这种掌控感在答辩时很有底气。1.2 “在云端”的核心定位与模块划分题目里的“在云端”不是噱头它对应的是平台的核心交互方式音乐资源存储在服务端用户通过浏览器随时随地访问不需要本地保存任何音频文件。这决定了整个系统必须以服务端为中心前后端通过HTTP协议交互。实际划分了七个模块用户模块注册、登录、个人信息维护、密码加密音乐模块音频上传、音频信息管理、在线播放、下载歌单模块创建歌单、收藏歌曲、歌单分类评论模块歌曲评论、评论回复、敏感词过滤搜索模块按歌名、歌手、专辑名模糊搜索排行榜模块按播放量、收藏量生成榜单后台管理模块用户管理、音乐审核、数据统计模块划分的原则是“高内聚、低耦合”每个模块通过Service层接口对外提供服务Controller层只负责参数接收和结果封装不直接写业务逻辑。这不仅是代码规范也是论文里架构图的核心依据。1.3 技术栈冷启动从环境到框架搭建环境这块比较省心JDK 8、Maven 3.6、Tomcat 8.5、MySQL 5.7是一套很稳的组合。不建议一上来就上JDK 17或者MySQL 8.0SSM是老框架跟新版本组件偶尔会有兼容性摩擦比如MySQL 8的驱动类名变了、时区配置不一样这些问题虽然不难解决但没必要在毕设阶段浪费精力。数据库命名为cloud_music核心表有user、song、song_list、list_song、comment、admin。建表的时候注意字符集统一utf8mb4因为评论里可能有特殊字符utf8mb4能存下emoji表情。创建Maven工程后第一件事不是写代码而是把所有依赖版本锁定。我用的版本仅供参考组件版本spring5.1.8.RELEASEmybatis3.5.3mybatis-spring2.0.3mysql-connector-java5.1.47druid1.1.20jackson2.9.9版本之间有没有坑有。比如spring-webmvc和spring-context版本不一致会导致容器启动报错所以所有Spring组件必须用同一个release版本别一个个单独引。2. 核心功能设计与实现要点2.1 用户登录与注册加密是第一道防线用户模块是几乎所有系统的门面“在云端”也不例外。注册时密码不能明文存数据库这是底线。我用的MD5加盐虽然现在安全要求更高了但在毕设里够用而且论文里能解释清楚“为什么要加盐”相同密码在不同盐值下产生不同摘要防止彩虹表反推。加盐逻辑很简单注册时生成一个UUID作为盐值把“盐值 密码”拼起来做MD5摘要数据库存盐值和摘要两列。登录时从数据库查出盐值对输入的密码做同样计算再比对摘要。登录状态用的是Session登录成功后把用户对象放进Session拦截器里判断Session是否存在不存在就跳回登录页。要注意的是Ajax请求和页面跳转的拦截处理不一样Ajax请求被拦截时不能直接重定向否则前端收到的是一整张HTML页面而不是JSON容易让人排查半天。2.2 音乐在线播放音频流与文件上传的配合在线播放是整个平台的灵魂也是最容易出问题的地方。播放链路是这样的歌曲上传时保存到服务端磁盘的upload/music目录数据库里只存相对路径。播放时Controller需要把请求映射到服务器上的真实文件写成/music/play/{songId}Controller根据songId查出路径再用流把文件写给前端。音频格式建议优先支持MP3兼容性最好。我之前试过WAV格式文件体积大流式播放时加载慢体验很差。MP3加一个前端HTML5的audio标签就能跑起来不需要额外的播放器插件。上传这边有几个参数很关键单个文件大小限制、总文件大小限制、上传文件保存路径。Spring的MultipartFile解析需要配commons-fileupload依赖然后在spring-mvc.xml里配MultipartResolver属性里设置maxUploadSize。我的经验是单文件限制别设太大10MB以下比较合理毕设里的测试音频基本都是短文件。2.3 歌单与排行榜数据关系的设计细节歌单和歌曲是多对多关系中间表list_song只存listId和songId两个外键。这里有个设计细节值得写进论文中间表上加了唯一约束listId, songId保证同一首歌不会被重复加入同一个歌单这是数据一致性在数据库层面的兜底。业务层面还要再兜一次插入前先查一遍这条记录是否存在。为什么做两层因为唯一约束能防住并发重复插入但业务查询能提前给出更友好的提示比如“这首歌已经在歌单里了”。两个机制配合既稳又友好。排行榜不实时算我用的方案是维护一张song表里的play_count字段用户每播放一次就加一榜单接口按播放量排序取前十。实时更新有锁竞争的问题但也可以用乐观锁update时where play_count #{expected}返回影响行数为0说明被并发改了重新读取再更新。毕设里数据量不大这个方案足够。2.4 搜索与评论为什么不直接用LIKE就完事搜索我用的是MySQL的LIKE模糊查询就是WHERE song_name LIKE CONCAT(%, #{keyword}, %)。这个实现简单论文里能写清楚功能上也能满足需求。但要说清楚它的局限首尾通配符会导致索引失效数据量大时全表扫描会很慢。所以论文里我分了一小节写“搜索性能优化思考”提到了两种方向全文索引和搜索引擎方案。并说明本项目当前阶段选用索引方案会更匹配中小型数据规模的定位。评论模块有一个容易被忽视的点是字段长度约束。评论内容如果前端不做限制、后端也不校验用户粘一大段文本进去数据库字段如果是varchar(255)直接报错前端体验很差。我在后端做得校验长度超过200字就拦截返回提示。敏感词过滤用的是基于前缀树的敏感词集合文本命中敏感词就拒绝发布。我能接受为了保证合规牺牲一点性能这个功能本身就是刚需。3. 深入SSM整合注解、配置与事务边界3.1 三层架构中的常用注解梳理SSM里高频注解就那几个但新手很容易混淆它们的使用位置这里做一个区分Controller标注在Controller类上配合RequestMapping做路由映射Service标注在Service实现类上标记业务层组件Repository标注在Dao层接口实现类上配合MyBatis动态代理后一般不用写实现类Autowired按类型注入依赖默认byTypeResourceJDK提供的注解先按名称后按类型注入Transactional声明事务边界加在Service方法上ResponseBody把返回值直接写入HTTP响应体返回JSON时必用RequestBody把请求体里的JSON数据绑定到对象参数上RestController其实是Controller加ResponseBody的组合用SSM的Controller类里我习惯分开写这样看代码时能明确知道“这个方法返回的是视图还是JSON”。这在论文截图的代码注释里可以体现得比较清楚。3.2 核心配置文件与Spring整合难点SSM有三个配置文件分工非常明确spring.xml负责Service层和Dao层的组件扫描、数据源、事务管理器spring-mvc.xml负责Controller层组件扫描、视图解析器、静态资源放行spring-mybatis.xml通常合并进spring.xml负责SqlSessionFactory和Mapper扫描组件扫描一定要分层。spring.xml里只扫com.cloud.service和com.cloud.daospring-mvc.xml里只扫com.cloud.controller。如果Controller也被spring.xml扫到了会出现事务管理的代理混乱有时候注解不生效排查起来特别费劲。事务管理器配置逻辑是DataSource交给Druid管理SqlSessionFactory依赖DataSource创建事务管理器PlatformTransactionManager绑定同一份DataSource。要保证SqlSessionFactory和事务管理器用的是同一个数据源事务才能拦截到MyBatis的数据库操作。这个连接指向的一致性属于这个项目里最需要读懂的配置逻辑。MyBatis的Mapper接口扫描用mybatis:scan base-packagecom.cloud.dao/它会自动为每个接口生成代理实现不用手写实现类。SQL写在XML文件里放在resources/mapper目录下命名跟接口保持一致。3.3 事务边界与数据一致性的项目实操事务是SSM里最有含金量的部分因为它在业务中真正管住了“要么全成功、要么全失败”。拿“创建歌单”来说业务操作有两步先插入一条歌单记录再往中间表插入若干初始歌曲。如果第二步失败而第一步已提交就会有一条“只有标题没有内容”的孤儿歌单。方法上加Transactional(rollbackFor Exception.class)两步就变成一个原子操作任何一步抛异常都会整体回滚。这里说一个隐藏点rollbackFor默认只回滚RuntimeException如果你代码里catch了异常却没重新抛出事务就失效了。我写过一段业务代码在Service里捕获异常打印日志后正常返回结果数据写到一半问题查了很久才定位到。经验是不要在事务方法内部吞掉异常要么让事务管理器处理回滚要么显式调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。数据一致性不是只靠事务数据库的唯一约束、前端参数校验、Service层业务判断要三层配合。这个在论文里可以当作“系统设计对一致性的保障”来写答辩时老师对这个点比较感兴趣。4. 论文结构拆解与写作思路4.1 论文每一章我写了什么毕业论文不是简单的项目说明书它要有“提出问题、分析问题、解决问题”的逻辑线。我的论文分了六章绪论毕设背景、研究意义、国内外现状。现状这部分别空谈去知网搜“在线音乐平台”和“流媒体技术”相关论文提炼3-5条真实存在的文献来对比评论。相关技术介绍Java语言特性、SSM框架、MySQL数据库、前端三件套加上Ajax每项写清楚“为什么选它”而不只是罗列“它是什么”。系统分析可行性分析技术、经济、操作、需求分析角色、功能、非功能需求、用例图、数据流图。系统设计架构设计B/S架构图、功能模块设计模块图、数据库设计ER图、表结构说明。系统实现按核心功能逐个写实现思路、关键代码片段、效果截图。系统测试先写“测试环境和测试方法”再写功能测试用例表和测试结果还要写性能测试分析。 最后是总结与展望回顾完成了什么点出不足给出后续优化方向。4.2 通用表结构和ER图设计的赘述与避坑数据库设计表结构时最容易翻车的是“字段冗余”。我的建议是每个表都要有id主键、create_time这两个字段几乎不占空间但后续做列表排序、数据分析时离不开。论文章节里的ER图工具建议用Visio或者draw.io。实体的属性别画太多每个实体画5-6个核心属性就行太密集反而显得杂乱。表格设计的每一列字段可以在论文里以表格形式呈现字段名、类型、约束、说明四列即可。4.3 答辩常见的提问与回答思路答辩老师最爱问的问题集中在几个方向。一是框架原理比如“Spring的IOC和AOP在项目里体现在哪”这需要有真实场景去回应IOC体现为业务对象由容器统一管理AOP体现为事务管理和拦截器。二是数据库设计比如“为什么歌单和歌曲要拆中间表”答辩时可以直接对比不拆表就会变成一张动态扩展列的表数据冗余且维护困难。三是并发场景比如“多个用户同时收藏同一首歌怎么办”乐观锁和唯一约束的应对机制要说清楚。这些问题在写作阶段就要有意识地在对应章节埋好伏笔答辩时就不会卡壳。5. 常见问题与排查技巧实录5.1 SSM整合期最容易掉进的坑整体走下来最耗时的是整合阶段配置问题五花八门这里列几个高频坑。第一个是Maven依赖冲突。我引入Druid后发现和低版本的log4j有冲突运行时报NoSuchMethodError。原因不是代码错了而是jar包版本不一致我用依赖树检查工具命令排查定位到冲突后统一版本才解决。建议遇到诡异报错先跑依赖树工具不要一上来就怀疑代码。第二个是数据库中文乱码。URL里加上characterEncodingutf8同时保证数据库连接属性配置完整MySQL 5.7以上还要注意useSSL参数。乱码问题排查链路很长请求参数、数据库连接、表字段字符集、页面编码四层都得检查。第三个是JSP页面找不到Bean或标签本质是taglib依赖缺失解决办法是引入jstl和standard依赖。5.2 文件上传问题的完整排查链上传模块问题是我调试时间最久的。如果你的上传也总是失败按这个顺序排查第一检查spring-mvc.xml里的MultipartResolver是否配置且bean名称是否叫multipartResolverSpring源码里固定按这个名称去找。用了CommonsMultipartResolver则它的顺序很关键。第二检查Tomcat的临时目录权限。Linux环境上/tmp目录权限不足会导致MultipartFile写入临时文件时报错改配置指定缓存上传临时目录路径能解决。第三检查数据库字段长度是否够用文件路径别用varchar(50)至少得varchar(255)。第四上传成功后马上测试播放路径因为播放时拼接的相对路径不能带上盘符或者绝对地址。我这里统一用相对路径存储前端渲染时加上contextPath拼完整URL。5.3 在线播放卡顿的分析与解决播放卡顿最常见的原因不是服务器带宽而是Tomcat默认的IO线程模型。Tomcat默认BIO模式一个请求固定占一个线程并发播放时线程池很容易打满。解决方法是把Connector切到NIO模式在server.xml里改protocolorg.apache.coyote.http11.Http11NioProtocol实测连播时线程占用明显下降。另一个原因是音频文件没有做字节范围请求支持。HTML5的audio标签在用户拖拽进度条时会发Range请求如果服务器不支持返回206 Partial Content播放器会重新从头加载整个文件。Spring MVC在静态资源处理上默认支持Range但如果你用自定义Controller流式返回文件就要手动处理Range头。我在播放方法里加了对Range请求的解析支持单段范围响应拖拽播放就流畅了。5.4 前后端联调时的数据格式陷阱前后端分离或半分离模式下联调期的接口对接问题往往占比很高。我遇到的比较典型的几个后端返回的时间字段是Date类型前端拿到的是长毫秒数字段名Date与数据库字段命名习惯不一致的情况也出现不少对策都是在VO对象里统一用自定义DateSerializer格式化以及命名规范统一。前端用Ajax提交数据时要特别注意如果提交的是JSON对象而不是FormData格式后端必须用RequestBody来接收。这个很容易成为被忽略的细节对应的前端contentType要设为application/json两边只要有一边不对拿到的永远是null。6. “在云端”项目的扩展空间与个人体会6.1 从SSM到Spring Boot的技术演进如果把“在云端”重做一版我会换Spring Boot加MyBatis Plus的组合。SSM教会了我框架原理但Spring Boot能更快地聚焦业务本身。核心思路是一样的分层架构不变、数据库设计不变、业务逻辑不变变的是配置方式从XML转向注解和自动配置很多繁琐的模板配置被消除了。这个对比很有价值论文里可以放一节叫“基于SSM与基于Spring Boot实现的差异分析”从配置方式、启动流程、部署形式三个维度对比说明各自适用的场景。这样的内容在答辩时比单纯做东西更有含金量体现的是工程认知。6.2 从毕设到真实项目我学到的三条经验第一条做系统前先画清楚数据库ER图数据库设计定了框架一旦中期要改表结构代码改动成本极高。第二条写代码每个功能要能console.log或断点验证再往下一个功能推进不要堆一堆代码再统一调试那是浪费时间的开始。第三条论文和代码同步推进功能做完写一章别等代码全部搞定再集中写论文到后面时间压力会让质量失控。6.3 如果你也打算做这个题目最后几点建议第一不要贪功能。在线音乐平台能做的方向很多推荐算法、私人FM、歌词逐字滚动、多端同步这些都可以是“展望”里的星星但毕设核心功能做完做稳才是正经事。第二测试用例要留痕。每一项核心功能至少写一条测试用例结果截图往论文里放这是最容易被看到工作量差异的地方。第三前期花时间理解技术栈的运行机制尤其是SSM里的IOC、AOP、事务、Mapper代理这几个机制。理解通透之后你写出来的代码是有判断力在里面的答辩时的状态也完全不同。“在云端”这个题目适合所有想通过一个完整项目把Java后端体系串起来的人。做的时候觉得配置繁琐、BUG反复做完回看会发现那些难啃的部分才是最扎实的收获。