简介:这是一份基于Java的在线音乐试听管理系统的毕业设计论文文档,适合计算机专业学生用于毕设参考或课程项目学习,尤其适合希望掌握JSP、Servlet、MySQL及Web前端技术整合的开发者。文档完整呈现了系统从课题背景、可行性分析、需求设计到代码实现的论文结构,系统基于B/S模式,融合MySQL、Servlet、JSP与HTML技术,针对游客、会员、管理员三类角色设计了歌曲显示、排行榜、在线注册、歌曲增删查改及会员管理等功能模块,并对其实现流程和数据库交互做了系统性论述。资源为单个doc文件,大小1.29MB,内容包含郑州大学毕业设计论文正文、摘要及目录预览,可帮助读者快速了解论文的论述框架与关键技术要点。目前已有50人学习,对于准备毕设或需要撰写Java Web项目论文的同学,可据此梳理技术选型、模块划分和开发流程,减少从零搭建系统与查阅资料的精力消耗。
1. 这份 Java 在线音乐管理系统论文:2019 年的毕设,现在照样能复现
拿到手里的这份郑州大学毕业设计论文,题目是「基于 Java 的在线音乐试听网的开发与设计」,技术栈是 JSP + Servlet + MySQL,典型的 2019 年 Java Web 课程设计路线。很多 java 课程设计案例源码 里装的都是这类项目,但它比网上流传的残缺 demo 要完整得多——三类角色、十几项功能、从需求分析到软件测试全流程都有,正好对应一篇能拿得出手的毕设论文该有的骨架。
先说清楚这东西能解决什么。如果你正卡在毕设开题,或者想用最短时间把 Java Web 全链路跑通,这份文档的价值在于它把「需求分析 → 数据库设计 → 功能实现 → 软件测试」这条线完整地串起来了,你不需要自己从零编需求,也不用去拼凑零散的教学代码。适合两类人:一是准备 Java 方向毕业设计的在校生,需要一套能讲清楚、能演示、能答辩的完整项目;二是想快速回顾 JSP/Servlet 老技术栈的开发者,看它比看八股文要直观得多。
当然,2019 年的项目放在今天,环境和工具都有变化,MySQL 版本、Tomcat 版本、JDK 版本都要重新对齐。后面我会把复现过程中的版本匹配、代码落点、常见翻车原因一条条拆开讲。
2. 技术选型与项目骨架:为什么是 JSP + Servlet + MySQL,环境怎么搭
2.1 B/S 结构与 MVC 分层在这份毕设里的落法
这套系统采用 B/S 模式,浏览器作为客户端,所有业务逻辑和数据库交互都集中在服务器端。选 B/S 而不是 C/S,核心理由是部署简单——用户不需要安装任何客户端,浏览器输入网址就能访问,管理员维护也只需要改服务器上的代码。这在毕设场景里几乎是必然选择,因为答辩演示时只需要准备一台笔记本加一个浏览器,不用考虑客户端分发的问题。
MVC 模式的应用要结合 JSP 时代的特点来看。当时的项目不像现在 Spring Boot 那样有强制的分层约束,常见的做法是:JSP 页面充当 View,负责展示和收集请求;Servlet 充当 Controller,负责接收请求、调用业务逻辑、转发页面;Model 层是 JavaBean + DAO,负责数据库操作。这份论文里的实现正是这个套路,我在复现时建议尽量沿用,不要试图把 Spring 塞进去,否则论文内容和代码实现会对不上。
理解这个分层对后面读代码非常关键。你会在 JSP 文件里看到大量的<% %>脚本片段,这些在现在看来是反面教材,但在 2019 年的毕设里是普遍写法。它的问题是业务逻辑和页面展示耦合在一起,但好处是直观——打开一个 JSP 文件,就能看到这个页面到底做了什么操作。我建议你在复现阶段先按原样跑通,后面要优化时再考虑把脚本片段迁出去。
2.2 开发环境搭建:MyEclipse、Tomcat 与 MySQL 的版本匹配
论文里用的是 MyEclipse + Tomcat + MySQL,这套组合在 2019 年是主流,现在复现时版本要对齐,否则会遇到各种隐性问题。先说 JDK,论文时代的主流是 JDK 1.8,这个不要动,JSP 2.3 和 Servlet 3.1 规范都以 JDK 8 为基础,JDK 11 以上跑老项目反而容易出现兼容问题。
Tomcat 建议用 8.5 或 9.0,不建议用 Tomcat 10。原因在于 Tomcat 10 把javax.servlet包名改成了jakarta.servlet,而这套代码里 import 的肯定是javax.servlet,直接用 Tomcat 10 会报 ClassNotFoundException。这是我复现时踩过的第一个坑,后面避坑章节会详细展开。MySQL 方面,如果本地装的是 8.0,要注意 JDBC 驱动必须用com.mysql.cj.jdbc.Driver,而论文时代的驱动是com.mysql.jdbc.Driver,这个差异会导致数据库连接失败。
环境变量配置是新手最容易卡住的地方。JAVA_HOME指向 JDK 安装目录,CATALINA_HOME指向 Tomcat 目录,PATH里加上%JAVA_HOME%\bin。这些不配置好,MyEclipse 里启动 Tomcat 时会直接报错,而且报错信息往往让人摸不着头脑——大概率是一堆 ClassNotFound 或者端口异常,实际根源就是环境变量没配对。
2.3 项目目录结构与部署方式
在这套代码里,目录结构遵循传统的 Web 项目布局。src下放 Java 源码,包括 Servlet 类、DAO 类、JavaBean;WebContent或WebRoot下放 JSP 页面、CSS、JS、图片等静态资源;WEB-INF下放web.xml配置文件和依赖的 JAR 包。论文里的项目没有用 Maven,所以 JAR 包是手动拷贝到WEB-INF/lib下的,这种方式的优点是简单,缺点是依赖管理全靠自觉,少拷一个包就运行不起来。
部署方式有两种,我在复现时推荐用第二种。第一种是直接在 MyEclipse 里关联 Tomcat,设置好 Server 后一键部署,这种方式对新手最友好,但缺点是把 IDE 和服务器绑定了,换个环境就要重新配置。第二种是手动打 WAR 包放到 Tomcat 的webapps目录下,启动 Tomcat 后自动解压部署,这种方式更贴近真实生产环境的操作习惯,而且出问题时定位更清楚。
需要注意一个细节:MyEclipse 2019 之后的版本对老项目的支持不算好,如果安装后新建 Dynamic Web Project 时没有 JSP 模板,可以手动建文件夹和文件,不影响运行。核心依赖就是mysql-connector-java这个 JAR 包,版本选 5.1.49 或 8.0.33 都行,关键是和你本地的 MySQL 版本对应。
3. 系统分析与数据库设计:三类角色、十几项功能的权限划分与表结构
3.1 需求分析:游客、会员、管理员的功能边界
这套系统的用户分为三类,每类角色的功能边界必须划分清楚,因为后面所有权限控制都基于这个划分。
游客是未登录用户,功能主要是浏览类的:查看歌曲列表、查看排行榜、在线注册。游客不能进行任何修改操作,也不能进入后台管理页面。会员是注册登录后的用户,在游客的基础上增加了试听、收藏、评论等交互功能,论文的核心功能是试听,所以会员需要能播放歌曲并记录播放行为。管理员负责整个系统的运维,功能包括歌曲查询、歌曲添加、歌曲删除、会员管理,相当于拥有后台的全部操作权限。
这三个角色的权限等级是递进的:游客 < 会员 < 管理员。在代码实现层面,权限控制通过 Session 对象完成——用户登录成功后把用户信息存入 Session,每个需要权限的页面或 Servlet 先检查 Session 里有没有对应角色标识,没有就跳转到登录页。这种控制方式是最基础的,但也是毕设答辩时最容易讲清楚的一种。
论文里提到实现的功能大约有十几个,我梳理下来核心模块是:歌曲展示、歌曲排行榜、在线注册、歌曲类别管理、歌曲信息管理、会员管理。这几个模块之间通过数据库表关联,设计好表结构是整个系统的地基。
3.2 数据库表设计:用户表、歌曲表、类别表怎么建
数据库设计分为概念结构设计和逻辑结构设计两步。概念结构设计一般用 E-R 图表达实体、属性和联系,然后转换成关系模型。这套系统里最核心的实体是:用户、歌曲、歌曲类别。它们之间的关系是:一个类别下有多首歌曲,一个用户可以对多首歌曲进行试听和评论。
转换成具体的表,需要四张基础表。我给出的是精简版的建表语句,与实际系统的表结构对应:
-- 用户表 CREATE TABLE `user` ( `uid` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE, `password` VARCHAR(50) NOT NULL, `email` VARCHAR(100), `role` INT DEFAULT 1 COMMENT '1-会员,2-管理员', `register_time` DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 歌曲类别表 CREATE TABLE `category` ( `cid` INT PRIMARY KEY AUTO_INCREMENT, `cname` VARCHAR(50) NOT NULL UNIQUE, `description` VARCHAR(200) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 歌曲信息表 CREATE TABLE `song` ( `sid` INT PRIMARY KEY AUTO_INCREMENT, `sname` VARCHAR(100) NOT NULL, `singer` VARCHAR(100), `category_id` INT, `play_count` INT DEFAULT 0 COMMENT '试听次数,用于排行榜', `file_url` VARCHAR(200), `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (`category_id`) REFERENCES `category`(`cid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有几个设计要点值得展开。role字段用整数区分会员和管理员,而不是单独建角色表,是因为只有两级权限,一张表外加一个字段足够,答辩时也好解释。play_count字段是排行榜的数据来源,每次用户点击试听时加一,排行榜查询就是按这个字段降序排列。file_url字段存歌曲文件的相对路径,指向服务器上的某个目录,这样数据库里不会直接存大文件,只存路径引用。
外键约束方面,song表的category_id关联category表的cid,这个外键保证了歌曲一定属于某个合法类别。删除类别时要注意,如果类别下还有歌曲,外键约束会阻止删除,所以管理员删除类别前需要先处理该类别下的歌曲,这个逻辑要在前台页面上给管理员提示,否则会直接报 SQL 异常。
3.3 数据库连接方式:JDBC 直连与连接池
论文时代最常见的数据库连接方式是 JDBC 直连,即每次需要操作数据库时,通过DriverManager.getConnection()获取连接,用完再关闭。这种方式逻辑简单、代码直观,适合毕设项目的代码量,但存在一个明显的性能问题:每次请求都要创建和销毁连接,高并发下开销很大。
当时的常见做法是写一个统一的数据库连接工具类,把驱动加载、连接获取、资源关闭封装起来。论文里的实现思路也类似。展示核心方法:
package com.music.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class DBUtil { private static final String DRIVER = "com.mysql.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/musicdb?useUnicode=true&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (Exception e) { e.printStackTrace(); } } }这个工具类的逻辑很直白:静态代码块里加载 JDBC 驱动,类加载时执行一次;getConnection()返回数据库连接;close()方法统一关闭三个资源。注意 URL 里带了useUnicode=true&characterEncoding=utf8参数,这是处理中文乱码的关键,如果不加,写入数据库的中文很容易变成问号。
参数说明里有一点需要特别注意:如果你本地数据库配置了密码,需要把PASSWORD常量改成自己的密码。另外如果用的是 MySQL 8.0 驱动,DRIVER要改成com.mysql.cj.jdbc.Driver,URL 里建议加上serverTimezone=Asia/Shanghai,否则会报时区错误。这些细节在论文里没有体现,但复现时几乎一定会遇到。
4. 核心功能实现:登录、歌曲管理、排行与前台展示的关键代码
4.1 登录与权限控制:Session 与角色判断
登录模块是整个系统的入口,也是权限控制的基石。实现思路是:用户提交用户名和密码,Servlet 接收参数后调用 DAO 层查询数据库,验证通过后把用户信息存入 Session,然后重定向到对应页面。核心逻辑在 LoginServlet 里。
package com.music.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.music.dao.UserDao; import com.music.entity.User; public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); UserDao dao = new UserDao(); User user = dao.findUser(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("loginUser", user); if (user.getRole() == 2) { response.sendRedirect("admin/index.jsp"); } else { response.sendRedirect("index.jsp"); } } else { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } } }这段代码有两个关键细节。第一个是request.setCharacterEncoding("utf-8"),必须在读取任何参数之前调用,否则从表单提交的中文用户名会乱码。第二个是登录成功后根据user.getRole()值判断跳转方向,管理员进后台页,普通会员进前台首页。Session 里存的是整个 User 对象而不只是用户名,这样页面里可以随时展示当前用户的信息,不用再查一次数据库。
findUser方法是 UserDao 里的查询逻辑,SQL 是SELECT * FROM user WHERE username=? AND password=?,用 PreparedStatement 防止 SQL 注入。答辩时如果被问到安全相关的问题,这里是一个很好的回答点——为什么用 PreparedStatement 而不是直接拼接字符串,因为预编译可以避免注入攻击。
4.2 歌曲类别管理:后台增删改查的实现
歌曲类别管理是管理员专属功能,实现对歌曲分类的新增、修改、删除和查询。这个模块是典型的增删改查,代码结构清晰,适合作为新手入门的参考。类别管理涉及两个动作:类别列表展示和类别操作请求处理。
列表展示用 JSP 加 JSTL 标签完成,不直接在页面里写 Java 代码。操作请求统一走 CategoryServlet,通过method参数区分是新增、修改还是删除:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String method = request.getParameter("method"); if ("add".equals(method)) { String cname = request.getParameter("cname"); String description = request.getParameter("description"); CategoryDao dao = new CategoryDao(); boolean result = dao.addCategory(cname, description); if (result) { response.sendRedirect("categoryServlet?method=list"); } else { request.setAttribute("error", "添加失败"); request.getRequestDispatcher("admin/category_add.jsp").forward(request, response); } } else if ("delete".equals(method)) { int cid = Integer.parseInt(request.getParameter("cid")); CategoryDao dao = new CategoryDao(); dao.deleteCategory(cid); response.sendRedirect("categoryServlet?method=list"); } }这里有一个设计值得学习:用一个 Servlet 通过method参数处理多个操作,比每个操作建一个 Servlet 要精简,也方便在web.xml里只配置一个映射地址。删除操作执行成功后重定向到列表页,避免了刷新页面时重复提交删除请求,这是 POST-Redirect-GET 模式的简化版。
4.3 歌曲信息管理与前台展示
歌曲信息管理与类别管理类似,但多了两个维度:文件路径和试听次数。后台添加歌曲时,管理员需要填写歌曲名、歌手、类别、文件路径等信息。前台展示时,歌曲列表页按类别进行分类展示,并提供试听按钮。
前台歌曲列表的核心是分页查询。论文里的数据量不大,但分页逻辑是答辩时的高频考点,建议认真实现。常见做法是接收页码参数,计算起始偏移量,用 LIMIT 子句取当前页数据,同时查询总数算出总页数:
public List<Song> getSongsByPage(int pageNum, int pageSize) { String sql = "SELECT * FROM song ORDER BY create_time DESC LIMIT ?, ?"; List<Song> list = new ArrayList<>(); try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, (pageNum - 1) * pageSize); ps.setInt(2, pageSize); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { Song song = new Song(); song.setSid(rs.getInt("sid")); song.setSname(rs.getString("sname")); song.setSinger(rs.getString("singer")); song.setPlayCount(rs.getInt("play_count")); song.setFileUrl(rs.getString("file_url")); list.add(song); } } } catch (Exception e) { e.printStackTrace(); } return list; }这段代码的关键参数是(pageNum - 1) * pageSize,这是分页查询最容易出错的地方。MySQL 的 LIMIT 子句第一参数表示偏移量,第二参数表示返回行数。第一页时 pageNum 为 1,偏移量是 0;第二页时偏移量是 pageSize。如果忘记减一,第一页的数据会被跳过。
4.4 排行榜实现:试听次数与排序逻辑
排行榜模块是这份论文的亮点,原理不复杂:歌曲表里有一个play_count字段,每次试听时加一,排行榜就按这个字段降序排列,取前 N 条。关键点在于「试听加一」这个动作的触发时机和实现方式。
试听动作的前端实现是在歌曲列表页点击播放按钮时触发跳转或弹窗,同时调一个 Servlet 接口来更新播放次数:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int sid = Integer.parseInt(request.getParameter("sid")); SongDao dao = new SongDao(); dao.incrementPlayCount(sid); Song song = dao.findById(sid); response.sendRedirect(song.getFileUrl()); }incrementPlayCount对应的 SQL 是UPDATE song SET play_count = play_count + 1 WHERE sid = ?。注意这里用的是字段自增而不是先查后改,避免了并发下读到的旧值覆盖新值的问题,在 MySQL 的默认隔离级别下,这条 UPDATE 语句是行级原子操作的。排行榜页面查询时用SELECT * FROM song ORDER BY play_count DESC LIMIT 10,取试听次数最高的十首。
在本地测试时,我一般会写一个 SQL 脚本批量插入几首歌曲并手动更新 play_count 值,这样排行榜页面能直接看到效果。生产中试听次数的防刷问题(比如刷新一次就加一)在毕设阶段可以不做深入处理,但答辩时要有意识——面试官或者答辩老师可能会问「怎么防止用户刷排行榜」,准备好用 Session 或者 IP 限制的思路去回答,就比答不上来强很多。
5. 软件测试与避坑:从能跑到能答辩的排查清单
5.1 测试用例设计:四类核心功能的边界验证
这份论文有专门的软件测试章节,包含管理员登录、添加歌曲类别、添加歌曲信息、删除会员信息四个测试实例。这个思路值得保留,因为测试不是走过场,而是在答辩时支撑「系统可靠性」的直接证据。设计测试用例时,除了正常流程,一定要覆盖异常输入和边界情况。
管理员登录测试需要覆盖三类情况:正确的用户名密码能登录成功;错误的密码提示登录失败;空用户名或空密码被拦截。添加歌曲类别测试需要验证:合法类别名能添加成功;重复类别名被数据库唯一索引拦截;超长类别名被截断或提示。删除会员信息测试要验证:删除存在的会员成功后列表减少一条;删除不存在的会员时给出友好提示;删除时数据库返回影响行数为 0 的情况不能报异常。
这四类用例分别对应权限控制、表单验证、唯一约束、空结果处理。把这些测试结果截图保存,放进论文的测试章节里,比任何文字描述都有说服力。我在复现时会把每一类测试的运行环境(Tomcat 版本、浏览器类型、数据库字符集)和测试数据记录下来,答辩时被追问「这个系统到底测过没有」,直接把测试记录摆出来。
5.2 避坑记录一:JSP 页面中文乱码
现象:页面显示正常,但向数据库插入中文数据后,数据库里存的是乱码,或者页面上取出来的中文变成问号。
原因:三层编码不一致——JSP 页面编码、HTTP 请求编码、数据库连接编码。JSP 文件头没有声明pageEncoding="utf-8",表单提交时浏览器按默认编码发送请求,Servlet 读取时用的还是 ISO-8859-1,数据库表字符集又是默认的 latin1,三层对不上,中文必乱。
解决:统一四个位置——JSP 文件头加<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>;Servlet 的doPost方法开头调用request.setCharacterEncoding("utf-8");数据库连接 URL 加useUnicode=true&characterEncoding=utf8;建表时指定CHARSET=utf8mb4。四管齐下,问题基本不会再出现。
5.3 避坑记录二:Tomcat 10 启动后报 ClassNotFound
现象:本地安装的是 Tomcat 10,部署项目后启动,访问任何 JSP 页面都报java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet,或者 Servlet 类无法加载。
原因:Tomcat 10 是 Jakarta EE 的起点,Servlet API 的包名从javax.servlet迁移到了jakarta.servlet,而项目的代码和web.xml里依然使用的是旧的javax.servlet包名,导致类加载失败。
解决:最省事的方案是换回 Tomcat 9 或 8.5。如果想留在 Tomcat 10,需要把所有代码里的 import 改成jakarta.servlet.*,同时web.xml头的xmlns也要改成 Jakarta 命名空间。对毕设项目来说,换版本是最快路径,改包名的成本完全没必要。
5.4 避坑记录三:MySQL 8.0 驱动认证失败
现象:用论文里提到的 JDBC 代码连接本地 MySQL,启动后提示Access denied for user 'root'@'localhost'或者Unable to load authentication plugin 'caching_sha2_password'。
原因:MySQL 8.0 默认的认证插件是caching_sha2_password,而老版本 JDBC 驱动(5.x 系列)只认mysql_native_password,两边对不上导致连接被拒。
解决:首选方案是把 JDBC 驱动换成 8.0.33,同时DRIVER常量改为com.mysql.cj.jdbc.Driver。如果项目必须用老驱动,可以在 MySQL 里执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';修改认证插件,但这属于绕路方案,不推荐。
5.5 避坑记录四:JDBC 资源泄漏导致连接耗尽
现象:系统运行一段时间后,某些页面打开越来越慢,最终报Too many connections,Tomcat 日志里全是连接超时。
原因:代码里获取了 Connection,但在异常分支里没有关闭,或者close()被提前 return 跳过了。MySQL 默认最大连接数是 151,泄漏到一定数量后所有新请求都无法获取连接。
解决:代码里统一使用 try-with-resources 语法,或者在 finally 块里关闭资源。对外层调用方法来说,拿到 Connection 之后必须确保close()一定执行——我用的是一个简单的原则:谁打开谁关闭,打开和关闭在同一层方法里完成,不把 Connection 往下传。同时给 MySQL 配置一个监控,本地开发时看SHOW PROCESSLIST有没有大量 Sleep 状态的连接,有就说明泄漏了。
6. 进阶:从论文代码到能演示的毕设,三处值得改写的关键点
6.1 把 JSP 里的脚本片段迁到 Servlet 与 DAO
如果你把这份论文的代码完整跑通了,打开任何一个 JSP 文件,大概率会看到满屏的<% %>。这在 2019 年还算常规操作,但现在答辩时被问到「为什么把业务逻辑写在 JSP 里」,很难给出让人信服的回答。所以进阶的第一步,就是把脚本片段迁出去。
具体做法是:JSP 里只保留 HTML 和 JSTL 标签,数据准备逻辑全部放到 Servlet 里完成。比如歌曲列表页,原来是在 JSP 里直接声明 List 变量、调 DAO 方法查数据;改写后在 Servlet 里查好数据放到 request 或 session 里,再 forward 转发给 JSP,JSP 只用c:forEach遍历展示。改写完的代码结构会跟 Spring MVC 的模式接近,答辩时讲起来你的架构思维会加分不少。
6.2 给排行榜增加防刷机制
论文里的试听次数是点一次加一次,没有任何限制。如果答辩现场演示时你多点了两次刷新,排行榜的名次就变了,这会直接影响演示效果。我建议在试听接口里加一个简单的防刷判断:基于 Session,同一个用户对同一首歌五分钟内只计一次试听。
HttpSession session = request.getSession(); String key = "play_" + sid; Long lastPlayTime = (Long) session.getAttribute(key); long now = System.currentTimeMillis(); if (lastPlayTime == null || now - lastPlayTime > 300000) { dao.incrementPlayCount(sid); session.setAttribute(key, now); }这段逻辑不复杂,但能让你的系统在演示时「表现稳定」——不管点多少次,排行榜只在第一次点击时变化。同时它也是一个很好的答辩话题,展示了你考虑了实际的恶意行为场景。
6.3 用 Git 管理版本并准备答辩演示脚本
最后一个建议和代码无关,但对毕设顺利通过很关键:把整个项目纳入 Git 管理。把初始部署成功的版本标记为v1.0,每完成一次重构打一个 tag。答辩前准备一页纸的演示脚本,按「登录 → 添加类别 → 修改歌曲 → 查看排行榜 → 删除会员」的固定顺序走一遍,确保每一个步骤都提前点过,不要在现场临时操作。
我曾经帮一个学弟调试类似的 JSP 项目,他答辩前夜还在改代码,原因是前一版能跑,加了新功能之后登录页挂了,但源码已经被覆盖,找不回旧版。从那以后我每次做项目都会先把能跑的版本提交一次,再动任何代码,这个习惯帮我避开了无数次「改坏了但找不回来」的局面。希望你也能从这份论文里把该踩的坑先踩一遍,然后把能跑通的版本稳稳握在手里,后面的路就好走了。希望帮到你。
本文还有配套的精品资源,点击获取