简介:基于Java Web技术的投票系统毕业设计源码包,面向计算机专业毕业生与JavaWeb初学者,完整展示Servlet、JSP、MVC分层及数据库交互在真实项目中的落地方式,可帮助理解投票主题管理、选项统计、用户投票与结果展示等核心业务逻辑。压缩包共136个文件、约5.7MB,内含19个Java源文件与对应class文件、10个jar依赖库、4个JSP页面、3个XML配置,以及CSS、JavaScript和图片素材,覆盖后端处理、前端展示和容器部署所需的主要类型。已有292人学习下载。通过研读代码可掌握基于JDBC的MySQL数据访问、MVC模式下的DAO设计、Session防重复投票及参数化查询防注入等关键实践,同时结合web.xml配置理解Servlet映射与过滤器机制,并可参考Page类等工具代码学习分页查询与统一数据库连接管理,整体目录结构清晰,适合课程设计参考、毕业答辩准备或面试项目复盘。
1. 投票系统Java Web源码包:毕业设计最稳的选题,也是最容易翻车的交付物
每年毕设季,"投票系统-Java Web项目源码.zip"都在应届生之间反复流转。你满怀期待地解压,翻出JSP页面、Servlet源码、SQL脚本和一份"运行说明",但从解压到看见登录页,中间隔着JDK版本、Tomcat配置、数据库编码几道暗坑。我顺着这个标题把一套典型Java Web投票系统源码从拿到手到答辩能演拆开讲:怎么验货、怎么配环境、投票逻辑怎么读、哪些坑最要命。投票系统这类题目,和健康饮食推荐系统、旅游小程序等Java Web毕设一样,毕业设计任务书真正考核的是你能不能把一套Web应用完整跑通并讲清原理——这篇笔记适合刚拿到源码不知道先干嘛的同学,也适合想在此基础上改成自己题目的同学。
2. 拆开zip先看家底:投票系统该有的模块与这套源码的技术选型
2.1 解压后先别急着点开IDE:用tree命令看清目录结构
不少人拿到zip的第一反应是双击解压,然后把整个文件夹拖进IDE,结果要么导入后一堆红叉,要么根本找不到项目入口。我自己的习惯是先在命令行把目录树打出来,花两分钟看清这包东西的真实结构,再做下一步。这个习惯在调试别人源码的时候特别值钱,因为很多毕设打包的人自己都没认真整理过目录。
mkdir ~/vote-project && cd ~/vote-project unzip vote.zip -d vote-src cd vote-src && tree -L 3unzip的-d参数指定解压目标目录,避免压缩包里的文件散落一地;tree -L 3限制只展开三层,足够看清结构。Windows用户在解压后的文件夹里打开PowerShell,用tree /F效果一样。如果unzip这一步就报错,先别往下走,第5章里有zip相关的完整坑位说明。
一个正常的Java Web毕设投票系统,解压后应该能看到四类东西:src目录放Java源码,包名常见com.xxx.vote,内部按servlet、service、dao三个包分层;WebRoot或web目录是Web应用根,里面有WEB-INF,WEB-INF下得有web.xml、classes、lib;sql或db目录放建库建表脚本,文件名常见vote_db.sql;最后是README.txt,里面写环境版本和运行步骤——但这份说明的质量全看作者心情,大部分时候只能当参考。
如果解压后没有src也没有WebRoot,只有一堆散落的jsp和.java文件,说明这是最原始的版本。这种包不是不能跑,但你要自己把它们组装成合法Web应用,耗时翻倍。验货阶段就发现这一点,能帮你尽早决定要不要换一套源码,而不是搭进去两三个晚上。
2.2 技术选型:为什么毕设偏爱JSP+Servlet而不是Spring Boot
投票系统在毕业设计里属于"业务量适中、模块完整、好答辩"的经典题目。我经手过的源码包,七成是JSP+Servlet+JDBC的三层结构,少部分是SSM,极少数用Spring Boot。标题下标的是Java Web,拿到JSP+Servlet的概率最高。
这个选型从技术上看不算新潮,但对付答辩非常合适:不需要Maven下载成百上千的依赖,不需要理解Spring容器初始化,把war包或目录丢进Tomcat就能跑。翻src目录的时候,看到@WebServlet注解,说明是Servlet 3.0以上的写法,JDK8配Tomcat8基本无痛;看到web.xml里一长串servlet-mapping,那是更旧的写法,兼容性反而更好,Tomcat5到9都能吃。
不利的一面是,JSP+Servlet的代码通常很直白:控制器里全是request.getParameter,SQL直接写在DAO类里,业务逻辑和流程控制裹在一起。答辩老师问"你这系统怎么分层的",你要能指着包名说清楚:servlet或controller只负责拿参数和跳转,service处理业务规则,dao只管数据库访问。这比纠结框架是否先进重要得多。
顺带说一句,如果你拿到的是SSM版本,那环境步骤就要加Spring配置文件、MyBatis映射文件,跑通成本高一些,好处是分层更标准。判断方法很简单:看src下有没有applicationContext.xml、mybatis-config.xml这类文件。有,就按SSM的路子配;没有,就按JSP+Servlet的老路子走。
2.3 数据库设计:投票系统该有的核心表与防重复兜底
一个能站住脚的投票系统,数据库至少要覆盖用户和投票两块。用户表管登录与权限,投票相关一般拆成主题、选项、记录三张表。我见过的一致性比较好的建表脚本长这样:
CREATE DATABASE IF NOT EXISTS vote_db DEFAULT CHARACTER SET utf8mb4; USE vote_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role TINYINT NOT NULL DEFAULT 0 COMMENT '0-普通用户 1-管理员' ); CREATE TABLE t_vote ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, start_time DATETIME DEFAULT NULL, end_time DATETIME DEFAULT NULL, status TINYINT DEFAULT 1 COMMENT '1-进行中 0-已结束' ); CREATE TABLE t_vote_option ( id INT PRIMARY KEY AUTO_INCREMENT, vote_id INT NOT NULL, option_name VARCHAR(100) NOT NULL, vote_count INT DEFAULT 0 ); CREATE TABLE t_record ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, vote_id INT NOT NULL, option_id INT NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_vote (user_id, vote_id) );这段脚本里最值得玩味的是最后一行UNIQUE KEY uk_user_vote (user_id, vote_id)。它保证即使业务层忘了判断,数据库自己也会拒绝同一用户对同一主题的第二次投票,直接抛唯一键冲突异常。很多毕设源码的防重逻辑并不严谨,数据库这层唯一索引是最后的后悔药。
另外注意password字段的类型。VARCHAR(32)大概率是MD5后落库,VARCHAR(100)可能是加盐哈希。答辩前要能讲出来,因为"密码是不是明文存储"几乎必被问。如果源码里直接存明文,建议至少改成MD5,改动量很小但安全观感完全不同。
2.4 验货:三步判断这套源码能不能直接跑
拿到源码包先别急着配环境,花五分钟做三个检查,能帮你避开后面一整晚的无头绪排错。
第一步看lib目录。WebRoot/WEB-INF/lib里必须要有mysql-connector-java的jar包,否则项目能编译,但DAO一执行就报ClassNotFoundException: com.mysql.jdbc.Driver。如果库里没有,自己去下载一个mysql-connector-java-5.1.49.jar放进去,这个版本配合MySQL5.7很稳。
第二步看servlet写法。翻开web.xml或任意一个Servlet,确认是注解还是XML配置。注解@WebServlet要求Tomcat7以上,JDK8直接配Tomcat8就行;XML配置通用性最强。这一步能帮你定死环境版本,不用从Tomcat11上面白白踩一遍catalina异常再回头。
第三步看SQL脚本的字符集。打开sql文件,看第一行有没有DEFAULT CHARACTER SET utf8mb4。没有的话导入后中文变问号的概率极高,第5章的5.3有完整解法。三步做完,你对这套源码的"身体状态"已经有了判断:能跑、小修能跑、还是得大动。这个判断值回票价。
3. 从JDK到Tomcat:把投票系统跑起来的完整环境配置与部署步骤
3.1 版本选型:JDK8+Tomcat8+MySQL5.7是最省心的组合
投票系统这种老牌Java Web项目,对环境的要求是"稳定大于新潮"。JDK8配Tomcat8,再加MySQL5.7,是我给大多数人的建议。这套组合跟JSP+Servlet时代的代码兼容性最高:javax.servlet在JDK8里是标准配置,mysql-connector-java-5.x驱动也能正常加载,几乎不会出现"代码是对的但环境不认"的情况。
如果机器上已经装了JDK11或更高,也不是完全不能跑,但要有心理准备:JDK11把Java EE模块从默认类库移除了,老项目运行时容易报ClassNotFoundException。处理这类问题花的时间,比装一个JDK8多得多。所以我一般会劝人直接装JDK8,配好JAVA_HOME,一劳永逸。
Windows下配置JAVA_HOME的步骤:右键"此电脑"→"属性"→"高级系统设置"→"环境变量",新建JAVA_HOME指向JDK安装目录,例如C:\Program Files\Java\jdk1.8.0_202,再在Path里追加%JAVA_HOME%\bin。配置完成后开一个新命令行窗口,输入java -version,看到1.8字样才算成功。如果输入后还是旧版本,检查Path里是不是有其他JDK的bin目录排在前面——这是装了多个JDK时最常见的翻车原因。
3.2 数据库导入与JDBC连接参数:中文不乱码的关键
数据库是地基,先建库建表。打开命令行进入MySQL执行脚本:
mysql -uroot -p123456 --default-character-set=utf8mb4 < /path/to/vote_db.sql这里--default-character-set=utf8mb4很关键,它让客户端发送给MySQL的SQL按utf8mb4编码解析,避免脚本里中文注释在导入时报错变成乱码。如果你习惯用Navicat,拖入sql文件时也要把连接编码改成utf8mb4。
导入后验证:
USE vote_db; SHOW TABLES; SELECT COUNT(*) FROM t_user;四张表都在,且t_user里有初始账号(通常admin或test),数据库这步就稳了。初始账号一般写在README里;README没写就在sql文件里搜INSERT INTO t_user。
接下来改JDBC连接配置,它一般在src下的jdbc.properties或db.properties,找不到就在DAO里搜DriverManager.getConnection。典型内容:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/vote_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456jdbc.url里的四个参数值得背下来:useUnicode=true配合characterEncoding=utf8是中文不乱码的前提;useSSL=false避免MySQL8连不上时那个SSL握手警告;serverTimezone=Asia/Shanghai解决驱动与数据库时区差异。如果MySQL是5.7且驱动是5.1.x,后两个不写能跑,但没有坏处。密码明文写在配置文件里,答辩时可以提一句"生产环境需要加密配置,毕设里为了演示方便保持明文",这是诚实又不减分的说法。
3.3 项目导入与部署:把src和WebRoot变成Tomcat能跑的应用
部署有两种常规做法。一种是在IDE里导入后让IDE管理Tomcat运行,适合开发调试;另一种是直接把项目展开到Tomcat的webapps目录,适合答辩现场演示,不依赖IDE,开机能跑。
先说明第二种。假设Tomcat在C:\apache-tomcat-8.5.xx,把项目里WebRoot下的内容整体复制到webapps/vote:
mkdir -p $TOMCAT_HOME/webapps/vote cp -r WebRoot/* $TOMCAT_HOME/webapps/vote/再编译Java源码到WEB-INF/classes:
javac -encoding UTF-8 -cp "$TOMCAT_HOME/webapps/vote/WEB-INF/lib/*" \ -d $TOMCAT_HOME/webapps/vote/WEB-INF/classes \ src/com/xxx/vote/**/*.javajavac参数说明:-encoding UTF-8保证源码中文注释和字符串常量不乱码;-cp指定编译依赖的jar包,用通配符lib/*把Tomcat和MySQL驱动都带上;-d指定class输出目录。编译完确认vote/WEB-INF/classes/com/xxx/vote下已有.class文件,才算真的成功。
IDE导入的做法对你来说可能更顺手:Eclipse用File→Import→General→Existing Projects into Workspace,选中解压后的目录,然后右键项目Run As→Run on Server;IDEA里配置好Tomcat Server后,Deployment加一个Artifacts,Application context设为/vote,点击运行。IDE会自动完成编译和部署,省去手动javac的繁琐,但代价是你得保证IDE里配置的Tomcat版本和JDK匹配,否则又回到3.1的坑。
3.4 启动验证与日志排错:看到什么才算跑通
部署完成后启动Tomcat。Windows点startup.bat,Linux/Mac执行startup.sh,然后别急着点浏览器,先把日志盯上:
tail -f $TOMCAT_HOME/logs/catalina.out看到INFO: Server startup in xxx ms才算容器起来了。这时候再访问http://localhost:8080/vote/index.jsp。如果页面能打开但登录不进,多半是数据库连接或初始账号问题;如果页面直接打不开,回来看catalina.out里第一个Exception。
排错的原则是:只盯第一个异常,不要被后续一串Caused by带走节奏。把第一段异常拿去搜索,投票系统是老项目,网上踩坑记录极多,基本都能找到答案。我最不建议的做法是页面打不开就反复重启Tomcat,重启十次也解决不了配置问题,日志才是唯一可信的黑匣子。
4. 投票业务核心代码:登录、投票、计票与防重复的实现
4.1 登录与Session:投票系统的人从哪里来
投票系统得先知道"谁在投",所以登录是第一个闭环。先看登录Servlet:
@WebServlet("/login") public class LoginServlet extends HttpServlet { private UserDao userDao = new UserDao(); protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); User user = userDao.findByUsernameAndPassword(username, password); if (user == null) { req.setAttribute("error", "用户名或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); return; } req.getSession().setAttribute("loginUser", user); resp.sendRedirect(req.getContextPath() + "/index.jsp"); } }逻辑很直接:表单里的用户名密码拿去数据库查,查不到就回登录页并带一个error提示,查到了就把User对象塞进Session再跳主页。req.getContextPath()是必须写的,它会动态补上部署时的应用上下文路径,避免你写死/vote导致以后改名就得改代码。
setAttribute的时机值得记一下:Session里的loginUser是后面所有投票接口判断登录状态的依据。如果登录成功后没有这一行,投票Servlet里每次去Session取都会拿到null,然后被踢回登录页。很多同学在联调时遇到"能登录但一投票就跳回登录页",八成是这里漏了。
4.2 投票主流程:从点击按钮到记录落库
用户在前端点"投票"按钮,表单把voteId和optionId提交到VoteServlet。这一层在典型源码里长这样:
@WebServlet("/vote") public class VoteServlet extends HttpServlet { private VoteService voteService = new VoteService(); protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); HttpSession session = req.getSession(); User user = (User) session.getAttribute("loginUser"); if (user == null) { resp.sendRedirect(req.getContextPath() + "/login.jsp"); return; } int voteId = Integer.parseInt(req.getParameter("voteId")); int optionId = Integer.parseInt(req.getParameter("optionId")); boolean success = voteService.vote(user.getId(), voteId, optionId); if (success) { resp.sendRedirect(req.getContextPath() + "/result.jsp?vid=" + voteId); } else { req.setAttribute("msg", "您已经参加过本次投票"); req.getRequestDispatcher("/index.jsp").forward(req, resp); } } }两个细节:Integer.parseInt前最好确认参数不是空,否则会抛NumberFormatException,更稳的写法外面包一层try-catch再给用户友好提示;投票成功后用sendRedirect而不是forward,避免用户按F5刷新时重复提交表单。这个细节答辩时可以直接讲成"防止表单重复提交",很拉好感。
4.3 计票逻辑:别在Java里累加,让SQL来算
结果页要展示每个选项的票数和百分比。常见的做法是直接查统计结果,不在Java里循环累加:
public List<VoteOption> countVotes(int voteId) { String sql = "SELECT o.id, o.option_name, COUNT(r.id) AS votes " + "FROM t_vote_option o " + "LEFT JOIN t_record r ON o.id = r.option_id " + "WHERE o.vote_id = ? " + "GROUP BY o.id, o.option_name " + "ORDER BY votes DESC"; // 执行查询,封装为List<VoteOption>返回 }LEFT JOIN和INNER JOIN的区别在这道SQL里是决定性的:LEFT JOIN保证那些还没人投过的选项也能出现在结果里,票数为0;INNER JOIN会把0票的选项直接过滤掉,列表看起来就少了选项,这是很隐蔽的bug。COUNT(r.id)统计的是右表关联上的记录数,不掺和选项本身。
GROUP BY后面的字段要和SELECT里的非聚合列对齐,这在MySQL 5.7默认设置下没问题,MySQL 8.0开启only_full_group_by时也是一样要求。有些源码会在t_vote_option表里维护vote_count字段,投票时UPDATE vote_count = vote_count + 1。这种方案查询确实快,但只要投票和更新count不在同一事务里,count和t_record的记录数就可能对不上。被老师问到,回答"为了保证计票与记录一致性,我选择实时COUNT"是会加分的。
4.4 防重复投票:Session、Cookie、IP与数据库唯一索引的配合
防重复投票是投票系统的核心考点。一个合格源码至少要覆盖一层防护,我在答辩现场听到最稳的回答是"Session快速拦截加数据库唯一索引兜底"。
Session层的思路是用户投完票后在Session里存一个标记,下次再进投票接口先查标记。但Session的缺陷太明显:换个浏览器或清掉Cookie就失效。因此真正靠得住的是数据库唯一索引,它把兜底沉到最底层:
public boolean vote(int userId, int voteId, int optionId) { Connection conn = null; PreparedStatement ps = null; try { conn = dataSource.getConnection(); conn.setAutoCommit(false); String insert = "INSERT INTO t_record(user_id, vote_id, option_id) VALUES(?,?,?)"; ps = conn.prepareStatement(insert); ps.setInt(1, userId); ps.setInt(2, voteId); ps.setInt(3, optionId); ps.executeUpdate(); conn.commit(); return true; } catch (SQLIntegrityConstraintViolationException e) { return false; // 唯一索引拦下重复投票 } catch (SQLException e) { conn.rollback(); throw new RuntimeException("投票失败", e); } finally { // 关闭连接,省略细节 } }关键点是捕获SQLIntegrityConstraintViolationException。只要触发唯一索引,就说明用户已投过,返回false,Servlet层就能提示。那为什么不用"先SELECT检查再INSERT"?因为先查后插存在竞态窗口,两个并发请求可能同时通过检查,最终还是会有一个撞上唯一索引异常。与其靠查,不如直接让索引裁决。
再补一句关于IP限制的坑:同一宿舍四个人共享一个出口IP,如果系统用IP做死限制,三个人投完你就被锁了,答辩演示时非常尴尬。Cookie限制作为辅助没毛病,但不能当唯一防线,清掉就没了。最优雅的表述是:Session做快速拦截,数据库唯一索引做最终裁决。这套说辞在开题和答辩里都站得住。
5. 答辩前必查的避坑清单:这5个问题不解决,演示现场一定翻车
5.1 zip解压报错或提示要密码:伪加密与zip密码移除的真实情况
现象:从网盘或群文件下载到投票系统源码zip,双击解压时弹出密码框,或解压到一半报"CRC校验失败"。
原因:一种情况是zip伪加密——打包时把加密标志位设成1,但文件内容并没有真正加密,解压软件看到标志位就要求输密码。另一种是压缩包在传输中损坏,中央目录和文件数据对不上。
解决:先用7-Zip打开压缩包。如果7-Zip能直接看到文件列表甚至预览内容,基本可以判定是伪加密。这类压缩包可以找ZipCenOp之类的开源小工具把加密标志位清掉,再正常解压;如果确认是损坏,用7-Zip的"修复压缩文件"功能或zip -F命令尝试重建中央目录。这个坑排在首位是因为它卡在源头,打不开包后面全是白说。
5.2 JDK版本与javax.servlet:一启动就ClassNotFoundException
现象:Tomcat启动时报ClassNotFoundException: javax.servlet.http.HttpServlet,或者部署后页面一访问就抛NoClassDefFoundError。
原因:JDK11及以上把Java EE模块从默认类库移除,javax.servlet不再随JDK提供;另一种情况是项目classpath里压根没有servlet-api.jar。现在很多笔记本自带JDK17,同学直接把老源码丢进新环境,第一步就翻车。
解决:最稳的是装JDK8并把JAVA_HOME指过去。Tomcat8自带servlet-api.jar,正常情况无需额外放置,但要确认classpath里没混入某个旧版本的servlet-api。如果坚持用JDK11,Tomcat得升到Tomcat10以上,源码里的javax全部改成jakarta——这个改动在老毕设里牵一发动全身,强烈不推荐。
提示:JDK和Tomcat的版本要成对升级,只动其中一个,老源码就会用报错提醒你什么叫兼容性。
5.3 数据库中文乱码:从连接URL到表编码的完整链路
现象:页面显示投票标题是??或"鏄庡ぉ",数据库里存进的就是乱码,或SQL导入时中文注释直接变问号。
原因:三层链路只要有一层不是UTF-8就会乱。第一层是MySQL连接URL没指定characterEncoding;第二层是表字符集不是utf8mb4;第三层是导入脚本时客户端编码不对。三层里任何一层出问题,最终落库的都是乱码。
解决:按顺序排查。先给表统一字符集:
ALTER TABLE t_vote CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; ALTER TABLE t_vote_option CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;再确认JDBC连接串里有useUnicode=true&characterEncoding=utf8,最后导入SQL时带--default-character-set=utf8mb4。改完三处重启Tomcat,重新插入一条中文数据验证,不要光盯着已经乱掉的老数据看——旧数据不会自动变干净,重新插一条没问题才叫解决。
5.4 Tomcat端口被占用:netstat一条命令定位
现象:startup.bat一启动就闪退,或在Eclipse/IDEA里启动Tomcat报Port 8080 is already in use。
原因:残留的Tomcat进程没杀干净,或者机器上别的服务占了8080。这跟源码本身没关系,但答辩演示现场遇到它,你就会在老师面前反复重启,气氛非常尴尬。
解决:Windows命令行执行:
netstat -ano | findstr :8080记下占用8080的PID,再执行:
taskkill /PID 12345 /F如果8080上跑着别的没法杀的服务,就改Tomcat的conf/server.xml,把Connector的port改成8081,然后访问http://localhost:8081/vote/index.jsp。改完端口记得访问链接同步换掉,我见过不止一个人改完端口不换URL,对着8080的404盯了十分钟。
5.5 404访问不到页面:context path与部署结构
现象:Tomcat正常启动,但访问http://localhost:8080/vote/index.jsp显示404,或者IDE里只能访问到根路径访问不到项目页。
原因:应用部署名和你URL里写的路径不一致。webapps目录下叫vote,URL写vote,两者必须完全对上;IDE部署时Application context配置成/,那访问/vote自然404。
解决:先确认webapps下的目录名,再看URL里的路径是否一致。如果是IDE部署,双击Tomcat配置,看Deployment面板里Application context是不是/vote。还可以直接把展开的目录丢进webapps,用http://localhost:8080/你命名的目录/index.jsp访问。404排错的核心是一句话:请求路径和部署路径逐段对上,不存在玄学,只存在没对上的字符串。
6. 从"能跑"到"有亮点":给投票系统加结果图表、日志与验证码
6.1 用ECharts把计票结果变成图表
答辩时老师最常问的不是"你怎么实现投票",而是"你做了哪些自己的东西"。只把源码跑通、界面原样照抄,很难拿高分。三个改动工作量不大,但都能成为实际亮点。第一个是给结果页加ECharts饼图,替换掉原来干巴巴的百分比文字。做法是在Java端把计票结果拼成JSON字符串放进请求属性:
List<VoteOption> list = voteService.countVotes(voteId); StringBuilder sb = new StringBuilder("["); for (int i = 0; i < list.size(); i++) { if (i > 0) sb.append(","); sb.append("{\"name\":\"").append(list.get(i).getOptionName()) .append("\",\"value\":").append(list.get(i).getVotes()).append("}"); } sb.append("]"); req.setAttribute("chartData", sb.toString());result.jsp里引入ECharts的CDN,初始化时读chartData。这个改动只动一个页面和一个方法,半小时完成,但视觉效果完全是两个层次,演示时老师的第一观感会好很多。
6.2 加一条审计日志和一个验证码,让答辩有故事可讲
第二个改动是加审计日志。不需要引入log4j全家桶,直接在数据库建一张t_log表,在VoteServlet投完票后写一条记录:谁、什么时间、投了哪个选项。这样一个简单的操作留痕,能回答"有人刷票你怎么查"这类的追问。
第三个改动是给登录加验证码。手写一个验证码Servlet,用BufferedImage画四位随机数字,答案存Session,登录时比对。注意验证码要在登录Servlet的doPost里通过session取出比对,比对通过才继续校验用户名密码。
这三个改动都不碰核心投票逻辑,风险小,产出可感知。我的习惯是:宁可只把一个图表讲透并现场演示,也不要堆三个自己都说不清的功能——被追问细节时卡壳,比功能少更扣分。把这些改动真正在源码上敲一遍,你才算是把"别人的毕设"变成了"自己的项目"。希望这个思路帮到你。
本文还有配套的精品资源,点击获取