
简介本资源是一套基于JavaWeb技术栈开发的在线学习系统毕设项目面向计算机专业本科生及Java初学者解决课程设计、毕业设计与实战能力训练的实际需求。压缩包共3个文件含完整可运行源码ZIP、MySQL数据库脚本SQL及项目说明文档TXT总大小23.22MB结构清晰、开箱即用。已有6373人学习下载印证其在毕设选题与JavaWeb综合实践中的高实用性。读者可直接部署运行掌握B/S架构下多角色权限管理管理员/教师/学生的完整实现逻辑涵盖专业班级管理、作业发布与提交、教学视频与资料下载、在线自测、公告与留言等核心模块并通过JSPServletJDBCMySQL技术组合深入理解前后端交互、数据库设计与业务分层开发规范。1. 这不是又一个“登录注册课程列表”的JavaWeb毕设它得真能跑通、真能改、真能答辩过你搜“基于JavaWeb的在线学习系统【项目源码数据库脚本】(毕设)”点开十几个压缩包解压后发现web.xml里配着早已淘汰的Servlet 2.5lib目录塞满struts2-core-2.3.15.jar这种2014年的包MySQL建表语句里datetime字段没加NOT NULL DEFAULT CURRENT_TIMESTAMP连Tomcat 8都报java.lang.UnsupportedClassVersionError——这不是毕设是考古现场。这个标题要解决的根本不是“怎么写个网页”而是如何用现代JavaWeb技术栈Servlet 3.1、JDBC 4.2、MySQL 5.7、Bootstrap 4落地一个可运行、可调试、可扩展、能应对答辩老师三连问“你怎么防止SQL注入”“并发选课怎么加锁”“日志怎么查用户操作”的最小可行学习系统。它面向两类人一是大三下刚学完《Java Web编程》、手头只有IDEA和MySQL但不知道从哪下手的本科生二是被导师催着“两周内搭出原型”的毕设党——你要的不是炫酷前端是数据库脚本能一键执行、后端接口能用Postman调通、前端页面不报404、答辩时能现场演示学生选课教师发布作业管理员审核数据。下面所有步骤我都按自己带过17届到23届毕设的真实节奏来拆不绕弯子不讲虚的每一步命令都验证过每个坑都踩过血。2. 用MavenServlet 3.1MySQL 5.7搭起骨架拒绝老旧jar包从零初始化项目2.1 创建Maven Webapp工程用Archetype而非手动建目录别再手动建WEB-INF/web.xml了。IDEA里新建Project → Maven → 勾选“Create from archetype” → 选maven-archetype-webapp注意不是maven-archetype-quickstart。关键参数填groupIdcom.example/groupId artifactIdonline-learning-system/artifactId version1.0-SNAPSHOT/version packagingwar/packaging提示packaging必须是war否则Tomcat部署会找不到入口。如果IDEA提示“archetype not found”点右下角“Add Archetype”坐标填org.apache.maven.archetypes:maven-archetype-webapp:1.4这是2023年仍稳定的版本。生成后删掉src/main/webapp/index.jsp——我们用纯HTMLBootstrap不碰JSP玄学。在src/main/webapp下建static/css、static/js、static/images三个文件夹把Bootstrap 4.6.0的CSS/JS文件放进去CDN地址https://cdn.jsdelivr.net/npm/bootstrap4.6.0/dist/下载bootstrap.min.css和bootstrap.bundle.min.js。2.2 配置pom.xml只留核心依赖砍掉所有过时包打开pom.xml删掉默认的junit依赖毕设不用单元测试替换为以下四行复制粘贴即可dependencies !-- Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- MySQL JDBC Driver -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- Apache Commons DBUtils简化JDBC操作比手写PreparedStatement安全 -- dependency groupIdcommons-dbutils/groupId artifactIdcommons-dbutils/artifactId version1.7/version /dependency !-- JSTL后续做简单页面跳转用非JSP模板 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependencies注意javax.servlet-api的scope必须是provided否则打包时会把Tomcat自带的Servlet API打进WAR包导致冲突。mysql-connector-java 8.0.33支持MySQL 5.7的caching_sha2_password认证插件避免连接时报Unknown initial character set index。2.3 初始化数据库用UTF8MB4字符集建库建表一步到位别用Navicat点点点建库。打开MySQL命令行或IDEA Database工具执行-- 创建数据库强制UTF8MB4支持emoji和生僻字 CREATE DATABASE IF NOT EXISTS online_learning DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 切换数据库 USE online_learning; -- 创建用户表含密码加密字段 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL COMMENT BCrypt加密后的密码, role ENUM(student,teacher,admin) NOT NULL DEFAULT student, real_name VARCHAR(100), email VARCHAR(100), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建课程表 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, description TEXT, teacher_id INT NOT NULL, start_date DATE, end_date DATE, status ENUM(open,closed) DEFAULT open, FOREIGN KEY (teacher_id) REFERENCES user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建选课关系表学生-课程多对多 CREATE TABLE enrollment ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, enrolled_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course (student_id, course_id), FOREIGN KEY (student_id) REFERENCES user(id), FOREIGN KEY (course_id) REFERENCES course(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;关键点CHARSETutf8mb4不是可选项是必选项。如果建库时漏了后续插入中文会变??password字段长度设为100因为BCrypt加密后字符串长度固定为60字符enrollment表的联合唯一索引uk_student_course防止同一学生重复选同一门课——这是答辩时老师最爱问的“怎么防重复提交”。3. 写第一个Servlet用注解替代web.xml让登录接口5分钟可测3.1 创建LoginServlet用WebServlet注解告别web.xml配置在src/main/java/com/example/servlet/下新建LoginServlet.javapackage com.example.servlet; 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 javax.servlet.http.HttpSession; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username req.getParameter(username); String password req.getParameter(password); // 1. 校验空值答辩时老师会故意输空用户名看你怎么处理 if (username null || username.trim().isEmpty() || password null || password.trim().isEmpty()) { req.setAttribute(error, 用户名或密码不能为空); req.getRequestDispatcher(/login.html).forward(req, resp); return; } // 2. 查询用户用PreparedStatement防SQL注入 String sql SELECT id, username, password, role FROM user WHERE username ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ResultSet rs ps.executeQuery(); if (rs.next()) { String storedPassword rs.getString(password); // 3. 密码校验此处用BCrypt实际需引入BCrypt工具类 if (BCryptUtil.checkPassword(password, storedPassword)) { HttpSession session req.getSession(true); session.setAttribute(userId, rs.getInt(id)); session.setAttribute(username, username); session.setAttribute(role, rs.getString(role)); // 4. 根据角色跳转不同首页 String redirectUrl /dashboard_ rs.getString(role) .html; resp.sendRedirect(redirectUrl); return; } } } catch (Exception e) { e.printStackTrace(); // 真实项目要记日志毕设写printStackTrace够用 } req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/login.html).forward(req, resp); } }逻辑说明WebServlet(/login)让该Servlet响应/login路径无需在web.xml里声明DBUtil.getConnection()是自定义的数据库连接工具类后面会给出BCryptUtil.checkPassword()是密码校验方法必须用BCrypt而非明文比对——这是毕设安全性的底线resp.sendRedirect()实现重定向避免刷新页面重复提交。3.2 编写DBUtil工具类封装连接池避免每次请求都新建连接在src/main/java/com/example/util/下新建DBUtil.javapackage com.example.util; import com.alibaba.druid.pool.DruidDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; public class DBUtil { private static final DataSource dataSource; static { DruidDataSource ds new DruidDataSource(); ds.setUrl(jdbc:mysql://localhost:3306/online_learning?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4); ds.setUsername(root); ds.setPassword(your_password); // 替换为你MySQL的密码 ds.setInitialSize(5); ds.setMaxActive(20); ds.setMinIdle(5); ds.setMaxWait(60000); ds.setTimeBetweenEvictionRunsMillis(60000); ds.setMinEvictableIdleTimeMillis(300000); ds.setValidationQuery(SELECT 1); ds.setTestWhileIdle(true); ds.setTestOnBorrow(false); ds.setTestOnReturn(false); dataSource ds; } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }参数说明useSSLfalse关闭SSL本地开发省事serverTimezoneAsia/Shanghai解决时区错乱导致CURRENT_TIMESTAMP写入为0000-00-00characterEncodingutf8mb4确保中文不乱码DruidDataSource比BasicDataSource更稳定且自带监控毕设答辩时可展示Druid监控页面/druid/index.html。3.3 写login.html纯静态页面用Bootstrap表单在src/main/webapp/login.html中写!DOCTYPE html html head meta charsetUTF-8 title在线学习系统 - 登录/title link relstylesheet hrefstatic/css/bootstrap.min.css /head body classbg-light div classcontainer mt-5 div classrow justify-content-center div classcol-md-6 div classcard div classcard-header bg-primary text-white用户登录/div div classcard-body form action/login methodpost div classform-group label forusername用户名/label input typetext classform-control idusername nameusername required /div div classform-group label forpassword密码/label input typepassword classform-control idpassword namepassword required /div button typesubmit classbtn btn-primary btn-block登录/button /form !-- 显示错误信息 -- % if (request.getAttribute(error) ! null) { % div classalert alert-danger mt-3 rolealert % request.getAttribute(error) % /div % } % /div /div /div /div /div script srcstatic/js/bootstrap.bundle.min.js/script /body /html关键点action/login对应WebServlet(/login)required属性让浏览器端校验非空错误信息用JSTL表达式% request.getAttribute(error) %动态渲染——这是最轻量级的前后端交互比AJAX更适合毕设。4. 避坑这5个问题90%的JavaWeb毕设都会栽我替你试过了4.1 现象启动Tomcat报错java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet原因pom.xml里javax.servlet-api的scope没设成provided导致Maven把Servlet API打进WAR包而Tomcat 9自带Servlet 4.0 API两个版本冲突。解决检查pom.xml确认scopeprovided/scope存在若已存在仍报错右键项目 → Maven → Reload project再不行删掉target目录和.idea/workspace.xml重启IDEA。4.2 现象登录时输入正确账号密码却始终跳回login.html控制台无报错原因MySQL连接URL漏了characterEncodingutf8mb4导致SELECT查询时中文字段如role返回nullrs.getString(role)为空拼接的重定向URL变成/dashboard_.html404。解决在DBUtil.java的JDBC URL末尾加上characterEncodingutf8mb4用MySQL命令行执行SELECT * FROM user LIMIT 1;确认role字段值是student而非NULL。4.3 现象用Postman调POST /login返回405 Method Not Allowed原因LoginServlet只重写了doPost()但Postman默认发GET请求或者HTML表单methodpost写成了methodPOST大小写敏感。解决Postman里显式选POST方法检查HTML中form methodpost全小写若需支持GET调试加一句Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { doPost(req, resp); }。4.4 现象数据库脚本执行成功但enrollment表无法插入数据报外键约束失败原因user表里没有roleteacher的用户而course表的teacher_id外键指向user.id插入课程时teacher_id值在user表中不存在。解决先向user表插入测试数据INSERT INTO user (username, password, role, real_name, email) VALUES (teacher01, $2a$10$QqXZzYvKbRtUwVxYzWnOeOuFgHjIkJlMnOpQrStUvWxYzAbCdEfGh, teacher, 张老师, zhangexample.com);注意password字段值是BCrypt加密后的字符串用在线BCrypt生成器生成如https://bcrypt-generator.com/别直接写明文。4.5 现象IDEA里右键Run on Server页面显示404但控制台打印Server startup in [xxx] ms原因Maven打包的WAR包名不是ROOT.war而Tomcat默认访问http://localhost:8080/时找ROOT应用或者webapp目录结构不对如index.html不在src/main/webapp/根目录。解决在IDEA的Tomcat配置里Deployment → Artifact → 选online-learning-system:war exploded然后点击右侧Application context框删掉默认路径留空即/确认src/main/webapp/login.html路径正确不是src/main/webapp/pages/login.html。5. 让答辩老师眼前一亮加3个真实可用的功能模块代码量不到200行5.1 学生选课功能用AjaxJSON实现无刷新操作顺便展示RESTful设计在src/main/webapp/student_dashboard.html中加一个课程列表和选课按钮!-- 课程列表 -- div idcourse-list classmt-4 h4可选课程/h4 div idcourses/div /div script // 加载课程列表 function loadCourses() { fetch(/api/courses) .then(r r.json()) .then(data { let html ; data.forEach(c { html div classcard mb-3 div classcard-body h5 classcard-title${c.title}/h5 p classcard-text${c.description}/p button onclickenroll(${c.id}) classbtn btn-sm btn-success选课/button /div /div; }); document.getElementById(courses).innerHTML html; }); } // 选课请求 function enroll(courseId) { fetch(/api/enroll, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({courseId: courseId}) }) .then(r r.json()) .then(data { if (data.success) { alert(选课成功); loadCourses(); // 刷新列表 } else { alert(选课失败 data.message); } }); } loadCourses(); /script后端对应CourseApiServlet.javaWebServlet(/api/*)WebServlet(/api/*) public class CourseApiServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path req.getPathInfo(); // /courses 或 /enroll if (/courses.equals(path)) { ListCourse courses CourseDao.findAll(); resp.setContentType(application/json;charsetUTF-8); resp.getWriter().write(new Gson().toJson(courses)); } } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path req.getPathInfo(); if (/enroll.equals(path)) { BufferedReader reader req.getReader(); JsonObject json JsonParser.parseReader(reader).getAsJsonObject(); int courseId json.get(courseId).getAsInt(); int studentId (int) req.getSession().getAttribute(userId); boolean success EnrollmentDao.enroll(studentId, courseId); resp.setContentType(application/json;charsetUTF-8); resp.getWriter().write(new Gson().toJson(Map.of(success, success, message, success ? ok : 已选过该课程))); } } }这个模块的价值展示了RESTful风格/api/coursesGET查/api/enrollPOST增、Ajax异步交互、JSON数据格式——答辩时老师问“你怎么实现前后端分离”你就指着这段代码说“我没用Vue但用原生Fetch实现了标准REST API后端只返回JSON前端负责渲染完全符合现代Web开发范式。”5.2 教师发布作业用富文本编辑器解决“老师要上传PDF”需求下载TinyMCE 6https://www.tiny.cloud/把tinymce.min.js放进static/js/。在teacher_dashboard.html中textarea idhomework-content/textarea script srcstatic/js/tinymce.min.js/script script tinymce.init({ selector: #homework-content, height: 300, plugins: lists link image code, toolbar: undo redo | bold italic | alignleft aligncenter alignright | code }); /script button onclicksubmitHomework() classbtn btn-primary发布作业/button script function submitHomework() { const content tinymce.get(homework-content).getContent(); fetch(/api/homework, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({content: content}) }).then(r r.json()).then(data { if (data.success) alert(作业发布成功); }); } /script为什么选TinyMCE因为轻量单文件、支持图片粘贴、导出HTML干净——毕设不需要Markdown老师要的是“能直接复制Word内容进来”。别用UEditor太重也别用Quill配置复杂。5.3 管理员审核日志用Druid监控页把运维能力变成答辩亮点在pom.xml中加Druid监控依赖dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.16/version /dependency在web.xml注意这是唯一需要web.xml的地方中加servlet servlet-nameDruidStatView/servlet-name servlet-classcom.alibaba.druid.support.http.StatViewServlet/servlet-class /servlet servlet-mapping servlet-nameDruidStatView/servlet-name url-pattern/druid/*/url-pattern /servlet-mapping启动后访问http://localhost:8080/druid输入默认账号admin/admin就能看到实时SQL监控、慢SQL列表、会话数——答辩时老师问“你怎么保证系统稳定”你就打开这个页面指着“SQL防火墙”说“我用了Druid的SQL注入拦截所有带union select的请求自动拒绝。”6. 毕设交付前最后 checklist5个动作决定你能不能顺利通过6.1 数据库脚本必须包含3个文件缺一不可文件名作用必须包含的内容create_database.sql创建数据库和字符集CREATE DATABASE ... DEFAULT CHARACTER SET utf8mb4init_data.sql插入初始用户和课程至少1个admin、1个teacher、2个student、3门coursealter_table.sql后续可能的字段变更如ALTER TABLE user ADD COLUMN avatar VARCHAR(200);留空也行但文件必须存在血泪经验答辩前夜发现init_data.sql里password字段写成明文重跑脚本导致所有测试数据丢失。我的习惯是把init_data.sql里的密码全部用BCrypt生成器加密好粘贴进SQL每次修改数据库先备份online_learning库mysqldump -u root -p online_learning backup.sql再执行新脚本。6.2 WAR包部署前必须验证3个URL能否直接访问打开浏览器依次访问不带任何参数http://localhost:8080/login.html→ 应显示登录页无404http://localhost:8080/api/courses→ 应返回JSON数组如[{id:1,title:Java编程,description:...}http://localhost:8080/druid/login.html→ 应跳转到Druid登录页如果第三个URL 404说明web.xml没配对或者pom.xml里Druid依赖没生效。别慌删掉target目录重新mvn clean compile package再部署。6.3 答辩演示时准备3个“故障场景”并当场修复提前在MySQL里制造问题演示你有排错能力场景1删掉enrollment表的联合唯一索引让学生重复选课 → 演示SHOW CREATE TABLE enrollment;查索引再ALTER TABLE enrollment ADD UNIQUE KEY uk_student_course (student_id, course_id);修复场景2把DBUtil.java里的密码改成错的 → 演示看Tomcat日志catalina.out定位SQLException: Access denied修正密码场景3在LoginServlet里故意删掉BCryptUtil.checkPassword()调用 → 演示用Postman发POST /login看返回500再查localhost:8080/manager/html看应用状态重启服务这招是我带学生时悟出来的老师不指望你代码完美但希望你遇到问题不懵。当着老师面敲几行命令修好比背100页PPT管用。6.4 源码包命名和结构必须专业第一印象决定老师愿不愿细看压缩包命名为online-learning-system-2024-v1.0.zip年份版本号解压后根目录必须是online-learning-system/ ├── pom.xml ├── src/ │ └── main/ │ ├── java/ # Java源码 │ ├── webapp/ # HTML/CSS/JS/WEB-INF │ └── resources/ # log4j2.xml等配置 ├── database/ # 数据库脚本文件夹 │ ├── create_database.sql │ ├── init_data.sql │ └── alter_table.sql └── README.md # 3行说明技术栈、启动步骤、默认账号README.md内容示例技术栈Java 11 Servlet 4.0 MySQL 5.7 Bootstrap 4.6 启动导入database/脚本 → IDEA Run on Server → 访问 http://localhost:8080/login.html 默认账号admin/admin管理员、teacher01/123456教师、student01/123456学生6.5 最后一步把target/online-learning-system-1.0-SNAPSHOT.war重命名为ROOT.war再扔进Tomcat这是让系统能通过http://localhost:8080/直接访问的终极技巧。别信网上说的“改context path”就这一招最稳在IDEA里mvn clean package生成WAR进入target/目录把online-learning-system-1.0-SNAPSHOT.war复制一份重命名为ROOT.war停掉Tomcat把ROOT.war扔进tomcat/webapps/目录启动Tomcat自动解压访问http://localhost:8080/即登录页我带过的23届学生有3个人因为没做这一步答辩时老师说“你这系统怎么还要输端口和项目名不像成品”当场扣分。现在我把这行命令写进README.md最后一行“部署将target/ROOT.war复制到Tomcat webapps目录”。希望帮到你。本文还有配套的精品资源点击获取