简介:基于Java与MySQL实现的Web在线评测系统,面向高校计算机相关专业课程设计、毕业设计及Java Web自学者,提供一套可扩展的程序在线评测与教学管理解决方案。系统围绕“选题—解答—评测—统计”闭环,支持学生参加课程、提交作业,教师创建课程与题目、布置作业并查看统计,同时内置内部论坛供师生交流。技术层面采用Spring MVC负责业务控制,Hibernate完成ORM映射,MySQL存储数据,并引入Lucene索引支持高效检索;前端以JSP展示、jQuery控制行为,前后端通过Ajax交互JSON数据,整体架构清晰,便于二次开发扩展。压缩包内含872个文件,以161个Java源码、191个JavaScript脚本、136个class编译文件、44个JSP页面及59个XML配置为主,辅以CSS样式、properties配置、doc文档等,整体约21.11MB,目录结构完整,覆盖源代码、配置、静态资源及说明文档。已有204人学习下载,适合课程设计参考或项目实战入门,可帮助理解在线评测系统的需求分析、数据模型、权限控制与前后端交互实现,并可直接部署运行或按需扩展评测功能。
1. 在线评测系统:课程设计之外的完整可扩展 OJ 方案
几十个人的算法课收代码作业,文件命名从1.java到最终版(3).java什么都有,编译环境更是各玩各的,这是很多做教学的人真实头疼的场面。在线评测系统的价值就是把这道流程线上化:学生选题、写代码、在线提交,系统自动编译运行,把结果反馈给用户。这里要拆的项目正是基于 Java + MySQL 实现的 Web 版可扩展在线评测系统,技术栈是 Spring、Hibernate、Lucene 这一套主流组合,前端 JSP 加 jQuery,前后端走 Ajax 传 JSON。适合正在做 JavaWeb 课程设计、或者想在校内搭一套能持续加需求的 OJ 系统的开发者和学生参考。
2. 技术栈拆解:Spring MVC、Hibernate 与 Lucene 在评测系统里的分工
2.1 为什么这个项目用 Hibernate 而不是 MyBatis
拿到这个系统,最先让人注意的是持久层选了 Hibernate。很多 JavaWeb 课程设计习惯用 MyBatis,因为 SQL 可控性好、上手直观。但这个系统里业务模型之间关系不简单:用户要参加课程,课程下有题目,题目有提交记录,提交要关联判题结果,论坛还得挂到用户上。这种场景用 Hibernate 的对象关系映射确实省事。
Hibernate 的核心优势是把表间关联直接映射成对象引用。查一次提交记录,就能通过submission.getProblem()拿到题目信息,通过submission.getUser()拿到提交人,不用手写 join 和 resultSet 转换。在课程设计这种体量下,这种映射方式的开发效率明显更高。
缓存和懒加载是 Hibernate 的另外两个红利。@ManyToOne默认走懒加载,列表页只查当前表,不进关联表,这对提交记录这种高频查询很友好。二级缓存虽然在这个项目里未必被充分利用,但架构上保留了扩展空间。
当然它不是没有代价。Hibernate 生成的 SQL 通常比手写 SQL 啰嗦,批次插入时如果主键策略选错还容易出现 N+1 问题。但考虑到这个项目的定位是可扩展、结构清晰,优先保证业务模型的表达力,而不是极端场景下的 SQL 性能,这个选择是合理的。
2.2 数据库模型:五张核心表撑起题库、课程、论坛三大业务
在线评测系统的业务最后都要落到数据表上。按这个项目的功能描述,核心业务分成三条线:用户选题做题、教师建课布置作业、论坛交流。对应的数据模型至少需要这些表。
-- 用户表 CREATE TABLE `user` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE, `password` VARCHAR(64) NOT NULL, `role` TINYINT NOT NULL DEFAULT 0 COMMENT '0学生 1教师 2管理员', `real_name` VARCHAR(50), `email` VARCHAR(100), `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程表 CREATE TABLE `course` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `teacher_id` INT NOT NULL, `name` VARCHAR(100) NOT NULL, `description` TEXT, `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程学生关联表(学生参加课程) CREATE TABLE `course_student` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `course_id` INT NOT NULL, `student_id` INT NOT NULL, UNIQUE KEY `uk_course_student` (`course_id`, `student_id`) ); -- 题目表 CREATE TABLE `problem` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `course_id` INT NOT NULL, `title` VARCHAR(100) NOT NULL, `description` TEXT, `input_desc` TEXT, `output_desc` TEXT, `sample_input` TEXT, `sample_output` TEXT, `time_limit` INT DEFAULT 1000 COMMENT '时间限制,单位毫秒', `memory_limit` INT DEFAULT 65536 COMMENT '内存限制,单位KB' ); -- 作业表(教师布置的课后作业) CREATE TABLE `homework` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `course_id` INT NOT NULL, `title` VARCHAR(100) NOT NULL, `deadline` DATETIME, `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 作业题目关联表 CREATE TABLE `homework_problem` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `homework_id` INT NOT NULL, `problem_id` INT NOT NULL ); -- 提交记录表(判题数据的核心) CREATE TABLE `submission` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `user_id` INT NOT NULL, `problem_id` INT NOT NULL, `homework_id` INT COMMENT '为空表示自由练习', `code` TEXT NOT NULL, `language` VARCHAR(20) DEFAULT 'java', `result` TINYINT DEFAULT 0 COMMENT '0等待 1编译错误 2运行错误 3通过 4答案错误 5超时 6超内存', `execute_time` INT, `submit_time` DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 论坛主题表 CREATE TABLE `forum_topic` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `user_id` INT NOT NULL, `title` VARCHAR(200) NOT NULL, `content` TEXT, `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP );这里几个字段设计值得说明。submission表的homework_id允许为空,是为了复用同一套判题流程:学生自由选题时没有作业上下文,教师布置作业后提交才关联到具体作业。result字段用 TINYINT 存状态码而不是直接存字符串,是为了判题模块返回时只做一个整数比对,查询统计也更快。time_limit和memory_limit放在题目表而不是全局配置,保证了不同题目可以有不同的资源限制,这是 OJ 系统的常见做法。
2.3 源码包里的 .class 与 .asta 文件:导入前先认清这三类文件
这个项目源码包里有几个特殊文件需要先认识清楚。
.asta文件是 StarUML 的模型文件。cdesign.asta和program-judge.asta是系统设计和程序判题模块的 UML 模型,里面主要包含类图、用例图这些设计产物。用 StarUML 可以直接打开,对理解整个系统的类结构和模块划分很有帮助。对应还有.asta.bak备份文件,可以忽略。
.class文件是已经编译好的字节码。ModuleService.class、HomeworkController.class、UserController.class、ConfigLoader.class、POJAjax.class这些说明项目里已经有完整的编译产物。但要注意,Java 字节码是向后兼容的,.class文件的编译版本如果高于你本地的 JDK 版本,运行时会直接抛UnsupportedClassVersionError,这是后面避坑章节第一个要解决的问题,导入 IDE 时先检查当前 JDK 版本和这些 class 的编译版本是否匹配。
log.cf是日志相关配置文件。POJAjax.class这个名字值得注意——POJ 指的是 Plain Old Java,这个类大概率是专门封装 Ajax 返回数据的普通 Java 对象,把处理结果统一包成 JSON 结构返回前端,这也是理解了它,后面看 Controller 层代码时会更顺。
3. 本地搭建与配置:从 JDK 版本到数据库脚本的成功路径
3.1 运行环境匹配:JDK、Tomcat、MySQL 的版本对应关系
拿到这类 JavaWeb 课程设计,第一步不是双击打开 IDE,是先确认运行环境。版本不匹配是这类源码最早翻车的地方,而且报错往往不直观。
从项目使用的技术来看,最稳的组合是 JDK 8 + Tomcat 8/9 + MySQL 5.7。JDK 8 是 Spring 4.x 和 Hibernate 4.x/5.x 最成熟的运行环境,Tomcat 8.5 支持 Servlet 3.1,和 Spring MVC 的注解驱动开发配合没有兼容性问题。
MySQL 版本的选择有讲究。MySQL 5.7 是这个项目对应的时代最常用的版本,如果图省事装了 MySQL 8,连接驱动必须换成mysql-connector-java 8.x,否则会遇上认证插件不兼容的报错。MySQL 8 默认的caching_sha2_password认证协议和旧版驱动不认识,启动即报Unable to load authentication plugin。我一般的建议是:先看包里有没有带 lib 目录,里面放着对应版本的数据库驱动 jar,直接用它配套的版本。
具体操作上,JDK 安装后先验证版本,确保java -version输出的是 8:
java -version # 期望输出包含 java version "1.8.x" echo $JAVA_HOME # Windows 上检查环境变量是否指向 JDK8 安装目录Tomcat 单独下载 zip 包解压即可,记得配置CATALINA_HOME环境变量。如果本地装了多个 JDK,IDE 里也要单独给项目指定编译级别,不要默认走环境变量,这一步容易忽略。
3.2 导入与初始化:建库脚本、连接参数与数据填充
数据库初始化是这个项目跑起来的第二个关口。
# 登录 MySQL,创建数据库并指定字符集 mysql -uroot -p CREATE DATABASE oj_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE oj_system; # 从项目目录导入表结构 source /path/to/project/sql/oj_system.sql;UTF8MB4 是一个容易忽略的细节。如果建库时只写了utf8,而题目描述里有数学符号或者 Emoji 之类的字符,存进去直接变问号。这个项目是教学场景,题目描述里可能包含各种特殊符号,建库时强制用utf8mb4是避免乱码全链路的第一步。
有些课程设计源码会利用 Hibernate 的hbm2ddl.auto=update自动建表,那就不需要手动执行 SQL 脚本。但我不推荐把它当作初始化的唯一手段——它建出来的表和作者原始设计里的索引、外键约束可能不完全一致,后面排查问题时缺索引会让你误判是代码 bug。最可靠的路径是:手动执行项目自带的 SQL 脚本建立表结构,然后把 Hibernate 的 DDL 自动策略关掉或改成validate。
导入完成后,检查一下关键表的记录数:
mysql -uroot -p -e "USE oj_system; SHOW TABLES; SELECT COUNT(*) FROM user;"如果user表里有数据,说明脚本连初始账号都建好了,直接用上面记录的账号密码登录比自己去注册更方便。
3.3 核心配置拆解:hibernate.cfg.xml 与 Spring 配置中的关键参数
这个项目的配置入口主要在hibernate.cfg.xml和 Spring MVC 的配置文件中。这两处能跑通,整个项目就成功了一半。
先看 Hibernate 的数据库连接配置:
<hibernate-configuration> <session-factory> <!-- 数据库连接四要素 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/oj_system?useUnicode=true&characterEncoding=UTF-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">你的密码</property> <!-- 数据库方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!-- DDL策略:validate 表示启动时只校验表结构不修改 --> <property name="hibernate.hbm2ddl.auto">validate</property> <!-- 控制台打印 SQL,排查问题时临时开启 --> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> </session-factory> </hibernate-configuration>连接 URL 里的useUnicode=true&characterEncoding=UTF-8这两组参数一定要保留。没有它们,即使数据库和表的字符集都正确,JDBC 驱动仍然可能用默认的 latin1 编码传数据,中文和特殊字符在传输过程中就会变成乱码。这里是对编码问题的一次性兜底。
MySQL5InnoDBDialect要对应你实际的 MySQL 版本。MySQL 5.7 用这个方言没问题;如果用了 MySQL 8,可以换成MySQL8Dialect,不过前提是驱动也一起升。
Spring 配置里重点关注两个地方。第一是注解扫描包路径,<context:component-scan base-package="com.oj.*"/>里的包路径必须和项目实际的包名一致,不一致会导致 Controller 没有被扫描进去,所有 Ajax 请求全部 404。第二是视图解析器的前后缀配置,它决定了 Controller 返回的字符串被解析到哪个 JSP 目录。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>这段配置的意思是:Controller 层return "problem_list"时,Spring 会去找/WEB-INF/jsp/problem_list.jsp这个文件。如果项目的 JSP 目录不是这个位置,页面会直接白屏,浏览器地址栏还停在原来的 URL。拿到别人的源码,第一件事就是打开这个配置,对照实际的 JSP 目录结构。
最后把编译后的项目打成 WAR 包丢进 Tomcat 的webapps目录,启动 Tomcat,访问http://localhost:8080/项目名/就能看到登录页了。
4. 核心模块实战:判题引擎、Ajax 交互与教师作业流程
4.1 判题核心逻辑:编译、执行、比对三步实现评测
OJ 系统的灵魂在判题模块。这个项目的判题流程可以拆成三个标准步骤:把用户提交的代码写入临时文件、调用 JDK 编译运行、把运行输出和标准答案比对。
Controller 层接收提交请求后,先保存到submission表,状态置为 0(等待评测),然后串行或异步调判题服务。课程设计场景通常串行就够,但真实 OJ 系统一般会用队列异步判题,避免阻塞 HTTP 请求。
判题核心的逻辑结构大致如下:
public JudgeResult judge(int submissionId) { // 1. 从数据库取出提交记录和题目 Submission sub = submissionDao.get(submissionId); Problem problem = sub.getProblem(); // 2. 把用户代码写入临时工作目录 File workDir = new File(System.getProperty("java.io.tmpdir"), "oj_" + submissionId); workDir.mkdirs(); File srcFile = new File(workDir, "Main.java"); Files.write(srcFile.toPath(), sub.getCode().getBytes(StandardCharsets.UTF_8)); // 3. 编译:javac 编译失败说明有编译错误,直接返回 CE Process compileProcess = Runtime.getRuntime() .exec("javac -encoding UTF-8 -d " + workDir.getAbsolutePath() + " " + srcFile.getAbsolutePath()); compileProcess.waitFor(5000, TimeUnit.MILLISECONDS); if (compileProcess.exitValue() != 0) { return JudgeResult.compileError(readAll(compileProcess.getErrorStream())); } // 4. 运行:设置内存和时间限制,读取 stdout Process runProcess = Runtime.getRuntime().exec( "java -cp " + workDir.getAbsolutePath() + " Main"); runProcess.waitFor(problem.getTimeLimit(), TimeUnit.MILLISECONDS); String output = readAll(runProcess.getInputStream()); // 5. 比对输出与标准答案 String expected = problem.getSampleOutput().trim(); if (output.trim().equals(expected)) { return JudgeResult.accepted(); } return JudgeResult.wrongAnswer(); }这段逻辑里有几个参数值得较真。-encoding UTF-8必须在编译参数里显式指定,否则用户提交的代码里如果包含中文注释,在 Windows 默认 GBK 编码的编译环境下会直接编译失败。waitFor的第一个参数是超时时间,这里用的是题目的time_limit字段,而不是写死一个值,这样才能支持不同题目不同限制。
输出比对用trim()是因为很多初学者提交的代码会多打印一行空行或者结尾多了个空格,这些应该视作通过而不是答案错误。比对的粒度可以按题目类型不同,这个项目用精确字符串比对是基础做法,后续可以扩展成忽略首尾空白、按行排序等。
4.2 前端交互:jQuery + Ajax 发送代码与接收 JSON 结果
这个项目前后端通过 Ajax 传 JSON 数据,前端用 jQuery 发起请求,后端 Spring MVC 接收并返回 JSON@ResponseBody。POJAjax这个类就是封装返回结构的载体。
前端代码编辑器里取代码,然后打包成 JSON 提交:
$('#submitBtn').click(function () { // 从代码编辑器获取内容 var code = editor.getValue(); if (!code || code.trim().length === 0) { alert('代码不能为空'); return; } $.ajax({ url: '/oj/submit/problem', type: 'POST', contentType: 'application/json', data: JSON.stringify({ problemId: $('#problemId').val(), language: $('#language').val(), code: code }), success: function (response) { // response 就是 POJAjax 封装后的 JSON 对象 if (response.result === 3) { $('#resultLabel').text('通过').css('color', 'green'); } else { $('#resultLabel').text(translateResult(response.result)).css('color', 'red'); } $('#executeTime').text(response.executeTime + ' ms'); }, error: function () { alert('提交失败,请检查网络'); } }); });这条交互链路有三个细节。第一,contentType明确设置成application/json,如果漏了,Spring MVC 的@RequestBody会接收不到参数。第二,JSON 里的字段名必须和后端接收对象的属性名保持一致,后端POJAjax类的字段是result、executeTime,前端就别写成execute_time。第三,成功回调用response.result判断状态,这里的 3 对应的就是第 2 章数据表设计里约定的「通过」。
4.3 教师端流程:课程、作业、统计的串行设计
教师端是这套系统区别于普通刷题网站的核心模块。教学场景里的完整闭环是:教师创建课程 → 学生选择课程加入 → 教师在课程下创建题目 → 教师把题目组织成作业并设置截止时间 → 学生提交后教师查看统计信息。
这个流程中,「作业」实际上是一个关联容器。homework表只记录作业的基本信息,homework_problem表把题目挂到作业下面,submission表通过homework_id字段关联到具体作业。教师查看作业情况时,核心查询是按作业 ID 过滤提交记录,再按用户聚合统计。
判题状态码的统一对统计很重要,这也是为什么结果字段用数字枚举而不是中文描述:
| result 值 | 状态 | 说明 |
|---|---|---|
| 0 | 等待评测 | 提交入队,尚未开始 |
| 1 | 编译错误(CE) | 代码无法通过编译 |
| 2 | 运行错误(RE) | 运行时异常或非零退出 |
| 3 | 通过(AC) | 输出与标准答案一致 |
| 4 | 答案错误(WA) | 输出存在差异 |
| 5 | 超时(TLE) | 超过题目时间限制 |
| 6 | 超内存(MLE) | 超过题目内存限制 |
教师端查看作业统计时,按这个状态码聚合,一眼就能看出全班 AC 率、哪些题目正确率低,这些数据在教学场景里是很有价值的信息,能让老师知道哪个知识点学生没掌握。
5. 常见问题排查:四个高频坑位与解决路径
5.1 启动报 UnsupportedClassVersionError
现象:Tomcat 或 IDE 启动项目时抛java.lang.UnsupportedClassVersionError: org/springframework/... has been compiled by a more recent version of the Java Runtime,程序直接起不来。
原因:项目中已经编译好的.class文件(比如源码包里带的ModuleService.class)是用高版本 JDK 编译的,而你本地的运行环境 JDK 版本更低。Java 字节码只向前兼容,不向后兼容。比如 class 是 JDK 11 编译的,用 JDK 8 跑必然报这个错。
解决:查看报错日志中提到的版本号,必须高于它,在 IDE 里给项目设置对应的 JDK。如果项目里有源码,最简单的方式是把所有.class文件删掉,让 IDE 用当前 JDK 重新编译源码,一步到位消除版本问题。从那之后我每次导入 JavaWeb 项目,第一件事就是先看 class 文件的编译版本。
5.2 Lucene 索引目录不存在导致搜索模块启动失败
现象:系统启动时报java.io.IOException: no such directory,或者第一次使用搜索功能时直接 500,日志里有org.apache.lucene.store.NoSuchDirectoryException。
原因:Lucene 索引文件的存放路径在配置里写的可能是相对路径,比如./index。而 Tomcat 启动时的当前工作目录是$CATALINA_HOME/bin,和 IDE 里运行时的当前目录不一致,相对路径解析到不存在的位置就抛异常了。
解决:把 Lucene 索引路径改成绝对路径,或者在启动阶段读取配置后先检查目录是否存在,不存在就自动创建。
// 读取索引路径配置,不存在则创建 String indexPath = configLoader.getProperty("lucene.index.path"); File indexDir = new File(indexPath); if (!indexDir.exists()) { indexDir.mkdirs(); }这个坑在本地 IDE 运行和 Tomcat 部署两种方式下表现不一样,IDE 里正常、部署后报错,大概率就是索引路径问题。检查配置走路先看log.cf或 properties 文件里配的路径前缀。
5.3 Ajax 请求 404 或返回 HTML 而不是 JSON
现象:前端 jQuery 发 Ajax 请求,浏览器 Network 面板里显示 404,或者请求 URL 正常但返回的是整个 JSP 页面代码而不是 JSON 数据。
原因:404 一般是 DispatcherServlet 的 URL 映射没覆盖到请求路径,或者@RequestMapping里的路径写错了。返回 HTML 则是后端 Controller 方法上没有@ResponseBody注解,Spring 把返回字符串当成视图名交给InternalResourceViewResolver去找 JSP 了。
解决:检查web.xml里 DispatcherServlet 的url-pattern是/还是/oj/*,这决定所有请求的前缀。再检查 Controller 类是否被 Spring 扫描到——回到第 3 章说的component-scan包路径,包名不一致时注解全部失效。至于返回 JSON 的问题,确认 Controller 方法是@ResponseBody返回,或者用了@RestController,确保返回值通过Jackson序列化成 JSON 而不是走视图解析。
5.4 中文乱码:题目描述变成问号
现象:题目列表中中文标题正常,但进入题目详情后描述文字显示??或????,学生提交的代码里中文注释页显示乱码。
原因:乱码有三种可能性,按链路排查。第一是 JSP 页面本身的pageEncoding没设置 UTF-8;第二是 MySQL 连接 URL 里缺少characterEncoding=UTF-8参数(第 2 章建表时强调过);第三是数据库表字段的字符集是 latin1。三个地方任何一个不一致,都会导致中文在传输或存储过程中丢失。
解决:统一字符集是唯一的办法。JSP 文件头部加<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>,MySQL 连接 URL 加characterEncoding=UTF-8,检查表字段字符集用ALTER TABLE problem CONVERT TO CHARACTER SET utf8mb4;修正。最有预防价值的还是建库那一层,从一开始用 utf8mb4,后面能省掉大量排查时间。
6. 二次开发进阶:扩展语言支持、中文检索与判题沙箱思路
6.1 扩展支持更多语言
这个项目目前主要判 Java 代码,但判题模块的三步流程是通用的。扩展时需要动的地方其实是编译和执行命令的选择逻辑。最常见的做法是在提交表加language字段,判题服务里按语言找到对应的编译器命令和输出比对规则。
switch (sub.getLanguage()) { case "java": compileCmd = "javac -encoding UTF-8 " + srcFile.getAbsolutePath(); runCmd = "java -cp " + workDir + " Main"; break; case "python": compileCmd = null; // Python 不需要编译 runCmd = "python3 " + srcFile.getAbsolutePath(); break; default: return JudgeResult.unsupportedLanguage(); }Python 这类解释型语言没有编译阶段,流程直接从编译跳到运行,这个分支逻辑要判断清楚,别让解释型语言去走编译那一步。
6.2 Lucene 中文分词的接入
系统用 Lucene 做内部论坛和题库的搜索,但默认的StandardAnalyzer对中文不太友好——它会按单字切分,「在线评测」会被拆成「在」「线」「评」「测」四个单字,搜索结果自然不准确。
教学论坛和题库的搜索场景是学术性内容,建议换成 IK Analyzer 这类中文分词器,它会按词典把「在线评测」作为一个整体词索引。接入方式不复杂:把分词器 jar 放进 lib 目录,索引写入时用 IKAnalyzer 替换原来的 StandardAnalyzer,查询时用同一个分词器保持一致。旧索引如果已经建过,要删掉重建,因为分词方式变了,旧索引的词典和新查询根本对不上。
6.3 判题沙箱的边界意识
最后必须聊一下安全边界。Runtime.getRuntime().exec直接执行用户代码,在真实公网环境里风险极大——用户可以写一段Runtime.exec("rm -rf /")或者无限循环。课程设计项目可以接受这种直接执行的方式,但如果系统要部署到真实环境,必须在判题模块外面套沙箱。
Java 场景最轻量的做法是启动 JVM 时加-Djava.security.manager启用 SecurityManager,限制文件访问和网络权限。更可靠的做法是拆出独立判题服务,用 Docker 容器隔离每个用户的代码执行,容器外层再限制内存和 CPU。每次环境安全事故都是血泪教训,这类评测系统如果不能确认封死用户代码的执行边界,就不要随意放到公网上线。
我第一次把这段判题逻辑写好时,在本地直接跑通就交差了,结果同学用他电脑的 JDK 17 一启动,立刻UnsupportedClassVersionError,整个页面白屏。从那以后我拿到任何 JavaWeb 源码,第一件事是确认编译环境和运行环境的 JDK 版本一致,再动数据库和配置文件。希望帮到你。
本文还有配套的精品资源,点击获取