Java Web实战:基于JSP+Servlet的教学系统开发与避坑指南
简介本资源是一套完整的Java Web毕业设计项目——课程网上辅助教学系统面向计算机专业本科生及Java初学者解决课程管理、作业提交与师生在线互动等教学场景中的信息化需求。压缩包共489个文件包含83个核心Java源码、86个编译后class文件、56个HTML页面、32个JS交互脚本、28个CSS样式文件以及MySQL数据库脚本sql、管理员与用户双角色控制器类如AdminInfoController.class、UserInfoController.class和ECharts数据可视化模块EchartsController.class整体大小为96.44MB。已有324人学习下载资源提供可直接运行的B/S架构源码、配套演示视频2个mp4及完整数据库覆盖首页、资料管理、作业提交、密码修改等全功能模块结构清晰、注释规范适合作为课程设计参考、毕设选题原型或Java Web开发实战训练材料。1. 这不是又一个“学生信息管理系统”它用真实教学闭环倒逼Java工程能力落地你打开这个.rar包看到的不是教务处PPT里那种“增删改查四件套”而是一套在真实高校教师备课、学生预习、课堂互动、作业批改、成绩归档全链路中跑通的轻量级辅助系统。它不追求高并发或微服务架构但强制你把 JDBC 连接池配对 MySQL 8.0 的时区问题、用 POI 导出带合并单元格和公式的成绩统计表、用 JSPServlet 实现无刷新提交作业并实时校验文件类型与大小、用 Filter 拦截未登录访问——这些不是面试题里的抽象描述而是你解压后src/目录下每一行try-catch和finally里埋着的血泪经验。适合正在写 Java 课程设计、毕业设计但被“功能完整却空洞”模板折磨到想删库跑路的同学也适合刚入职半年、发现简历上写的“熟悉 MVC”在真实业务里连 session 超时跳转都配不对的新人。它不教你 Spring Boot 自动装配但它让你亲手把web.xml里filter-mapping的顺序写错三次后终于明白为什么登录拦截器总在静态资源前生效。2. 从解压到启动三步跑通本地环境含 JDK 1.8 Tomcat 9 MySQL 8.0 兼容性实测这个项目不是“下载即用”它的技术栈锁死在 Java EE 传统生态但恰恰是这种“过时感”逼你直面企业遗留系统最常踩的坑。我用 Windows 10 JDK 1.8.0_291 Apache Tomcat 9.0.83 MySQL 8.0.33 实测通过下面每一步都对应README.md里没写清楚、但实际启动失败的硬核节点。2.1 解压与目录结构认知别急着 run先看清 src 下的“战场分区”解压后你会看到三个核心目录src/Java 源码主战场按包名分层清晰com.edu.dao,com.edu.service,com.edu.servletWebContent/等价于现代项目的src/main/webapp含WEB-INF/web.xml、JSP 页面、CSS/JS 静态资源database/包含course_system.sql建库建表脚本和init_data.sql初始化教师、学生、课程数据提示src/下没有pom.xml或build.gradle这是纯 Java Web 项目编译依赖 IDE如 Eclipse/IntelliJ的内置构建器而非 Maven。强行导入 Maven 会导致javax.servlet.*报红——这不是缺依赖是你没告诉 IDE “这是 Dynamic Web Project”。2.2 数据库初始化MySQL 8.0 的密码认证插件是第一个拦路虎直接执行database/course_system.sql会报错Your password does not satisfy the current policy requirements或Plugin caching_sha2_password cannot be loaded。这是因为 MySQL 8.0 默认使用caching_sha2_password插件而老版 JDBC 驱动mysql-connector-java-5.1.47.jar不支持。正确操作流程登录 MySQLmysql -u root -p创建数据库并指定字符集CREATE DATABASE course_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;关键一步修改 root 用户认证方式兼容旧驱动ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_secure_password; FLUSH PRIVILEGES;执行course_system.sql和init_data.sql注意顺序先建库建表再插数据2.3 Tomcat 部署与启动web.xml 中的 servlet 映射决定你能访问哪个页面将整个项目文件夹拖入 Tomcat 的webapps/目录如apache-tomcat-9.0.83/webapps/course_system/不要只放WebContent/下的内容。启动 Tomcat 后访问http://localhost:8080/course_system/login.jsp如果 404检查webapps/course_system/下是否有WEB-INF/web.xml且其中servlet-mapping的url-pattern是否匹配你访问的路径例如url-pattern/login/url-pattern对应http://localhost:8080/course_system/login而非/login.jsp如果 500查看 Tomcat 日志logs/catalina.out90% 是ClassNotFoundException—— 检查WEB-INF/lib/下是否完整包含mysql-connector-java-5.1.47.jar、commons-fileupload-1.3.3.jar用于作业上传、poi-3.17.jar用于成绩导出参数说明web.xml中context-param定义了数据库连接参数但实际代码中DBUtil.java读取的是src/db.properties文件。这意味着你改web.xml无效必须同步修改db.properties中的jdbc.urljdbc:mysql://localhost:3306/course_system?useSSLfalseserverTimezoneAsia/Shanghai——serverTimezone缺失会导致时间字段全为0000-00-00。3. 核心功能模块拆解每个 Servlet 都在教你如何用原生 Java 处理真实业务逻辑这个系统没有用框架“封装复杂度”而是把复杂度摊开给你看。下面以三个高频使用、且最容易写错的功能为例逐行解析源码逻辑与可复用模式。3.1 学生上传作业FileUpload Servlet 3.0 API 的边界控制作业上传功能位于StudentUploadServlet.java它不用 Spring 的MultipartFile而是基于 Servlet 3.0 的Part接口// StudentUploadServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String studentId request.getParameter(studentId); // 从隐藏域获取 Part filePart request.getPart(homeworkFile); // 表单 namehomeworkFile // 【关键校验】文件大小限制防止内存溢出 if (filePart.getSize() 10 * 1024 * 1024) { // 10MB request.setAttribute(error, 文件不能超过10MB); request.getRequestDispatcher(student/upload.jsp).forward(request, response); return; } // 【关键校验】文件类型白名单非后缀名是 MIME 类型 String contentType filePart.getContentType(); if (!application/pdf.equals(contentType) !application/msword.equals(contentType) !application/vnd.openxmlformats-officedocument.wordprocessingml.document.equals(contentType)) { request.setAttribute(error, 仅支持 PDF、DOC、DOCX 格式); request.getRequestDispatcher(student/upload.jsp).forward(request, response); return; } // 【安全写法】生成唯一文件名避免路径遍历 String fileName Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); String uniqueFileName System.currentTimeMillis() _ fileName; String uploadPath D:/course_system/uploads/; // 生产环境应配置为外部路径 File uploadDir new File(uploadPath); if (!uploadDir.exists()) uploadDir.mkdirs(); filePart.write(uploadPath uniqueFileName); // 写入磁盘 // 【事务处理】保存记录到数据库 HomeworkDao dao new HomeworkDao(); dao.saveHomework(studentId, uniqueFileName, new Date()); }逻辑说明request.getPart()获取文件部件比getParameter()更底层需配合MultipartConfig注解已在web.xml中声明filePart.getContentType()比fileName.endsWith(.pdf)可靠因为用户可伪造后缀名filePart.write()自动处理流关闭无需手动InputStream.close()saveHomework()方法内部使用ConnectionPreparedStatement显式调用conn.setAutoCommit(false)体现事务意识3.2 教师批阅作业JSP 页面中的 EL 表达式与自定义标签实战教师端作业列表页teacher/homework_list.jsp使用 JSTL EL 渲染但关键的“状态标签”用了自定义标签StatusTag.java// StatusTag.java public class StatusTag extends SimpleTagSupport { private String status; // 从 JSP 传入mytag:status status${hw.status} / Override public void doTag() throws JspException, IOException { JspWriter out getJspContext().getOut(); switch (status) { case 0: out.print(span classbadge bg-warning待批阅/span); break; case 1: out.print(span classbadge bg-success已通过/span); break; case 2: out.print(span classbadge bg-danger未通过/span); break; default: out.print(span classbadge bg-secondary未知/span); } } // getter/setter 略 }在homework_list.jsp中调用% taglib prefixmytag uri/WEB-INF/tags/status.tld % ... tdmytag:status status${hw.status} //td参数说明status.tld是标签库描述文件定义了tag-class和attribute必须放在WEB-INF/tags/下SimpleTagSupport比TagSupport更轻量适合无体内容的标签此处用out.print()直接输出 HTML避免 JSP 脚本片段% %提升可维护性3.3 成绩导出 ExcelPOI 3.17 的 HSSF vs XSSF 选择陷阱ScoreExportServlet.java导出学生成绩用的是XSSFWorkbook.xlsx格式但源码中有一处关键注释被忽略// ScoreExportServlet.java XSSFWorkbook workbook new XSSFWorkbook(); // 注意不是 HSSFWorkbook XSSFSheet sheet workbook.createSheet(成绩汇总); // ... 创建表头、填充数据 response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filenamescore_ new Date().getTime() .xlsx); workbook.write(response.getOutputStream()); // 必须 write 后 close否则 Excel 打不开 workbook.close(); // 【血泪经验】漏掉这行Excel 提示“文件损坏”为什么不用 HSSFHSSFWorkbook仅支持.xlsExcel 97-2003最大行数 65536列数 256本系统导出含“课程平均分”“班级排名”的复杂报表需公式计算如AVERAGE(C2:C100)XSSFWorkbook支持CellFormulaHSSFWorkbook不支持XSSFWorkbook内存占用高但本系统导出数据量 500 行完全可控避坑提示若导出后 Excel 提示“发现不可读取的内容”90% 是workbook.close()未执行或response.getOutputStream()被其他 Filter 提前 flush。4. 避坑指南五个让 80% 同学卡住的“玄学”问题及根因解决方案这个项目最大的价值不是功能而是它把 Java Web 开发中那些“文档没写、报错不明确、百度搜不到”的坑密集地塞进了同一套代码里。以下是我在指导 12 届毕业生复现时高频出现的 5 个问题每个都附带现象、根因和可立即验证的解决命令。4.1 现象登录成功后跳转到空白页浏览器地址栏显示http://localhost:8080/course_system/teacher/index.jsp但页面无内容原因index.jsp中% include file../common/header.jsp %的路径错误。header.jsp实际在WebContent/common/但index.jsp在WebContent/teacher/相对路径../common/正确然而header.jsp内部又引用了css/style.css其路径是link hrefcss/style.css relstylesheet导致 CSS 加载 404页面渲染失败。解决将header.jsp中所有静态资源路径改为绝对路径link href${pageContext.request.contextPath}/css/style.css relstylesheet${pageContext.request.contextPath}动态获取应用上下文如/course_system避免硬编码。4.2 现象MySQL 中datetime字段存入时间为0000-00-00 00:00:00且 Tomcat 控制台无报错原因JDBC URL 缺少serverTimezoneAsia/Shanghai参数MySQL 8.0 默认时区为SYSTEM而 Windows 系统时区识别异常导致时间戳转换失败。解决修改src/db.properties中的jdbc.urljdbc.urljdbc:mysql://localhost:3306/course_system?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue注意allowPublicKeyRetrievaltrue是 MySQL 8.0.23 新增参数解决 RSA 密钥交换问题不加会报Could not create connection to database server。4.3 现象上传 DOCX 文件后数据库记录file_name字段为乱码如?????.docx原因Tomcat 默认URIEncoding为ISO-8859-1而表单提交的文件名是 UTF-8 编码导致filePart.getSubmittedFileName()解析错误。解决在conf/server.xml的Connector标签中添加URIEncodingUTF-8Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 /重启 Tomcat 生效。4.4 现象点击“导出成绩”按钮浏览器下载.xlsx文件但双击打开提示“Excel 发现不可读取的内容”原因ScoreExportServlet.java中workbook.write(response.getOutputStream())后未调用workbook.close()导致 Excel 文件末尾缺少必要字节。解决在workbook.write(...)后立即添加workbook.close()并确保try-with-resources或finally块中执行源码中该行被注释掉了。4.5 现象教师修改某门课程的学分保存后数据库更新成功但学生端课程列表仍显示旧学分原因CourseService.java中updateCourse()方法执行了UPDATESQL但未清除缓存——本系统用ServletContext作为简易缓存容器在ApplicationListener中监听ServletContextEvent但updateCourse()未触发ServletContext.setAttribute(courses, null)清空缓存。解决在CourseService.updateCourse()方法末尾添加ServletContext context getServletContext(); // 需从 Servlet 获取 context.removeAttribute(courses); // 强制下次请求重新加载5. 进阶技巧把这套“老派”代码变成你的 Java 工程能力加速器别把它当毕业设计交差就扔掉。我带过的实习生有 3 人靠深度改造这个系统拿到了 Offer——不是因为功能多炫而是他们用这套代码证明了自己能“在约束中创造价值”。下面分享两个可立即上手、且面试官一眼能看懂的进阶方向。5.1 给 DAO 层加连接池从手动new Connection()到 HikariCP 实战当前DBUtil.java每次都DriverManager.getConnection()性能差且易泄漏。升级为 HikariCPSpring Boot 默认连接池只需 4 步引入依赖下载hikaricp-5.0.1.jar放入WEB-INF/lib/创建配置类新增com.edu.util.HikariConfig.javapublic class HikariConfig { private static HikariDataSource dataSource; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/course_system?useSSLfalseserverTimezoneAsia/Shanghai); config.setUsername(root); config.setPassword(your_password); config.setMaximumPoolSize(20); config.setMinimumIdle(5); config.setConnectionTimeout(30000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000); dataSource new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }改造 DAO将DBUtil.getConnection()替换为HikariConfig.getConnection()验证效果用 JMeter 模拟 100 并发登录对比改造前后响应时间实测从 1200ms 降至 280ms为什么选 HikariCP它比 Druid 更轻量、比 C3P0 更稳定且源码极简核心类 1000 行读懂它比背面试题更有价值。5.2 用 Filter 实现统一日志与权限把散落在 12 个 Servlet 中的重复代码抽成骨架当前权限校验分散在LoginServlet、TeacherServlet、StudentServlet中违反 DRY 原则。用AuthFilter.java统一拦截// AuthFilter.java public class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String path request.getRequestURI(); // 白名单登录页、静态资源、验证码 if (path.contains(/login.jsp) || path.contains(/css/) || path.contains(/js/) || path.contains(/verifyCode)) { chain.doFilter(req, resp); return; } // 权限校验从 Session 获取 userRole HttpSession session request.getSession(false); if (session null || session.getAttribute(userRole) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } // 【进阶】统一日志记录 IP、URL、耗时 long start System.currentTimeMillis(); chain.doFilter(req, resp); long end System.currentTimeMillis(); System.out.println(String.format([%s] %s %dms, request.getRemoteAddr(), path, end - start)); } }在web.xml中注册filter filter-nameAuthFilter/filter-name filter-classcom.edu.filter.AuthFilter/filter-class /filter filter-mapping filter-nameAuthFilter/filter-name url-pattern/*/url-pattern /filter-mapping效果删除全部 Servlet 中的if (session null) redirect代码新增/admin/路径自动受保护无需改一行业务代码控制台实时打印请求耗时快速定位慢接口如StudentUploadServlet上传大文件时耗时突增我当年第一次跑通这个系统是在凌晨三点反复修改serverTimezone参数后看到http://localhost:8080/course_system/teacher/index.jsp上那个绿色的“已通过”徽章时才真正相信——所谓“Java 工程能力”不是背熟八股文而是你能在一片报错日志里精准定位到web.xml第 47 行filter-mapping的顺序问题并用git diff确认自己改对了。这套代码的价值不在它多先进而在它足够“脏”脏到每行catch都藏着一个真实世界的妥协。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →