尧图精选

基于J2EE的在线考试系统设计与实战:从分层架构到并发控制

🕒 发布时间:2026/10/2 4:33:19 📁 来源:尧图网络
开篇为什么我建议练手项目选这个如果你正在学 Java 后端或者准备毕业设计、跳槽面试想找一个“既能展示基本功、又不会烂大街到千篇一律”的项目基于 J2EE 的在线考试系统是个被很多人低估的选择。它不像电商系统那样堆业务也不像简单的 CRUD 那样缺乏含金量它天然包含了用户登录、权限控制、考试流程状态机、随机抽题、限时计时、自动阅卷和成绩统计这些典型模块每一块都能拆开细讲面试时几乎每个技术点都有得聊。我当年做这个项目时踩过不少坑尤其是“考试中途刷新页面导致答案丢失”“多人在线同时交卷时数据库连接被占满”这类问题相信很多同学也遇到过。这篇文章把我当时的整体设计思路、核心模块的实现细节、踩坑记录和排查方法完整写出来基于 J2EE 的这套方案即使放在今天它的分层思想、会话管理和事务控制依然适用换成 Spring Boot 也只是一个框架迁移的问题核心逻辑完全相通。这篇文章适合正在准备毕设的计算机专业学生也适合想系统梳理 J2EE 知识点的在职开发甚至你只是想找一个能写到简历上的项目下面的内容都能给你直接参考的代码结构和设计草图。1. 整体架构设计与技术选型思路1.1 这个“在线考试系统”到底要解决什么问题在线考试系统的核心痛点不是“能不能答题”而是“怎么保证考试过程公平、结果可靠、并发不出错”。线下考试有监考老师盯着线上考试全靠系统自身的逻辑去约束所以整体设计必须围绕三条主线展开身份可信只有合法用户能进考场、过程可控考试期间不能随意切换、超时要自动交卷、结果可用每道题、每个得分都要有据可查。从功能拆解来看一套完整系统通常包含三类角色管理员负责维护题库、创建试卷、查看成绩教师负责批改主观题和管理考试场次学生负责参加考试和查询成绩。这个角色模型决定了权限模型必须设计成基于角色的访问控制RBAC而不能简单地在每个页面里写死“是否是管理员”的判断。我在实际开发中把系统拆成了两大端管理端和考试端。管理端是典型的“重表单页面”新增试题、组卷、发布考试考试端则是“重流程页面”倒计时、切题、交卷。两类页面的交互模式完全不同所以在设计页面模板和请求接口时要有意识地分开否则后期会越改越乱。1.2 J2EE 分层架构到底分哪些层各层职责是什么J2EE 的传统分层模型到今天依然是 Java Web 项目的主流思想按我的习惯分为四层Web 表现层、业务逻辑层、数据访问层和基础数据层。Web 表现层直接面向用户负责接收请求、处理表单校验、把结果渲染到页面。在 J2EE 时代这一层通常是 JSP加上ServletJSP 管展示Servlet 管请求分发。这里有个容易犯的错很多初学者喜欢在 JSP 里写 Java 代码块% %结果页面乱成一锅粥。我当时的做法是严格禁止在 JSP 中写业务逻辑只用 EL 表达式和 JSTL 标签库取值逻辑全部封装到 Servlet 里这样页面只保留结构维护起来非常舒服。业务逻辑层是整个系统最核心的一层所有规则都放这里。比如“考试是否可以开始”的判断规则是当前时间在考试开放时间范围内、考生状态为已报名、考生没有缺考记录三条同时满足才能进入考场。这些规则如果是分散写在 Servlet 里一旦规则变化就要到处改集中写在业务层里之后规则变更只需要改一处。业务层你需要面向接口设计每个模块先写接口再用实现类完成这也是 J2EE 分层思想中的重要约定。数据访问层负责和数据库打交道用 JDBC 写基础增删改查用 DAO 模式对上层屏蔽数据库差异。这样设计的好处是今天用 MySQL 明天换 Oracle只有这一层需要改动业务层完全不受影响。基础数据层就是我们的数据库本身包括表结构设计、索引、视图、存储过程等。1.3 技术选型的权衡Servlet、Struts 还是更现代的框架在用 J2EE 做这个项目时灯具是绕不开的选择题。我当时纠结过纯 Servlet、Struts 2 还是轻量级的 Spring MVC。如果你现在做这个项目我也建议先考虑这几个方向。纯 Servlet 的优点是没有额外依赖所有流程自己控制适合理解 HTTP 请求的完整生命周期缺点是参数封装、表单校验、请求转发要写大量重复代码开发效率偏低。Struts 2 在当年是主流拦截器机制非常强大但 XML 配置量很大对新手很不友好。Spring MVC 的注解式开发让代码量大幅减少是目前最推荐的方案但它其实已经属于 Spring 生态严格来说不算纯 J2EE 的范畴。我的建议是如果你是用于学习 J2EE 原理核心模块用 Servlet JSP 手写一遍就够了如果你是用于项目交付或毕业设计直接用 Servlet JSP 做表现层业务层和数据访问层自己写这样可以完整展示 J2EE 分层能力又不至于被框架源码淹没。我最终选择的是 Servlet JSP JDBC 这套组合没有引入重量级框架因为我要在一篇文章里讲清楚“系统的设计逻辑”而不是“框架怎么配置”。2. 数据库设计与核心模块实现细节2.1 表结构怎么设计才能支撑“考试”这个业务场景数据库设计往往决定了后续开发是舒舒服服还是处处踩坑。在线考试系统的表结构按我最终的实践可以归纳为九张核心表先看整体清单再逐个解释。用户表user存放所有账号基础信息字段包括用户ID、用户名、密码加密后的密文、真实姓名、角色类型、创建时间。角色类型用数字区分1代表管理员2代表教师3代表学生。密码我使用了加盐的 MD5 存储虽然现在看来不算最安全但 J2EE 阶段配合消息摘要算法是够用的。题库表question用来存放所有试题。字段包括题目ID、所属科目、题型1单选、2多选、3判断、4简答、题目内容、选项A到选项D非选择题可置空、正确答案、分值、难度系数。这里有一个关键设计正确答案绝对不能明文存储在查询列表里否则一旦查询条件写错把答案带到前端页面就是重大事故。我当时把答案字段独立出来业务层查询时明确指定“不查出该字段”展示和判卷走两条不同的查询路径。试卷表exam_paper记录每次考试的基本信息包括试卷ID、试卷名称、考试科目、总时长、总分、是否随机抽题、及格线、创建人、创建时间。是否需要随机抽题这个字段很多人会忽略加了之后才支持“每个学生的题不完全相同”的防作弊策略。考试记录表exam_record用于追踪每位考生参加某场考试的状态。字段包括记录ID、用户ID、试卷ID、开始时间、交卷时间、考试状态1未开始、2进行中、3已完成、总得分。这表是为了解决“考生中途退出后再次进入还能不能继续答”的问题没有这张表断线续考就是纸上谈兵。答题明细表answer_detail记录某位考生对某道题的作答内容。字段包括明细ID、考试记录ID、题目ID、考生答案、是否得分、题目得分。交卷之后系统进行自动判卷结果会写到这里教师也能针对主观题在此表上做人工评分和覆盖操作。辅助表科目表subject、班级表class、考生班级关联表student_class、考试班级可见表exam_paper_class。这套设计是为了支撑“某个班只能看到分配给他们的考试”的逻辑没有它就会出现所有考生都能看到所有考试权限就失控了。2.2 题库随机抽题的三种算法方案对比随机抽题是考试系统里最常被问到的功能点。它要解决的核心问题是既要随机又要保证知识覆盖率还要高效。我对比过三种方案。第一种是数据库 ORDER BY RAND()。SQL 非常简单SELECT * FROM question WHERE subject_id ? ORDER BY RAND() LIMIT 20。优点是代码量极少缺点是当单表数据量超过几万条时RAND() 会对整表进行全量排序性能急剧下降。我在项目初期就用了这个方案测试阶段只有几百道题感觉很快但到数据量过万后明显变慢后来不得不改掉。第二种是“先取 ID 再取题”的方式。先从数据库中查出符合条件的所有题目ID然后在 Java 代码中用Collections.shuffle()或Random进行随机打乱截取前 N 个 ID再用 IN 语句查询完整题目。优点是随机性好缺点是当题库很大时在 Java 内存中加载了过多无用 ID。第三种是我最终使用的“ID 区间随机 补偿”方案。先把题目表中的 ID 最小值查到最大值查到然后生成 N 个处于该区间内的随机数作为目标 ID去数据库按这些 ID 查询。如果某些 ID 已被删除导致查询结果不足 N 条再补查一次。这种方式在题目数量大且 ID 基本连续时性能极好缺点是 ID 断层太严重时会导致补查频率高。实际项目中测试题库的题目 ID 是连续的所以这个方案表现非常稳定。关于随机抽题还有一个容易被忽略的细节同一个学生在一次考试中不能抽到重复题目。所以每次抽题成功后必须把已抽到的题号记录下来并且在后续查询时排除。我最初漏了这个逻辑导致学生刷新页面后题目重复出现排查了大半天才找到原因。2.3 自动阅卷的实现难点客观题与主观题怎么分别处理自动阅卷分为客观题和主观题两条路径。客观题的判卷逻辑其实很简单单选和判断就是对字符串是否相等多选题要求答案完全匹配。我的实现做法是交卷时遍历学生的答案列表对每道题从数据库查出正确答案字符串用equals比较如果答案一致就给这道题的分值不一致则视为零分。多选题的每个选项我用固定分隔符如A,B,C存储比较时先把两个字符串拆分到集合再比较避免顺序不同导致的误判。主观题的自动判卷不能做真正的语义理解只能做关键词匹配。我的做法是为简答题维护一个“得分关键词表”存入人工事先定义好的关键词及权重。阅卷时系统对学生答案做分词处理匹配到一些关键词就按权重累积分得到一个基础分。但这个分数只是参考最终教师要在管理端人工审核并可以修改分数。主观题完全靠机器判是不可靠的系统应该始终保留“教师仲裁”的兜底机制。自动阅卷还有一个容易搞错的地方一旦选择了自动阅卷并交卷成绩就已经生成了。如果教师在后台修改了某道题的标准答案已经考完的学生的成绩应保持不变。所以答题明细表中必须存储“该学生作答时的标准答案快照”而不是在阅卷时实时去查题库里的答案。我就在这里踩过一个坑后来通过在answer_detail表增加standard_answer_snapshot字段解决了凡是涉及历史数据一致性的问题快照方案都是最稳妥的选择。2.4 考试计时与会话状态管理防作弊和防超时背后的逻辑在线考试必须解决一个问题学生开考后考试时间从哪里开始计算、超时后如何强制交卷、中途关闭浏览器二次进入时能否继续。我当时的设计是学生点击“开始考试”时系统在exam_record表插入一条考试记录记录开始时间为当前服务器时间考试状态置为“进行中”。之后页面计时器显示的倒计时由后端返回的剩余时间计算而不是靠前端本地时间。这样即使前端把系统时间改了倒计时依然以服务器为准避免学生通过改本地时间作弊。倒计时的实现我采用的是前端 JavaScript 定时器每秒钟做一次倒计时显示同时每隔一定时间比如30秒向后端发送一次心跳请求同步剩余时间与保持 Session 活跃。这么设计的原因在于如果仅靠前端倒计时学生刷新页面后时间可能会重置这是一个非常严重的漏洞如果只靠服务器时间而不做前端展示学生看不到剩余时间会非常焦虑。前后端结合的方式兼顾了两端需求。对于超时强制交卷我实现了两层措施第一层是前端倒计时归零后立即调用交卷接口第二层是后端在每次接收答案请求时检查剩余时间如果发现已超时直接按照“强制交卷”逻辑处理并把考试状态置为已完成。这层后端兜底非常重要单纯依赖前端会被人绕过。会话状态我用 Session 保存登录用户信息和当前考试记录ID。但这里有个坑Session 默认超时时间通常设置的是 30 分钟如果一场考试时长 90 分钟学生中途长时间不操作导致 Session 过期再操作时已经被踢出登录状态。解决办法是在无状态会话要求下把关键状态尽量存入数据库和 Cookie而 Session 中只保存用户身份信息同时可以将 Session 超时时间设置为比考试时长更长但更好的方案是配合前文提到的心跳请求人在页面上操作时 Session 一直能得到续期。2.5 考试中的防作弊手段哪些值得做哪些纯属摆设防作弊是面试官喜欢深挖的话题。展开讲没意义我能落地且有效果的几个手段分享一下。比较有效的是“切屏检测”。页面监听visibilitychange事件和window.blur事件一旦检测到页面隐藏或失焦就记录切出时间每次切出超过一定次数或累计超过一定时长系统自动标记该场考试“疑似作弊”管理员可以在后台看到标记。这个实现成本低效果也比较显著。同样有效的是“题目乱序”和“选项乱序”。同一个学生前后两次进入考场看到的题目顺序不同选项位置也不同。这让学生在无法与他人共享屏幕信息时很难快速比对答案。我的做法是查询题目时对题目ID做随机排序对多选题和单选题的选项顺序也做随机排列选项和答案在输出时统一映射到 A/B/C/D 位置。这一块要注意数据库存的正确答案是以“A/B/C/D”形式存储的如果选项顺序变了判卷时的答案必须重新映射否则会出现倒了大霉的“全部零分”事故。比较鸡肋的是“强制全屏”。用requestFullscreen在理论上可以实现考试时强制全屏且无法退出但浏览器兼容性差而且现代浏览器都允许用户通过键盘退出全屏。这个方法后来我在实践中删掉了换成“记录切屏次数”并预警。与其做对抗性功能不如留下证据由教师判断是否作弊。3. 核心环节的代码实现与关键参数说明3.1 基于 Servlet 的登录鉴权和权限控制实现登录鉴权是整个系统的入口它的实现好坏直接影响所有页面的安全。我在这个项目里没有引入 Shiro 或 Spring Security因为想展示 J2EE 原生的过滤器机制。用户提交用户名和密码后Servlet 接收请求调用业务层方法从数据库查询用户。密码校验前我把用户输入的明文密码用与注册时相同的盐值拼接后做 MD5 运算再和数据库中存储的密文比对。比对成功后把用户对象写入 SessionSession 的 key 用常量定义而不是直接写字符串。之后我定义了一个过滤器类AuthFilter在 web.xml 中配置过滤规则拦截所有需要登录才能访问的路径。过滤器的核心逻辑是从 Session 中取用户对象存在则放行不存在则跳转到登录页面。对于管理端路径/admin/*还需要额外校验用户的角色是否为管理员否则直接转向无权限提示页。public class AuthFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; HttpSession session req.getSession(false); User user (session ! null) ? (User) session.getAttribute(Constants.SESSION_USER) : null; String uri req.getRequestURI(); if (uri.startsWith(req.getContextPath() /admin/)) { if (user null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } if (!Constants.ROLE_ADMIN.equals(user.getRole())) { resp.sendRedirect(req.getContextPath() /noPermission.jsp); return; } } chain.doFilter(request, response); } }代码第 6 行使用getSession(false)而不是getSession()是为了避免一个常见的逻辑隐患如果用户没有登录getSession()会自动创建一个全新的空 Session让过滤器无法区分“未登录”和“登录了但 Session 过期了”这会导致无限创建无效 Session白白消耗内存。这个问题当初排查了很久报错表现为系统运行一段时间后内存逐渐上涨最后定位到getSession()的滥用上。3.2 在线考试主流程开始考试、答题提交、强制交卷在线考试的流程设计是整个项目的主心骨。正常情况下按以下顺序执行第一步开始考试时前端按钮提交试卷ID控制层接收后调用业务层方法业务层先检查考试记录是否已存在。若存在且状态是“进行中”则直接返回原有记录实现断点续考若不存在则创建一条新的考试记录状态置为“进行中”并把初始开始时间写入数据库。这里关键参数是开始时间记的是服务器时间不能取前端时间。第二步答题过程中前端每切换一道题会自动保存当前题目答案到本地即可但为了稳妥我把每题答案都实时同步发到后端保存。这一步骤的好处是即使出现网络抖动或浏览器崩溃答题内容也不丢失。缺点是有一定的数据库写压力但因为题目数量有限每秒几十次写入对于 MySQL 完全扛得住不必采用复杂缓存方案。提交方式上我提供了两个途径一种是学生主动点击交卷按钮一种是倒计时结束自动强制交卷。主动交卷时前端把所有作答数据一次性提交强制交卷时前端提交动作由定时器触发。如果因网络异常导致交卷请求没到达后端则在心跳请求里携带“已到超时时间”的标记后端会在下一次心跳时执行强制交卷逻辑。这样设计等于加了一道保险丝保证超时交卷的最终一致性。交卷的数据库操作都在一个事务中完成先更新考试记录为“已完成”并写入交卷时间再逐题写入答题明细最后批量调用自动判卷逻辑更新得分。使用事务十分必要因为一旦中途出现异常而部分数据已提交会导致“记录已完成但明细不完整”这种灾难性数据不一致。3.3 多人在线同时交卷的并发控制与避坑方案我开发到后期做并发测试时发现问题当几十个学生同时交卷系统会出现明显卡顿甚至偶发报错。后来排查发现主要瓶颈在两个方面数据库连接池的连接数耗尽和事务锁等待。最初用 C3P0 连接池时我设置的初始连接数是 5最大连接数是 20。当外部没有并发时完全够用但考试是典型的高峰值场景结束前 5 分钟所有人都挤在一起交卷瞬间请求量可能达到几十甚至上百导致连接池被占满后续请求全部排队。调整方案是使用 DBCP 或 Druid 连接池把最大连接数调整到 100并设置了合理的等待超时时间让请求不至于无限阻塞下去。这个参数调整是最直接见效的一步。第二个瓶颈是事务锁。每个交卷请求都会更新同一张考试记录表虽然不同的学生更新的是不同的行但 MySQL 在大量更新时会存在锁竞争。我采取的优化思路是缩短事务执行时间把比较耗时的答题明细写入拆成小批量分次提交而不是全部在一个事务里完成。比如 50 道题拆成 5 次提交每次 10 条事务时间明显缩短锁覆盖率也大幅下降。更绝的方案是在数据库层面做“错峰”交卷请求启动时先获取一个时间戳如果同一秒内请求数超过阈值就让一部分请求在内存队列里等待 200~500 毫秒再处理。这个“削峰填谷”的思路来源于电商秒杀系统的限流思想把它用在小规模考试系统上实现简单但效果很好。并发控制还有一个细节自动交卷时要严格防止同一考试记录被重复提交。我通过数据库的exam_record表加了一个状态字段在交卷时执行UPDATE exam_record SET status 3 WHERE id ? AND status 2只有当返回更新行数大于 0 时才继续后续逻辑否则直接返回“已交卷”。这种方法用数据库行锁天然地避免重复交卷比在 Java 代码里加锁要可靠得多。3.4 事务控制的两种典型场景与实现示范J2EE 里事务控制最典型的有两个场景登录注册时的数据一致性、交卷阅卷时的数据一致性。在这里重点讲后者。交卷的流程是可以理解为“写明细”和“算总分”两个阶段。假设第 1 题写入成功写到第 30 题时数据库突然报错如果开启了事务前 29 题的写入会整体回滚考试记录状态依然保持“进行中”如果没有事务那就陷入了“写了 29 题但状态还是进行中”的中间状态学生再次进入时题目答案不完整又无法重考非常难处理。所以我强烈建议使用 Spring 的声明式事务或者 JDBC 手动事务来管理这个关键节点。JDBC 手动事务的写法比较直观Connection conn dataSource.getConnection(); try { conn.setAutoCommit(false); // 执行考试记录状态更新 // 循环写入答题明细 // 执行自动判卷和总成绩更新 conn.commit(); } catch (Exception e) { conn.rollback(); throw new RuntimeException(交卷失败请重试, e); } finally { conn.setAutoCommit(true); conn.close(); }代码中setAutoCommit(false)的作用是告诉数据库暂时不要自动提交每条 SQL等所有操作全部成功后再一次性提交这就是事务最核心的理解。初学者最容易犯的错误是在循环里调用statement.executeUpdate()后没有积累在事务中结果还是每条独立提交。事务还有一个隔离级别问题。对于考试场景多个学生同时交卷时用的是不同数据行读已提交的隔离级别就能满足需求但如果要保证查询成绩时看不到未提交的脏数据就要使用读已提交或可重复读。MySQL 默认是可重复读注意在 Oracle 上默认是读已提交这导致在一种数据库上正常、换库后出现问题的现象很常见项目里要确认清楚目标数据库的默认行为。4. 常见问题排查与优化经验实录4.1 刷新就丢答案问题到底出在哪里这个坑几乎每个做在线考试系统的人都会踩。我刚上线测试时发现学生答题中途按了一下 F5页面刷新后刚答过的题全部变成空白气得测试同学直骂娘。一开始我以为是前端没有保存答案排查代码后才发现前端确实在点击下一题时把答案存到了 SessionStorage但刷新后会重新从后端获取题目而获取题目时用的参数是“重新抽题”于是出现了一套新题旧答案当然对不上。根因在于前后端没有约定“同一场考试的题目集合必须稳定”。正确的做法是学生开始考试后后端生成一份该学生专属的“试卷题目快照”下次进入时直接按这个快照返回题目而不是重新抽题。为了支持刷新恢复我的exam_record表增加了一个question_ids字段存的是 JSON 数组格式的题目 ID 序列学生每次进入考试都从这个字段恢复题目列表。这一招从根上解决了刷新丢题的问题。另外答案丢失的问题也不能全怪后端。我在前端实现上把已答题目的答案存到了 SessionStoragekey 是考试记录ID加题目ID刷新页面时先从 SessionStorage 恢复答案再渲染到页面。SessionStorage 的存活周期恰好是标签页生命周期刷新不会清空关闭标签页才会。这样配合后端answer_detail的保存双保险策略下答案基本不可能丢。4.2 页面秒开 vs 长时间卡顿数据库连接与查询优化实录在线考试系统的性能瓶颈大多不在代码逻辑而在数据库查询。我在做完基本功能后进行了一轮性能优化重点关注三类查询。第一类是考试列表。学生进入系统后要看他能参加的考试列表原始 SQL 是关联四张表做条件查询在数据量小的时候还好后来数据量增加到几千条后页面加载明显变慢。优化方式是增加联合索引(student_id, class_id)和(exam_paper_id, class_id)。MySQL 中每张表的关联字段都建议建索引这个原则在后端开发中时刻适用。第二类是题目查询。每次学生进入考试要查询整套试卷的所有题目按原始方案关联题目表和试卷明细表几百道题要查很多次。我做了两个优化一是把题目的列表查询和选项查询合并成一条 SQL用GROUP_CONCAT做选项拼接二是把题目的标准答案和选项内容分两次查询标准答案走独立缓存。前者减少查询次数后者避免答案泄露。第三类是成绩统计。管理端需要查看某个班某场考试的平均分、最高分、及格率。原始 SQL 是对exam_record表做聚合查询但当学生数量达到上千人时查询耗时明显上升。我把聚合语句改成只扫描必要字段的索引查询并为exam_record表的(paper_id, student_id)建立复合索引查询时间从原来的几百毫秒降到了几十毫秒效果立竿见影。做这些优化时有一个通用原则数据量不到十万级别时不要过早引入缓存框架如 Redis先用索引、SQL 优化、分页等基础手段解决问题。直接引入 Redis 反而会增加系统复杂度得不偿失。我在项目里就没用 Redis一样支撑了并发一百多的考试场景。4.3 常见问题速查表与避坑清单现象根因解决方案学生刷新后题目全变每次进入都重新抽题考试记录表保存题目ID快照恢复时按快照查询交卷后成绩为零分选项乱序但判卷未映射新选项判卷时根据选项乱序映射关系还原标准答案考试中途 Session 过期被踢Session 超时时间短于心跳间隔调整 Session 超时并实现心跳续期机制同时交卷时系统卡死连接池连接数不足或事务过长调大连接池上限拆小事务削峰排队简答题分数不符合预期关键词匹配太简单结合分词器与关键词权重人工复核兜底后台查询成绩很慢关联表多且无索引对关联字段建复合索引优化聚合 SQL考试记录状态异常交卷时未用条件更新防止重复提交使用UPDATE ... WHERE status2保证只交一次Cookie 被禁用导致页面异常过于依赖 Cookie 存状态关键状态存入数据库Cookie 只做辅助关于题目的正确答案防止泄露还有一个小技巧考试过程中后端返回给前端的题目数据里不包含正确答案前端页面也完全没有正确答案相关的变量。判卷完成后学生查看成绩时后端再判断是否允许该生查看正确答案。这样可以保证即使学生打开浏览器开发者工具翻遍请求响应也拿不到答案因为数据压根没传过去。4.4 从 J2EE 到 Spring Boot 迁移时哪些设计可以原样保留现在很多人在做这类项目时老师会要求或建议直接使用 Spring Boot。你可以发现J2EE 时期设计的分层思想、事务控制思路、表结构设计、并发控制策略在 Spring Boot 项目中几乎原样保留只是把 Servlet 换成了 Controller把 DAO 手写 JDBC 换成了 MyBatis 或 JPA把过滤器换成了拦截器或 Spring Security。在迁移过程中最不需要改的是数据库表结构。我在 J2EE 中设计的九张表换成 Spring Boot 项目后一行表结构基本都不需要变因为它们是在业务分析层面得到的稳定结构与框架无关。最需要改的是数据访问层手写 JDBC 的 DAO 类换成 MyBatis 的 Mapper 接口后代码量能减少一半以上。业务层的接口设计和事务划分也可以整体平移。原来用Transactional标注在实现类上的方法在 Spring Boot 中注解不变只是事务管理器从原来配置的 JTA 或 JDBC 事务管理器换成了 Spring 自动配置的事务管理器。原来在 web.xml 里配置的过滤器链在 Spring Boot 中可以替换为拦截器注册或者直接使用 Spring Security 过滤器链。所以如果你现在正在犹豫要不要重做一个 Spring Boot 版本我的建议是不要推倒重来而是把现有 J2EE 项目当作一个可运行的原型按层替换技术栈。这个方法能让你在两三天时间里完成迁移而且每迁移一个模块都能对比出框架带来的开发效率差异对理解框架的价值非常有帮助。5. 个人实战体会与可继续扩展的方向我做这个项目最大的体会是一个系统能不能扛住真实使用场景关键不在用了多新的框架而在数据设计是否合理、状态流转是否有闭环、事务边界是否清晰。J2EE 时代常常被人说重但分层思想、组件化开发、事务管理的理念到今天依然是后端核心技能。把基于 J2EE 的这套在线考试系统完完整整做一遍理解透其中的设计决策再去接触微服务中的服务拆分、分布式事务时你会发现有大量概念都能在这里找到投影。最后分享一个亲身经历的小教训项目临近交付时考试时间配置发生了变更我直接在数据库里手工改了exam_paper表的开始时间和结束时间结果有一条学生考试记录的开始时间晚于新的结束时间系统判定这场考试过期学生无法进入。排查好久才意识到是脏数据问题。所以涉及时间、状态这类关键字段所有变更都应该通过管理功能在系统里完成而不是直接修改数据库。这个习惯保住了我之后很多项目的质量。这个系统后续还能扩展的方向不少接入消息推送在考试开始前自动提醒学生增加成绩分析图表让教师直观看到知识点的掌握分布引入题库批量导入功能从 Excel 一次性录入成千上万道题。任何一个方向挑出来都足够撑起另一个有深度的项目。希望我的这些实战记录能帮你绕开我踩过的坑把精力放在真正有价值的设计上。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →