建议大家直接看springboot+mybatis+vue版本的,个人感觉更简单明了,在SE版本的基础上扩展了一些功能。 码云地址
一、项目说明
1.1 项目目标
- 巩固JDBC、Http、Servlet和Java基础等内容
- 实现一个在线做题、判题系统
1.2 项目平台与技术栈
- 技术栈:Servlet、Mysql、Runtime
- 平台与环境:Windows、IDEA、Maven、Tomcat
1.3 项目功能
- 进入题目列表页,展示当前系统中的所有题目
- 进入题目详情页,展示题目的具体要求和代码模块,提供一个编辑框来供用户编辑
- 对Java代码的编译/运行/测试功能,针对用户提交的代码进行编译运行,并自动执行测试用例、返回测试结果
二、项目演示
2.1 进入题目列表页
2.2 进入题目详情页
2.3 编写代码
2.4 提交运行
点击提交之后,服务器最终会将结果会返给客户端。
三、系统流程(核心)
起初,我们需要在后台录入题目和测试用例。
四、系统设计
4.1 执行指令设计
通过Runtime对象下的exec()方法能让Java代码去执行一个具体的指令,并且将标准输出和标准错误进行重定向。
4.2 编译和运行设计
- 一次编译过程中依赖要编译的代码,和标准输入中的内容
- 一次编译运行过程中会产生:状态码、出错原因、标准输出对应的内容、标准错误对应的内容
- 编译过程中依赖临时文件:要编译运行的文件、编译错误对应的文件、标准输出对应的文件、标准错误对应的文件
4.3 读写文件的设计
- 将指定文件中的内容读到String中
- 将String中的内容写到指定文件中
4.4 数据库设计
需要保存题目id、标题、难易程度、描述信息、模板代码和测试用例
4.5 JDBC工具类
- 获取数据库连接
- 释放资源
4.6 题目增删查的设计
4.6.1 查
用sql语句直接去数据库中查询
- 获取题目列表
- 获取所有题目id、标题、难易程度
- 获取一道题目详细信息
- 获取指定id题目的id、标题、难易程度、描述信息、模板代码、测试用例;测试用例不提供给客户端。
4.6.2 增
插入题目的id、标题、难易程度、描述信息、模板代码、测试用例信息到数据库中
4.6.3 删
删除数据库中指定id的行
4.7 题目信息API
4.7.1 获取题目列表页
系统流程橘色部分对应模块
4.7.2 获取题目详情页
系统流程黄色部分对应模块
4.8 提交运行API
系统流程绿色部分对应模块
4.9 前端设计
五、开发步骤
5.1 创建maven项目
在pom.xml文件中引入依赖包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>haozhang</groupId>
<artifactId>online_oj</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<!-- <name>blogdemo Maven Webapp</name> -->
<!-- FIXME change it to the project's website -->
<!-- <url>http://www.example.com</url> -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<!-- servlet和tomcat版本有对应关系 -->
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!--这个意思是我们只在开发阶段使用servlet,部署到tomcat上的时候就不需要了-->
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>online_oj</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
5.2 编译模块设计
5.2.1 设计类让Java代码可以执行一条指令
package compile;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
*
* 借助这个类让Java代码能够去执行一个具体的命令
* 如 javac Test.java
*
* @author haozhang
* @date 2020/09/04
*/
public class CommandUtil {
/**
*
* @param cmd 要执行的命令
* @param stdoutFile 表示标准输出结果重定向到那个文件中 如果为null表示不需要重定向
* @param stderrFile 表示标准错误结果重定向到哪个文件中
* @throws IOException
*/
public static int run(String cmd, String stdoutFile, String stderrFile) throws IOException, InterruptedException {
//1.获取Runtime对象,Runtime对象是一个单例的
Runtime runtime = Runtime.getRuntime();
//2.通过Runtime中的 exec 方法来执行一个指令 相当于在命令行中执行cmd命令
Process process = runtime.exec(cmd);
//3.针对标准输出进行重定向
if (stdoutFile != null) {
InputStream stdoutFrom = process.getInputStream();
OutputStream stdoutTo = new FileOutputStream(stdoutFile);
int ch = -1;
while ((ch = stdoutFrom.read()) != -1) {
stdoutTo.write(ch);
}
stdoutFrom.close();
stdoutTo.close();
}
//4.针对标准错误重定向
if (stderrFile != null) {
InputStream stderrFrom = process.getErrorStream();
OutputStream stderrTo = new FileOutputStream(stderrFile);
int ch = -1;
while ((ch = stderrFrom.read()) != -1) {
stderrTo.write(ch);
}
stderrFrom.close();
stderrTo.close();
}
//5.为了确保子进程先执行完 就需要加上进程等待的逻辑
//父进程会在这里阻塞,直到子进程执行结束,才继续往下执行
int exitCode = process.waitFor();
return exitCode;
}
public static void main(String[] args) throws IOException, InterruptedException {
run("javac", "d:/oj/stdout.txt", "d:/oj/stderr.txt");
}
}
5.2.2 一次编译过程中的依赖
package compile;
/**
*
* 一次编译运行过程中都依赖哪写数据
*
* @author haozhang
* @date 2020/09/04
*/
public