ARTICLE DETAIL

资讯详情

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

JavaWeb作业管理网站源码解析:从部署到改造避坑指南

JavaWeb作业管理网站源码解析:从部署到改造避坑指南 简介面向初学JavaWeb的开发者这套作业管理网站项目源码覆盖教师发布作业、学生提交作业与管理员后台管理三大核心场景适合课程设计、毕业设计及Servlet/JSP技术实战练习。压缩包共53个文件、约7.73MB以JSP页面、Java类、Jar依赖库为主辅以CSS样式、JS脚本和数据库配置信息目录结构清晰便于直接导入开发工具运行。目前已有97人学习。资源内含登录与信息维护、作业发布、作业汇总、作业提交等完整功能页面涉及Servlet请求处理、JDBC数据交互、MVC分层设计以及Bootstrap前端框架可帮助读者掌握作业管理系统的前后端联动、权限校验与数据库操作实现思路。1. JavaWeb作业管理网站这套源码包里到底有什么可以复用的做JavaWeb课程设计或者接手老项目时最怕的不是业务复杂而是拿到一个结构不明的zip包解压后不知道从哪行代码开始读。这份基于JavaWeb的作业管理网站源码解压后是一个典型的Servlet JSP JDBC三层结构没有引入Spring那套重量级依赖数据源用的是druid.properties配置的Druid连接池正好适合拿来理解JavaWeb最原始的请求-处理-响应链路。如果你正打算做一套「教师发作业、学生交作业、管理员管数据」的小系统或者想把课设里的SSM项目降级成纯JSP/Servlet版本以便讲清楚原理这份源码是一个可以快速跑通的参考底座。它能帮你省下搭骨架的时间直接看到登录、发布作业、提交作业、统计收集情况这几条主流程在JSP页面和Servlet之间是怎么流转的。2. 先拆运行链路Servlet、JSP、JDBC在作业流转里各自扮演什么角色2.1 从index.jsp到login.jsp请求入口与页面跳转的设计习惯打开压缩包先看页面文件。index.jsp通常是站点入口常见做法是放一个重定向或者欢迎页根据session里有没有登录用户决定是进login.jsp还是直接进work.jsp。这份源码里login.jsp被单独拆出来说明登录逻辑是独立处理的。!-- index.jsp -- % page contentTypetext/html;charsetUTF-8 languagejava % % // 检查session中是否有已登录用户 Object user session.getAttribute(user); if (user ! null) { // 已登录直接进入作业列表页 response.sendRedirect(work.jsp); } else { // 未登录先去登录页 response.sendRedirect(login.jsp); } %这里的核心逻辑是不要在前端页面里写死跳转地址而是通过session状态做一次判断。很多新手课设会把index.jsp直接做成登录表单页但实际项目中入口页往往是路由分发的作用。参数方面session.getAttribute(user)里存的user对象是在登录Servlet里set进去的这个命名要保持全项目一致否则跳到work.jsp后拿不到用户信息页面就会报空指针。JSP页面本身不做业务判断它只负责根据已有数据渲染界面。所以work.jsp、gather.jsp、issue.jsp这些页面里你会发现大量的% %脚本片段和% %输出标签它们拿到的数据都是Servlet通过request.setAttribute()塞进来的。理解这一点你就能快速定位每张页面对应的Servlet是哪个。2.2 druid.properties与JDBC数据库连接池配置的四个关键参数源码里的druid.properties是整站数据访问的命根子。JavaWeb项目里直接写Class.forName(com.mysql.jdbc.Driver)硬编码连接的做法已经过时了用Druid连接池管理数据库连接是更稳的姿势尤其在并发提交作业的场景下连接池能避免频繁创建和销毁连接带来的性能损耗。# druid.properties driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/homework_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai usernameroot password123456 initialSize5 maxActive10 maxWait3000四个关键参数逐个说driverClassName对应你用的MySQL版本5.x用com.mysql.jdbc.Driver8.x要换成com.mysql.cj.jdbc.Driver不换会直接报ClassNotFoundException。url里的characterEncodingutf8是必须的不写的话中文作业标题入库后全是问号。useSSLfalse只用于本地开发环境生产环境建议开启SSL或者放在内网。serverTimezoneAsia/Shanghai是MySQL 8.x的硬性要求少了它连接会报时区错误。连接池参数里initialSize5表示启动时预建5个连接maxActive10是最大连接数maxWait3000是拿不到连接时的最长等待毫秒数超过就抛异常。做课设的话这组参数够用但如果你要模拟全班同时交作业建议把maxActive调大到20否则高并发下会看到wait millis 3000, active 10, maxActive 10的报错。2.3 MVC在源码里怎么落地的Servlet是ControllerJSP是View那Model在哪很多人在课设里写MVC写着写着就变成了JSP里既写SQL又写HTML。这份源码的好处在于它把Controller和View分开了但Model层用的是最朴素的DAO写法没有Service层。看web目录下的Servlet你会发现每个Servlet只做三件事接收参数、调用DAO、跳转页面。// LoginServlet.java 核心片段 WebServlet(/login) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 接收前端表单参数 String username request.getParameter(username); String password request.getParameter(password); // 2. 参数校验空值直接打回 if (username null || password null || username.isEmpty() || password.isEmpty()) { request.setAttribute(msg, 用户名和密码不能为空); request.getRequestDispatcher(login.jsp).forward(request, response); return; } // 3. 调用DAO查询用户 UserDAO dao new UserDAO(); User user dao.findByUsernameAndPassword(username, password); // 4. 根据结果跳转 if (user ! null) { request.getSession().setAttribute(user, user); response.sendRedirect(work.jsp); } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } }这段代码的逻辑说明第一步从request里拿参数对应前端表单的name属性名字对不上拿到的就是null第二步做空值校验这里有个新手常见坑——只校验了null和空字符串没有trim掉首尾空格用户输入 admin 就会查不到第三步调DAODAO里再写JDBC查询第四步跳转时区分了重定向和转发登录成功后用sendRedirect是防止刷新页面重复提交表单登录失败用forward是为了把msg错误信息带到JSP页面上显示。关于Model层这份源码的Model是散落在DAO类和实体类里的。你会在com目录下看到User、Homework、Submission这样的实体类它们对应数据库表结构。这种写法虽然不够企业级但好处是链路短特别适合还没学框架的人理解。如果你想改成Spring MVC版只需要把WebServlet(/login)换成RequestMapping(/login)把request.getParameter换成RequestParam业务逻辑几乎可以原样搬过去。3. 把项目跑起来IDEA配置、Tomcat部署与数据库初始化3.1 前置环境JDK版本、Tomcat版本、MySQL版本怎么搭配不翻车先说结论我试过几组搭配最稳的是JDK 1.8 Tomcat 8.5 MySQL 5.7如果你机器上装的是MySQL 8.0那就把pom里的驱动换成8.x版本同时url里加serverTimezoneAsia/Shanghai。JDK 11以上跑老项目会遇到一个问题Tomcat 8.5默认用的是JSP 2.3规范而JDK 11里移除了JSP相关的EE包你需要在WEB-INF/lib下补一个jsp-api.jar不然启动时控制台会报java.lang.NoClassDefFoundError: javax/servlet/jsp/JspFactory。Tomcat版本和JDK的对应关系也容易踩坑。Tomcat 9需要JDK 8以上但Tomcat 10把javax.servlet改成了jakarta.servlet这份源码里的import javax.servlet.*在Tomcat 10下直接编译不过。所以如果你用的是Tomcat 10要么换回Tomcat 9要么全局替换javax为jakarta我建议直接换Tomcat 9省事。MySQL这边注意字符集。建库的时候用CREATE DATABASE homework_db DEFAULT CHARACTER SET utf8mb4;别用默认的latin1。utf8mb4和utf8的区别在于前者能存emoji表情和生僻字JavaWeb项目里学生提交作业内容可能带特殊符号用utf8mb4能少一个乱码坑。数据库连接用户的密码要和druid.properties里的password123456对齐不然启动时报Access denied for user rootlocalhost。3.2 IDEA导入本地zip项目的完整步骤不要直接Open根目录很多新手拿到zip解压后直接IDEA Open结果项目结构识别不出来全是红色报错。正确姿势是这样的解压zip确认目录结构里有src和web/WEB-INF这说明是war包结构不是Maven结构。IDEA里选择File - New - Project from Existing Sources选到解压后的根目录。弹窗里选Create project from existing sources一路Next到Select project name and location那一步改一下Project name避免默认名带特殊字符。导入完成后右键项目根目录选Add Framework Support勾选Web ApplicationIDEA会自动识别web.xml并配置好Web模块。如果源码里有Maven的pom.xmlIDEA会提示Maven项目点导入即可。如果没有pom.xml说明用的是纯JSP/Servlet lib目录下的jar包检查WEB-INF/lib下有没有mysql-connector.jar和druid.jar没有的话需要手动添加到Libraries。配置好之后还要设置一下项目的JDK级别。File - Project Structure - Project里把SDK选成1.8Modules - Dependencies里确保lib目录下的jar已经被引入。这一步漏了的话编译时所有import com.alibaba.druid.*和import com.mysql.*全会飘红。3.3 配置Tomcat并部署war包Artifact的坑你躲不开IDEA里跑JavaWeb项目本质是启动一个本地Tomcat实例然后把项目部署上去。很多人卡在Run按钮是灰色原因是没配Artifact。# 在IDEA菜单栏 Run - Edit Configurations - - Tomcat Server - Local # Server标签页Application server选择本地Tomcat安装路径 # Deployment标签页 - Artifact - 选择 xxx:war exploded # Application context填 /这样访问时不用带项目名这里有个关键选择xxx:war和xxx:war exploded的区别。前者是压缩包模式Tomcat每次启动先解压war后者是解压目录模式改动JSP后刷新页面立即生效不用重新打包。开发阶段一定要选exploded不然你改一行JSP代码就要重启整个Tomcat血泪经验。Application context建议填/这样浏览器访问http://localhost:8080/index.jsp就到了项目入口。如果填了/homework那你访问路径得带项目名JSP里的response.sendRedirect(work.jsp)这类相对路径在跳转时容易出问题——当前路径变成/homework/work.jsp如果Servlet的注解是WebServlet(/work)而JSP里写的是form actionlogin那提交表单时请求会打到/homework/login能通但如果你在Servlet里用了response.sendRedirect(/work.jsp)带不带项目名就是天壤之别了。统一用/可以少踩一半的路径坑。3.4 初始化数据库从源码里的SQL脚本反推表结构源码zip里不一定自带SQL脚本这时你需要从实体类和DAO层的代码里反推表结构。我一般会先打开com目录下的实体类看看有哪些字段再打开DAO类看查询语句用了哪些列名。-- 根据源码实体类和DAO反推的核心表结构 CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, role TINYINT DEFAULT 0 COMMENT 0-学生 1-教师 2-管理员, real_name VARCHAR(20) ); CREATE TABLE homework ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(100) NOT NULL, content TEXT, teacher_id INT, deadline DATETIME, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (teacher_id) REFERENCES user(id) ); CREATE TABLE submission ( id INT AUTO_INCREMENT PRIMARY KEY, homework_id INT, student_id INT, submit_time DATETIME, file_path VARCHAR(255), status TINYINT DEFAULT 0 COMMENT 0-未提交 1-已提交, FOREIGN KEY (homework_id) REFERENCES homework(id), FOREIGN KEY (student_id) REFERENCES user(id) );表结构设计里要注意三点。第一role字段用TINYINT存角色比用字符串存省空间但代码里要用常量维护比如User.ROLE_TEACHER 1别在DAO里写魔法数字where role 1不然三个月后你自己都看不懂。第二submission表里的status字段看似冗余因为存在记录就说明提交过了但实际业务里存在教师撤销提交让学生重交的场景所以保留状态位更灵活。第三外键约束在生产环境可以不加但课设项目里加了能向评委展示你有数据一致性意识。初始化数据时至少插入一个教师账号、一个学生账号、一条作业记录。密码建议直接用明文因为这份源码的登录校验大概率是select * from user where username ? and password ?你如果手贱把密码加密了反而登录不进去。等后面改造时再统一加MD5或BCrypt。3.5 启动顺序与验证清单看到login.jsp只算成功了一半启动Tomcat后浏览器输入http://localhost:8080/index.jsp能跳转到login.jsp说明Tomcat和项目本身没毛病。但这时候别急着欢呼按下面清单逐项验证验证项操作预期结果登录功能用教师账号登录跳转到work.jsp显示作业列表发布作业教师身份进入issue.jsp填写标题和内容提交跳转到work.jsp后能看到新作业学生登录退出后用学生账号登录看到教师发布的作业但不能显示发布按钮提交作业学生点击提交输入作业内容submission表新增记录gather.jsp能看到统计数权限拦截未登录直接访问work.jsp页面能打开但拿不到数据或者跳回login.jsp这里第五项是个大坑。如果源码里的Servlet只在登录时校验密码没有做session拦截那未登录用户直接访问work.jsp时页面里的session.getAttribute(user)是null如果JSP脚本里直接调用user.getUsername()Tomcat会抛NullPointerException页面白屏。验证这个场景很有必要如果翻车了有两个补救方案要么在JSP页面顶部加判断要么写一个AuthFilter拦截所有*.jsp和Servlet路径。后面第四章我会细说Filter的方案。4. 避坑与排查JavaWeb作业管理跑不起来最常见的五个问题4.1 现象Tomcat能启动但访问页面报404原因Artifact没部署上去或者Application context路径不对。IDEA里Tomcat虽然启动了但Deployment标签下如果没有添加ArtifactTomcat就是个空壳服务器访问任何路径都404。另外如果你改过Application context比如从/改成了/homework旧的重定向代码response.sendRedirect(index.jsp)会定位到/homework/index.jsp但如果JSP里写的又是相对路径a hreflogin.jsp当前浏览器地址栏的路径决定了这个相对路径解析到哪混用绝对路径和相对路径就会交叉出404。解决检查Run - Edit Configurations - Deployment里有没有xxx:war exploded没有就点加号补上。Application context统一用/然后所有Servlet的WebServlet注解用/xxx格式JSP页面之间跳转用相对路径Servlet跳JSP用request.getRequestDispatcher(xxx.jsp)这样整个项目的路径规则是自洽的。4.2 现象页面中文全是问号数据库里存的数据也是乱码原因三层乱码每一层都可能出问题。第一层是JSP页面编码% page contentTypetext/html;charsetUTF-8 %没写或者写错第二层是请求编码Tomcat默认用ISO-8859-1解码POST请求你Servlet里直接request.getParameter()拿到的中文就是乱码第三层是数据库连接编码druid.properties里url少了characterEncodingutf8。解决三层分别处理。JSP页面头部加% page contentTypetext/html;charsetUTF-8 languagejava %。Servlet里在读取任何参数之前加request.setCharacterEncoding(UTF-8)。对于GET请求还需要在Tomcat的conf/server.xml里给Connector加URIEncodingUTF-8属性。数据库url保证有characterEncodingutf8且物理表结构是utf8mb4。这三层少一层中文就乱给你看。4.3 现象登录时明明输入正确却提示用户名或密码错误原因第一数据库表里的密码有肉眼不可见的空格比如建库脚本里写了123456 带了个尾随空格第二druid.properties里连接的数据库和实际初始化的数据库不是同一个库你往里插数据的表和程序查的表对不上第三登录Servlet里没做trim()用户输入时手滑带了个空格。解决先用SQL客户端直接查select * from user where username admin确认表里有数据且密码正确。然后在Servlet的DAO层打印日志Log一下实际执行的SQL和传入的参数。如果SQL没问题那就是字符集或空格问题在SQL里用where username ?配合username.trim()过滤。这一步排查最重要的原则先用数据库工具排除数据本身的问题再怀疑代码。4.4 现象学生提交作业后gather.jsp页面的统计数字对不上原因统计SQL用了COUNT直接select count(*) from submission但没有按homework_id分组所以所有作业的提交数混在一起或者LEFT JOIN的关联字段写错比如submission.student_id关联到了homework.teacher_id统计结果自然全是0。解决打开gather相关的Servlet看一下统计SQL。正确写法应该按作业ID分组-- gather.jsp背后的统计逻辑 SELECT h.id, h.title, COUNT(CASE WHEN s.status 1 THEN 1 END) AS submitted_count, COUNT(u.id) AS total_students FROM homework h LEFT JOIN submission s ON h.id s.homework_id LEFT JOIN user u ON u.role 0 GROUP BY h.id, h.title;这段SQL的细节LEFT JOIN保证即使没人提交作业作业记录也能显示在列表里COUNT(CASE WHEN s.status 1 THEN 1 END)只统计已提交的学生COUNT(u.id)统计所有学生总数。如果源码里用的是内连接INNER JOIN没提交的作业就不会出现在统计页面上这就是数字对不上的根本原因。4.5 现象JSP页面报错java.lang.NullPointerException定位到session.getAttribute(user)那行原因JSP脚本里直接使用了session中不存在的属性。用户未登录、或者session过期了user变量是null你直接调用user.getId()就会炸。解决JSP页面顶部加判断更规范的做法是写过滤器统一拦截。我一般会在com包下新建一个AuthFilter类// AuthFilter.java 权限拦截过滤器 WebFilter(/*) public class AuthFilter implements Filter { 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.endsWith(login.jsp) || path.endsWith(/login) || path.endsWith(index.jsp)) { chain.doFilter(req, resp); return; } // 检查session if (request.getSession().getAttribute(user) null) { response.sendRedirect(login.jsp); return; } chain.doFilter(req, resp); } }这段代码的逻辑从request.getRequestURI()拿到当前请求路径endsWith判断是不是公开页面是就放行。然后检查session里有没有user没有就踢回登录页。WebFilter(/*)表示拦截所有请求包括静态资源所以要在放行条件里加上/css/、/js/、/images/这些静态资源路径否则Bootstrap样式全加载不出来页面裸奔。5. 进阶改造给作业管理系统加上截止时间校验与文件上传基础版本跑通之后下一步值得做的改造有两个一是发布作业时带上截止时间提交作业时校验是否已过期二是把提交内容从文本框改成文件上传。这两个改造都能让项目从课设demo变成接近真实可用。截止时间校验的核心思路是在submit相关的Servlet里加一道时间判断// SubmitHomeworkServlet 核心逻辑 WebServlet(/submitHomework) public class SubmitHomeworkServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); User user (User) request.getSession().getAttribute(user); int homeworkId Integer.parseInt(request.getParameter(homework_id)); // 检查提交内容 String content request.getParameter(content); // 加载作业信息判断截止时间 HomeworkDAO hdao new HomeworkDAO(); Homework homework hdao.findById(homeworkId); Date deadline homework.getDeadline(); Date now new Date(); if (deadline ! null now.after(deadline)) { request.setAttribute(msg, 作业已截止无法提交); request.getRequestDispatcher(submit.jsp).forward(request, response); return; } // 通过校验后执行提交逻辑 SubmissionDAO sdao new SubmissionDAO(); boolean success sdao.submitHomework(homeworkId, user.getId(), content); // 成功跳转回作业列表 response.sendRedirect(work.jsp); } }这段代码的核心不是提交动作而是now.after(deadline)这个时间判断。有一个容易被忽略的点如果作业没有设置截止时间deadline可能是null所以要加空值保护否则now.after(null)会抛NPE。时间格式的话前端表单里用input typedatetime-local后端解析时格式化yyyy-MM-ddTHH:mm这个T是HTML5时间控件的标准格式。文件上传改造需要引入commons-fileupload和commons-io两个jar包。如果你不想引入第三方库可以用Servlet 3.0原生的MultipartConfig注解加request.getPart(file)来获取上传文件。这个方案的好处是不用额外依赖但要确认Tomcat版本支持Servlet 3.0及以上Tomcat 8.5完全没问题。// 文件上传核心片段 MultipartConfig(maxFileSize 1024 * 1024 * 10, maxRequestSize 1024 * 1024 * 50) public class UploadServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part filePart request.getPart(file); String fileName Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // 避免重名覆盖拼上时间戳 String newName System.currentTimeMillis() _ fileName; String uploadPath getServletContext().getRealPath(/uploads); File uploadDir new File(uploadPath); if (!uploadDir.exists()) { uploadDir.mkdirs(); } filePart.write(uploadPath File.separator newName); // 把文件路径存到submission表里 SubmissionDAO sdao new SubmissionDAO(); sdao.submitHomeworkWithFile(user.getId(), homeworkId, uploads/ newName); } }文件上传这里有三个血泪坑。第一个是getSubmittedFileName()在不同浏览器下返回的路径不一样IE会返回完整的绝对路径所以要用Paths.get().getFileName()只取文件名部分。第二个是文件名重名问题两个学生交的文件叫report.docx先交的会被后交的覆盖解决方式就是加时间戳或者随机数前缀。第三个是uploadDir.mkdirs()如果父目录不存在必须用mkdirs()而不是mkdir()否则返回false且目录创建不成功。我最初用mkdir()排查了半天换成mkdirs()立刻就好了。改造完成后记得重新验证一下完整链路教师发布带截止日期的作业 → 学生在截止日期前上传文件 → 截止日期后再次提交被拦截 → gather.jsp统计里能看到提交状态和文件路径。这个流程走通你的项目从功能完整度上已经超过大部分课设了。从那以后我每次跑JavaWeb项目都强制自己先走一遍「未登录访问拦截、中文乱码、路径404」这三关确认没有基础问题再往深了改。这套源码虽然朴素但正因为没有框架封装反而逼你把Servlet、JSP、JDBC、Filter这些底层的恩怨情仇全部看清楚希望帮到你。本文还有配套的精品资源点击获取
返回列表