ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Servlet+JSP+MySQL+Bootstrap学生成绩管理系统全解析

Servlet+JSP+MySQL+Bootstrap学生成绩管理系统全解析 简介这是基于Java技术栈开发的一套学生成绩管理系统项目包整合了Servlet、JSP、Bootstrap与MySQL。系统面向Java Web初学者、课程设计及毕业设计场景覆盖学生信息维护、成绩录入、多条件查询、统计分析和数据备份恢复等功能。代码采用MVC分层设计Servlet负责请求分发与业务控制JSP完成页面展示Bootstrap提供响应式界面MySQL承担数据持久化各模块职责清晰适合按需研读。压缩包共575个文件大小约9.17MB包含85个Java源文件、74个JSP页面、170个class编译类、122个JavaScript脚本、56个CSS样式表另附SQL数据库脚本、项目配置文件、依赖jar包及前端图片资源结构较为完整。将项目导入开发工具后可以直观看到从Http请求到Dao数据访问的完整调用链也可参考其前后端交互方式与表单校验逻辑。目前已有3118人学习或下载是一个值得参考的Java Web综合实战案例。1. 学生成绩管理系统这套经典的 JavaWeb 组合为什么还在被反复做JavaServletJSPBootstrapMysql 学生成绩管理系统几乎是中文互联网上出现频率最高的 JavaWeb 课设/毕设题目之一。它的核心思路不复杂Java 写业务逻辑Servlet 接收浏览器请求JSP 动态渲染页面Bootstrap 给这套老技术栈套上一层现代样式MySQL 负责把学生、课程、成绩落库。你搜到的“-2”多半是作者传网盘时的版本序号不用太在意它不影响技术内容。这套系统能解决的问题很具体给学校信息管理做一个能用的成绩录入和查询页面让管理员维护学生和课程数据让老师录成绩、学生查成绩。适合两类人——一类是刚学完 Java 基础、想通过完整项目把 Servlet/JSP/数据库串起来的新手另一类是手里维护着老 JavaWeb 项目、需要快速理解同类代码结构的从业者。别嫌它老正是因为老每一层都没藏什么黑匣子你才容易全链路看懂。2. 技术选型与项目骨架ServletJSP 为什么还没被 Spring Boot 完全取代2.1 Servlet 与 JSP 的分工谁在接请求谁在画页面很多第一次做这个项目的人会把 Servlet 和 JSP 混在一起用最常见的翻车方式是在 JSP 里写大段 Java 代码把页面变成一锅粥。其实这两个东西的分工从设计之初就定死了Servlet 是控制器负责接收 HTTP 请求、调用业务方法、决定跳转到哪里JSP 是视图只负责把后台准备好的数据渲染成 HTML。放到学生成绩系统里直观的分工是这样浏览器点“登录”按钮请求先落到 LoginServletServlet 校验用户名密码成功后跳转到成绩列表的 Servlet那个 Servlet 从 MySQL 查出成绩数据放进 request 作用域再转发给 list.jspJSP 里用 EL 表达式和 JSTL 标签把数据一行行渲染进表格。Bootstrap 在这里做的事很简单——提供现成的表格、按钮、导航栏样式让你不用自己写 CSS。用这套组合而不是直接上 Spring Boot倒不是说技术越老越好而是 Servlet 能让你看到 HTTP 从请求到响应最原始的一条线。Spring Boot 封装得太干净新手做完一遍往往说不清楚 dispatcher 和拦截器在哪个环节干了什么。我一般建议课设、毕设、或者想补 JavaWeb 底层知识时选这套如果是公司正式项目且没有老代码包袱那确实没必要从 Servlet 起手。2.2 用 Maven 建一个可运行的 JavaWeb 骨架目录结构与 pom.xml我见过不少同学用 Eclipse Dynamic Web Project 手动导 jar 包也能跑起来但依赖一旦冲突就非常痛苦。这个项目本身不大但涉及 Servlet API、JSTL、MySQL 驱动三组依赖用 Maven 管理最省心打包成 war 丢进 Tomcat 就能跑。目录结构按下面的来后面每个类放哪都不会迷路student-score/ ├── pom.xml └── src/main/ ├── java/com/example/ │ ├── filter/LoginFilter.java │ ├── servlet/LoginServlet.java │ ├── servlet/ScoreServlet.java │ ├── service/ScoreService.java │ ├── dao/BaseDao.java │ ├── dao/ScoreDao.java │ └── model/User.java ├── resources/db.properties └── webapp/ ├── WEB-INF/web.xml ├── static/css/bootstrap.min.css ├── static/js/bootstrap.bundle.min.js ├── login.jsp └── score/list.jspstatic 目录放 Bootstrap 的 CSS 和 JS 文件直接从官网下载压缩包解压出 dist 目录复制过来就行。webapp 根目录下是 JSP 页面WEB-INF 目录用来放 web.xml 和后续的 class 文件浏览器不能直接访问 WEB-INF 里的东西这是个重要的边界。pom.xml 里最需要留意的是依赖坐标和 scope。Servlet 和 JSP 的依赖在 Tomcat 里已经自带所以 scope 必须写成 provided否则打包出来的 war 里会重复带一套类库部署时可能引发诡异冲突。MySQL 驱动则要随项目走用不 providedproject modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdstudent-score/artifactId packagingwar/packaging version1.0.0/version properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency /dependencies /project这段配置的逻辑javax.servlet-api 和 jsp-api 是编译时依赖运行时由 Tomcat 提供jstl 必须打进 war因为 Tomcat 10 之前的版本不默认带它mysql-connector-j 装的是 MySQL 官方维护的 Connector/J。如果你是用的 Tomcat 10要注意包名已经换成 jakarta.servlet依赖坐标也要对应调整否则会报 ClassNotFoundException 这类很基础的错误。版本号不是死的你本机 MySQL 是 5.7 且不想升级驱动时可以把连接器换回 5.1.x但驱动类名和连接参数也要同步调整这个在第 3 章细讲。2.3 Bootstrap 的引入方式不做前端构建直接引静态资源Bootstrap 在 ServletJSP 项目里的引入方式和你在 Vue/React 项目里那套完全不同。这里不需要 npm、webpack、vite直接把 bootstrap.min.css 和 bootstrap.bundle.min.js 这两个文件丢进 webapp/static 目录即可。JSP 页面里用 link 标签引入 CSS需要交互的页面在 body 底部引入 JS。有一个细节很容易踩路径别写死成static/css/bootstrap.min.css。因为项目部署后上下文路径contextPath可能叫这个名字那个名字你用相对路径浏览器会把它拼在当前的 URL 路径下面经常出现 404。稳妥做法是用 JSP 的 EL 表达式动态拼% page contentTypetext/html;charsetUTF-8 languagejava % !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1 title登录 - 学生成绩管理系统/title link relstylesheet href${pageContext.request.contextPath}/static/css/bootstrap.min.css /head /html逻辑说明${pageContext.request.contextPath} 会输出当前项目的上下文路径比如/student-score这样无论如何部署CSS 地址都是完整的。把 Bootstrap 放进本地还有一个隐藏好处内网环境或者机房无外网时页面样式不会因为你访问不到 CDN 而崩溃这个在真实答辩演示时很关键。你要是愿意用 CDN 也不是不行但万一现场断网评委看到的就是一个没有样式的裸列表页场面很难看。3. 数据库设计与账号体系成绩表结构决定了后面少改多少代码3.1 四张核心表用户、学生、课程、成绩怎么建学生成绩管理系统的数据模型非常经典系统里有用户登录账号有学生有课程有成绩。四者关系是学生选课程成绩挂在学生和课程组合下面。建表时我会把用户单独建表而不是把账号密码塞进学生表——因为老师也需要登录系统管理数据如果把账号字段塞进 student 表老师的账号就没地方放。下面这套 SQL 是我常用的初始版本角色字段用字符串存扩展性好踩坑少你直接在 MySQL 里执行即可CREATE DATABASE IF NOT EXISTS student_score DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_score; CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT ADMIN ); CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, class_name VARCHAR(50) ); CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL, credit DECIMAL(3,1) DEFAULT 0 ); CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,1), exam_date DATE, UNIQUE KEY uk_student_course (student_id, course_id), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(id) );这段设计的几个关键点score 表不直接存课程名和学生名只存外键 id查询时再用 JOIN 把名称带出来。这样改学生姓名或课程名称时成绩记录不需要动。成绩字段用 DECIMAL(5,1)能存 0.0 到 999.9 的分值大多数高校的百分制和五级制都够用。唯一索引 uk_student_course 是这个表最重要的设计——它从数据库层面堵死了“同一个学生同一门课录两次成绩”的可能。如果你的业务里允许补考覆盖原成绩后面用 INSERT INTO ... ON DUPLICATE KEY UPDATE 更新即可别把这个索引去掉。3.2 账号密码与角色权限不要明文存密码也别把逻辑写死在 JSPsys_user 表里密码字段我建议至少存 MD5 或 SHA-256 的散列值不要直接存明文。课设项目经常被老师问“密码为什么是 123456”你回答“这是演示数据”没问题但如果整张表都是明文评审环节很容易被追问到尴尬。用 JDBC 插入时做一个简单的哈希转换就行// 注意仅演示用。生产环境请用 BCrypt 这类慢哈希别用 MD5 String hashed DigestUtils.md5DigestAsHex( (username password).getBytes(StandardCharsets.UTF_8) );这段说明一个容易忽略的问题我在哈希时把 username 拼进去是为了防止两个用户密码相同导致哈希值完全一样。虽然 MD5 本身已不适合生产场景但课设里用这种方式至少能体现你考虑了常见安全问题。角色字段建议用字符串常量而不是数字比如 ADMIN 和 TEACHER代码里可读性高SQL 查出来也直观。JSP 页面里判断角色时要用 JSTL 标签对比不要写% if(user.getRole().equals(ADMIN)) %这种脚本代码。3.3 JDBC 连接参数MySQL 5.7 和 8.0 的配置差异数据库建好后Java 侧需要一个统一的 JDBC 连接配置。常见做法是写一个 db.properties 放在 resources 目录下然后用一个 BaseDao 读取配置并维护连接。注意 MySQL 8.0 之后驱动类名从 com.mysql.jdbc.Driver 改成了 com.mysql.cj.jdbc.Driver网上大量老教程用的旧类名在 8.0 下直接抛 ClassNotFoundException。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_score?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password你的数据库密码参数逐个解释一下characterEncodingutf8 保证中文写入和读出不乱码serverTimezone 指定时区因为 MySQL 8.0 对时区敏感不写会出现时间字段的异常或警告useSSLfalse 表示本地开发时关闭 SSL 加密握手allowPublicKeyRetrievaltrue 是配合 MySQL 8.0 默认的 caching_sha2_password 认证插件使用否则连接时可能报 Public Key Retrieval is not allowed。如果你用的是 MySQL 5.7allowPublicKeyRetrieval 这个参数可以不要驱动类名也换回 com.mysql.jdbc.Driver。很多人在这一步卡住其实是在 MySQL 安装配置阶段没把 root 密码和编码设置清楚。MySQL 8.0 安装时默认字符集已经是 utf8mb4和上面 SQL 建库语句一致基本不用额外调整。但如果是 5.7 且安装时用了默认的 latin1你后边插入中文就会变成乱码或问号这时要优先检查 CHARACTER SET 而不是在 Java 代码里折腾。4. 从登录到成绩列表ServletJSP 在主流程上的落地顺序4.1 登录过滤器与 Session每个请求先进来验一下身份成绩管理系统至少有登录和查询两个角色但你不希望用户不登录就输入 URL 直接访问成绩列表。Servlet 规范里做这件事的标准工具是 Filter。我一般写一个 LoginFilter拦下除登录页、登录接口、静态资源以外的所有请求核心逻辑就这么一段package com.example.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; WebFilter(filterName LoginFilter, urlPatterns /*) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; String uri req.getRequestURI(); // 白名单登录页面、登录请求、静态资源直接放行 boolean isLoginPage uri.endsWith(login.jsp) || uri.endsWith(LoginServlet); boolean isStatic uri.contains(/static/); if (isLoginPage || isStatic) { chain.doFilter(request, response); return; } // 非白名单请求session 里没有登录标记就回登录页 if (req.getSession().getAttribute(loginUser) null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }这段过滤器的设计要点在放行顺序先放行静态资源再放行登录接口最后才校验 Session。很多项目样式加载不出来就是因为 Filter 把 bootstrap.min.css 拦下来重定向到了登录页浏览器拿到的是 302 而不是 200。把 /static/ 路径单拎出来放行能少踩一个常见坑。重定向时必须用 getContextPath() 拼路径否则项目部署在非根路径下跳转会丢失上下文。4.2 成绩列表与删除一个 Servlet 用 action 参数做分发JSP 时代最常见的 Servlet 写法是用一个 Servlet 承载多个动作通过 URL 里的 action 参数区分。ScoreServlet 既负责列表查询也负责删除后续要加新增和导入 Excel继续往 doGet 或者 doPost 里加分支。这种方式虽然原始但代码结构一目了然适合新手理解请求分发的思路package com.example.servlet; import com.example.service.ScoreService; import com.example.vo.PageResult; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; WebServlet(/ScoreServlet) public class ScoreServlet extends HttpServlet { private ScoreService scoreService new ScoreService(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (list.equals(action)) { // 页码参数做防御性校验避免非数字导致 500 int page 1; String pageStr req.getParameter(page); if (pageStr ! null pageStr.matches(\\d)) { page Integer.parseInt(pageStr); } String keyword req.getParameter(keyword); PageResult result scoreService.list(page, 10, keyword); req.setAttribute(page, result); req.setAttribute(keyword, keyword); req.getRequestDispatcher(/score/list.jsp).forward(req, resp); } else if (delete.equals(action)) { int id Integer.parseInt(req.getParameter(id)); scoreService.delete(id); resp.sendRedirect(req.getContextPath() /ScoreServlet?actionlist); } } }这个类的两个动作用了两种跳转方式list 用 forward把数据转发到 JSP 渲染delete 用 sendRedirect因为删除后如果还停留在原页面用户按 F5 刷新时会重复提交删除请求。这就是 JSP 项目里常说的“表单重复提交”问题搜“jsp 页面刷新一次”相关方案时最常见的解决办法就是写操作后重定向而不是转发。把业务逻辑拆到 ScoreService而不是直接写在 Servlet 里是为了后面加新功能时不用动控制器代码。4.3 JSP 页面上的 JSTL 渲染把 Java 循环从页面里请出去JSP 页面的核心原则是不要出现%。所以成绩列表在 JSP 里用 JSTL 的 c:forEach 循环输出配合 Bootstrap 的 table 样式代码非常干净% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % page contentTypetext/html;charsetUTF-8 languagejava % table classtable table-hover table-striped thead tr th学号/th th姓名/th th课程/th th成绩/th th操作/th /tr /thead tbody c:forEach items${page.list} varrow tr td${row.studentNo}/td td${row.studentName}/td td${row.courseName}/td td${row.score}/td td a classbtn btn-sm btn-warning href#editModal>public ListScoreVO list(int pageNo, int pageSize, String keyword) { // 第 1 页 offset 0第 2 页 offset pageSize int offset (pageNo - 1) * pageSize; String sql SELECT s.student_no, s.name, c.course_name, sc.score FROM score sc JOIN student s ON sc.student_id s.id JOIN course c ON sc.course_id c.id WHERE s.name LIKE ? ORDER BY s.student_no LIMIT ?, ?; // 参数按顺序传 % keyword %, offset, pageSize }5. Bootstrap 渲染与分页的避坑样式加载、中文乱码和分页翻车5.1 静态资源加载失败的排查顺序先看样式缺失还是请求 404Bootstrap 在这个项目里的引入并不复杂但“页面有数据没样式”是出现频率最高的问题。现象是表格里的字全部堆在左侧按钮没有圆角弹窗点了没反应。碰到这种情况我按下面的顺序排查基本两分钟内定位。排查第一步打开浏览器开发者工具的 Network 面板刷新页面看 bootstrap.min.css 这个请求的状态码。如果显示 404说明路径不对如果显示 302说明被登录过滤器拦了。第二步看路径请求 URL 是http://localhost:8080/student-score/static/css/bootstrap.min.css还是http://localhost:8080/static/css/bootstrap.min.css。缺了前面一段 contextPath就是你在 JSP 里没用 ${pageContext.request.contextPath} 拼路径。第三步看文件位置bootstrap 必须放在 webapp 下的 static 目录而不是 WEB-INF 下面因为浏览器不能直接访问 WEB-INF 内的文件。有一个相关现象值得单独说JSP 页面能打开但弹窗 JS 不生效。这种情况通常不是路径问题而是你在 Bootstrap 的 JS 之前引了 jQuery或者只复制了 CSS 没复制 JS。Bootstrap 4 依赖 jQueryBootstrap 5 不再依赖。如果你用的是 Bootstrap 5引入 bundle.min.js 即可它已经内置了弹窗所需的全部逻辑。5.2 中文乱码的三层治理页面、Servlet、数据库连接参数乱码是 ServletJSPMySQL 项目里最经典的翻车点。现象很好认页面显示“姓名”变成“å§“å”数据库里存的是“????”。这个问题的成因是中文在代码、网络传输、数据库存储三个环节的编码不一致通常要同时处理三层才能根治。第一层在 JSP 页面头部必须写% page contentTypetext/html;charsetUTF-8 languagejava %浏览器才知道页面用什么编码渲染。第二层在 Servlet 中读取 POST 表单参数之前先调用req.setCharacterEncoding(UTF-8)响应输出前设置resp.setCharacterEncoding(UTF-8)。注意 setCharacterEncoding 必须在 getParameter 之前调用否则设置不生效。第三层在数据库连接串第 3 章里的 characterEncodingutf8 不能丢。如果你建库时已经用了 utf8mb4连接串里写 utf8 即可utf8mb4 是 utf8 的超集。失败的例外情况是网页显示正常但数据库里是乱码。这说明请求响应链路已经通了问题出在数据库连接串或建库字符集上。先在 MySQL 命令行执行SHOW CREATE TABLE student看看表的 DEFAULT CHARSET 是不是 utf8mb4。如果表已经建成 latin1光改连接串是没用的要用 ALTER TABLE 改回来。MySQL 5.7 的 utf8 和 utf8mb4 要区分开utf8mb4 才能完整支持四字节字符和部分生僻汉字utf8 在 5.7 里其实是个阉割版。5.3 MySQL 8.0 与 JDBC 的 SSL 报错一段连接串就能解决做 JavaWeb 项目的人大概率见过这类异常java.sql.SQLException: Communications link failure翻完整日志还会看到SSL connection error或Public Key Retrieval is not allowed。第一次遇到会觉得是网络不通或者 MySQL 没启动其实根源在 MySQL 8.0 默认的认证插件是 caching_sha2_password它要求客户端要么启用 SSL要么用 allowPublicKeyRetrieval 明确允许获取服务器公钥。针对这个坑正确解决方式是把 JDBC 连接串的参数补全useSSLfalseallowPublicKeyRetrievaltrue。前者告诉驱动本地开发环境不需要加密传输后者在加密关闭时允许客户端向服务器要公钥来完成认证。两个参数通常一起出现单独只加一个还可能继续报错。这个配置只适合本地和测试环境生产环境不要图省事关 SSLMySQL 的 SSL 报错在生产里的标准解法是配置信任证书而不是直接绕开。还有一种情况容易被忽略Tomcat 使用 JNDI 数据源时连接串写在 server.xml 或 context.xml 里改了配置文件要重启 Tomcat 才能生效不像 JDBC 直连改 properties 重启程序就好。用 JNDI 反而比直连多一层缓存排查时记得先把 Tomcat 停了再改。对于课设联调我一般建议直接用 db.properties 直连少一层封装就少一个排查点。5.4 分页参数的翻车现场offset 计算、搜索条件丢失、页码越界分页查询在 DAO 层是两条 SQL一条查总数一条查当前页数据。但页面表现出的问题往往集中在三个位置。最常见的是点击第 2 页后数据错位比如第 1 页的尾行又出现在第 2 页。原因基本可以锁定在偏移量公式如果 offset 从 0 开始第一页 offset 应该是 0第二页是 pageSize也就是(pageNo - 1) * pageSize如果你 pageNo 传的是 0公式就成了pageNo * pageSize第一页直接跳过前十条。第二个高频问题从搜索页翻页时搜索结果丢失。比如你在姓名搜索框输入“张”点击第 2 页结果变成全量数据。原因很直接——翻页链接里没带 keyword 参数。解决方式是在列表页的翻页按钮上把搜索条件原样带出a href${pageContext.request.contextPath}/ScoreServlet?actionlistpage${page.current 1}keyword${param.keyword}下一页/a第三个问题是页码越界用户手动改 URL 里的 page999DAO 执行 LIMIT 998, 10 返回空列表页面直接空白。我在第 4 章的 Servlet 里用正则\\d做了第一道校验但更完整的做法是在 Service 层把查询出的总页数也返回JSP 里对当前页码做上下界压制页码小于 1 显示第 1 页大于总页数显示最后一页。给一张参数关系表可以把这个逻辑理得更清楚参数含义示例pageNo当前页码从 1 开始2pageSize每页条数10offsetLIMIT 起始位置(2-1)*1010total总记录数53totalPage总页数(5310-1)/106这四条是我在这个项目里遇到频率最高的坑。它们每一个单独拆开都不难但连续出现就会给人一种“JSP 项目玄学问题好多”的感觉。其实归纳起来就三个根源路径没拼全、编码没统一、分页参数公式没搞清楚。你把这三点盯住了这个项目的疑难问题基本消灭大半。6. 部署验证与一个小技巧让成绩系统经得起拷问项目代码写完后部署到 Tomcat 的流程是Maven 打包成 war把 war 复制到 Tomcat 的 webapps 目录启动 MySQL 和 Tomcat然后按清单验证。常见的部署命令就三步mvn clean package cp target/student-score.war $CATALINA_HOME/webapps/ $CATALINA_HOME/bin/startup.sh验证清单我建议按这个顺序过第一未登录状态下直接访问 /ScoreServlet应该被重定向到 login.jsp第二用管理员账号登录进入成绩列表页检查 Bootstrap 样式完整第三新增一条成绩刷新页面确认没有重复提交第四搜索“张”翻到第二页确认搜索条件没有丢第五退出登录后按浏览器后退键确认无法回到列表页。这一步是针对 Session 失效的常见验证——很多项目退出后用户点后退还能看到数据因为浏览器缓存了页面需要在 JSP 响应头里加Cache-Control: no-store比赛答辩时这算是个加分项。最后一个具体技巧给成绩列表加一个“导出 CSV”功能代码量不超过十行却非常容易打动评审老师。在 ScoreServlet 里加一个 actionsexport 的分支查询全部数据后把结果写成 CSV 流式输出// 导出当前查询结果为 CSV resp.setContentType(text/csv;charsetUTF-8); resp.setHeader(Content-Disposition, attachment;filenamescore.csv); PrintWriter out resp.getWriter(); out.println(学号,姓名,课程,成绩); for (ScoreVO row : scoreService.listAll()) { out.println(row.getStudentNo() , row.getStudentName() , row.getCourseName() , row.getScore()); }这套导出实现要理解两个点Content-Disposition 的 attachment 会让浏览器直接下载而不是打开CSV 文件用 Excel 打开时如果中文乱码是因为输出编码和 Excel 的本地编码不一致常见做法是输出带 BOM 的 UTF-8 或者把编码改成 GBK。导出接口的验证方式是点按钮后下载文件用 Excel 打开检查列对齐。我早年做这个题目时最大的教训是图省事把业务逻辑全写在 JSP 页面里页面和 Java 代码搅在一起后期改一个分页参数要找半天。现在我做这套系统一定让 JSP 里看不到 Java 脚本片段让 Servlet 只做转发和分发让数据库把唯一约束兜住。这个习惯一直沿用到后面做 Spring Boot 项目收益非常大。希望这一篇能把你这套成绩管理系统的开发路线理清楚也帮你避掉那些我当年踩过的坑。本文还有配套的精品资源点击获取
返回列表