1. 前端页面:学生登录、考试界面、考试结束页面。
  2. 后端逻辑
    • 处理登录验证。
    • 从数据库读取考试题目和选项。
    • 处理学生提交的答案。
    • 计算考试成绩并保存。
  3. 数据库设计:存储学生信息、考试题目、学生答案和成绩。
  4. 项目结构:标准的 Maven Web 项目结构。

第 1 步:项目环境准备

  1. JDK: 安装 JDK 8 或更高版本。
  2. Web 服务器: Apache Tomcat 9.x。
  3. 数据库: MySQL 8.0。
  4. 开发工具: IntelliJ IDEA (推荐) 或 Eclipse。
  5. 构建工具: Maven (用于管理依赖)。

第 2 步:数据库设计

我们需要创建几张表来存储数据:students (学生表), questions (题目表), exams (考试表), student_answers (学生答题记录表)。

jsp制作学生考试网页
(图片来源网络,侵删)

在 MySQL 中执行以下 SQL 语句:

-- 创建数据库
CREATE DATABASE exam_db;
USE exam_db;
-- 1. 学生表
CREATE TABLE students (
    id INT PRIMARY KEY AUTO_INCREMENT,
    student_number VARCHAR(20) NOT NULL UNIQUE,
    password VARCHAR(50) NOT NULL,
    student_name VARCHAR(50) NOT NULL
);
-- 2. 题目表
CREATE TABLE questions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    question_text TEXT NOT NULL,
    option_a TEXT NOT NULL,
    option_b TEXT NOT NULL,
    option_c TEXT NOT NULL,
    option_d TEXT NOT NULL,
    correct_answer CHAR(1) NOT NULL -- 'A', 'B', 'C', 或 'D'
);
-- 3. 考试表 (可以扩展,比如考试名称、时间限制等)
CREATE TABLE exams (
    id INT PRIMARY KEY AUTO_INCREMENT,
    exam_name VARCHAR(100) NOT NULL
);
-- 4. 学生答题记录表
CREATE TABLE student_answers (
    id INT PRIMARY KEY AUTO_INCREMENT,
    student_id INT NOT NULL,
    question_id INT NOT NULL,
    selected_answer CHAR(1),
    is_correct BOOLEAN,
    exam_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (student_id) REFERENCES students(id),
    FOREIGN KEY (question_id) REFERENCES questions(id)
);

插入一些测试数据:

-- 插入一个学生
INSERT INTO students (student_number, password, student_name) VALUES ('2025001', '123456', '张三');
-- 插入几道题目
INSERT INTO questions (question_text, option_a, option_b, option_c, option_d, correct_answer) VALUES
('Java 是由哪个公司开发的?', 'Sun', 'Microsoft', 'Oracle', 'IBM', 'A'),
('以下哪个不是 Java 的基本数据类型?', 'int', 'String', 'boolean', 'char', 'B'),
('JSP 的全称是什么?', 'Java Server Page', 'Java Standard Procedure', 'Java System Process', 'Java Script Protocol', 'A');
-- 插入一个考试记录
INSERT INTO exams (exam_name) VALUES ('Java 基础测试');

第 3 步:创建 Maven Web 项目

在 IntelliJ IDEA 中,创建一个新的 Maven 项目,并选择 maven-archetype-webapp 模板。

项目结构如下:

jsp制作学生考试网页
(图片来源网络,侵删)
exam-system
├── src
│   ├── main
│   │   ├── java          // Java 源代码
│   │   │   └── com
│   │   │       └── example
│   │   │           └── exam
│   │   │               ├── dao          // 数据访问对象
│   │   │               ├── model        // 实体类
│   │   │               ├── util         // 工具类 (如数据库连接)
│   │   │               └── web          // Servlet 和 JSP
│   │   │                   ├── LoginServlet.java
│   │   │                   ├── ExamServlet.java
│   │   │                   └── SubmitServlet.java
│   │   ├── resources     // 配置文件 (如 db.properties)
│   │   └── webapp        // Web 应用根目录
│   │       ├── WEB-INF
│   │       │   └── web.xml
│   │       ├── css
│   │       │   └── style.css
│   │       └── jsp
│   │           ├── login.jsp
│   │           ├── exam.jsp
│   │           └── result.jsp
│   └── test
└── pom.xml

第 4 步:添加依赖 (pom.xml)

打开 pom.xml 文件,添加必要的依赖:

<dependencies>
    <!-- Servlet API -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- JSP API -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
        <scope>provided</scope>
    </dependency>
    <!-- JSTL (JSP Standard Tag Library) -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- MySQL Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
</dependencies>

第 5 步:编写后端代码

1 数据库连接工具类 (DBUtil.java)

src/main/java/com/example/exam/util 目录下创建。

package com.example.exam.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
    private static final String URL = "jdbc:mysql://localhost:3306/exam_db?useSSL=false&serverTimezone=UTC";
    private static final String USER = "root"; // 你的数据库用户名
    private static final String PASSWORD = "your_password"; // 你的数据库密码
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USER, PASSWORD);
    }
    public static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

2 实体类 (Student.java, Question.java)

src/main/java/com/example/exam/model 目录下创建。

// Student.java
package com.example.exam.model;
public class Student {
    private int id;
    private String studentNumber;
    private String password;
    private String studentName;
    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getStudentNumber() { return studentNumber; }
    public void setStudentNumber(String studentNumber) { this.studentNumber = studentNumber; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public String getStudentName() { return studentName; }
    public void setStudentName(String studentName) { this.studentName = studentName; }
}
// Question.java
package com.example.exam.model;
public class Question {
    private int id;
    private String questionText;
    private String optionA;
    private String optionB;
    private String optionC;
    private String optionD;
    private String correctAnswer;
    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getQuestionText() { return questionText; }
    public void setQuestionText(String questionText) { this.questionText = questionText; }
    public String getOptionA() { return optionA; }
    public void setOptionA(String optionA) { this.optionA = optionA; }
    public String getOptionB() { return optionB; }
    public void setOptionB(String optionB) { this.optionB = optionB; }
    public String getOptionC() { return optionC; }
    public void setOptionC(String optionC) { this.optionC = optionC; }
    public String getOptionD() { return optionD; }
    public void setOptionD(String optionD) { this.optionD = optionD; }
    public String getCorrectAnswer() { return correctAnswer; }
    public void setCorrectAnswer(String correctAnswer) { this.correctAnswer = correctAnswer; }
}

3 Servlet (控制器)

src/main/java/com/example/exam/web 目录下创建。

jsp制作学生考试网页
(图片来源网络,侵删)

LoginServlet.java - 处理登录

package com.example.exam.web;
import com.example.exam.model.Student;
import com.example.exam.util.DBUtil;
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;
import java.sql.SQLException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String studentNumber = req.getParameter("studentNumber");
        String password = req.getParameter("password");
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        Student student = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "SELECT * FROM students WHERE student_number = ? AND password = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, studentNumber);
            ps.setString(2, password);
            rs = ps.executeQuery();
            if (rs.next()) {
                student = new Student();
                student.setId(rs.getInt("id"));
                student.setStudentNumber(rs.getString("student_number"));
                student.setStudentName(rs.getString("student_name"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.closeConnection(conn);
        }
        if (student != null) {
            // 登录成功,将学生信息存入 session
            HttpSession session = req.getSession();
            session.setAttribute("student", student);
            // 重定向到考试页面
            resp.sendRedirect(req.getContextPath() + "/jsp/exam.jsp");
        } else {
            // 登录失败,返回登录页面并提示错误
            req.setAttribute("error", "学号或密码错误!");
            req.getRequestDispatcher("/jsp/login.jsp").forward(req, resp);
        }
    }
}

ExamServlet.java - 加载考试题目

package com.example.exam.web;
import com.example.exam.model.Question;
import com.example.exam.util.DBUtil;
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 java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@WebServlet("/exam")
public class ExamServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 检查用户是否已登录
        if (req.getSession().getAttribute("student") == null) {
            resp.sendRedirect(req.getContextPath() + "/jsp/login.jsp");
            return;
        }
        List<Question> questions = new ArrayList<>();
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "SELECT * FROM questions";
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            while (rs.next()) {
                Question q = new Question();
                q.setId(rs.getInt("id"));
                q.setQuestionText(rs.getString("question_text"));
                q.setOptionA(rs.getString("option_a"));
                q.setOptionB(rs.getString("option_b"));
                q.setOptionC(rs.getString("option_c"));
                q.setOptionD(rs.getString("option_d"));
                q.setCorrectAnswer(rs.getString("correct_answer"));
                questions.add(q);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.closeConnection(conn);
        }
        req.setAttribute("questions", questions);
        req.getRequestDispatcher("/jsp/exam.jsp").forward(req, resp);
    }
}

SubmitServlet.java - 处理答案提交和计分

package com.example.exam.web;
import com.example.exam.model.Question;
import com.example.exam.util.DBUtil;
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;
import java.sql.SQLException;
import java.util.List;
@WebServlet("/submit")
public class SubmitServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        Student student = (Student) session.getAttribute("student");
        if (student == null) {
            resp.sendRedirect(req.getContextPath() + "/jsp/login.jsp");
            return;
        }
        int correctCount = 0;
        List<Question> questions = (List<Question>) req.getAttribute("questions"); // 从 ExamServlet 传递过来,或者重新查询
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            // 开始事务
            conn.setAutoCommit(false);
            // 1. 保存学生的答案并计分
            String insertSql = "INSERT INTO student_answers (student_id, question_id, selected_answer, is_correct) VALUES (?, ?, ?, ?)";
            PreparedStatement insertPs = conn.prepareStatement(insertSql);
            for (Question q : questions) {
                String selectedAnswer = req.getParameter("answer_" + q.getId());
                boolean isCorrect = selectedAnswer != null && selectedAnswer.equals(q.getCorrectAnswer());
                if (isCorrect) {
                    correctCount++;
                }
                insertPs.setInt(1, student.getId());
                insertPs.setInt(2, q.getId());
                insertPs.setString(3, selectedAnswer);
                insertPs.setBoolean(4, isCorrect);
                insertPs.addBatch();
            }
            insertPs.executeBatch();
            // 2. 计算总分 (假设共10题,每题10分)
            int totalScore = (correctCount * 10);
            req.setAttribute("score", totalScore);
            req.setAttribute("correctCount", correctCount);
            req.setAttribute("totalCount", questions.size());
            // 提交事务
            conn.commit();
        } catch (SQLException e) {
            try {
                if (conn != null) conn.rollback(); // 回滚事务
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                if (conn != null) {
                    conn.setAutoCommit(true); // 恢复默认提交模式
                    DBUtil.closeConnection(conn);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        req.getRequestDispatcher("/jsp/result.jsp").forward(req, resp);
    }
}

第 6 步:编写前端页面

src/main/webapp 目录下创建 jspcss 文件夹。

css/style.css - 样式文件

body {
    font-family: 'Arial', sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 20px;
}
.container {
    max-width: 800px;
    margin: 0 auto;
    background-color: #fff;
    padding: 30px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1, h2 {
    color: #333;
    text-align: center;
}
.form-group {
    margin-bottom: 15px;
}
label {
    display: block;
    margin-bottom: 5px;
    font-weight: bold;
}
input[type="text"], input[type="password"] {
    width: 100%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-sizing: border-box;
}
.btn {
    display: inline-block;
    background-color: #007bff;
    color: #fff;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
    text-align: center;
    text-decoration: none;
}
.btn:hover {
    background-color: #0056b3;
}
.question-card {
    border: 1px solid #ddd;
    padding: 20px;
    margin-bottom: 20px;
    border-radius: 5px;
}
.question-title {
    font-weight: bold;
    margin-bottom: 10px;
}
.options label {
    display: block;
    margin: 8px 0;
    cursor: pointer;
}
.options input[type="radio"] {
    margin-right: 10px;
}

jsp/login.jsp - 登录页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>学生考试系统 - 登录</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css">
</head>
<body>
<div class="container">
    <h1>学生考试系统</h1>
    <%-- 显示错误信息 --%>
    <c:if test="${not empty error}">
        <div style="color: red; text-align: center; margin-bottom: 15px;">${error}</div>
    </c:if>
    <form action="${pageContext.request.contextPath}/login" method="post">
        <div class="form-group">
            <label for="studentNumber">学号:</label>
            <input type="text" id="studentNumber" name="studentNumber" required>
        </div>
        <div class="form-group">
            <label for="password">密码:</label>
            <input type="password" id="password" name="password" required>
        </div>
        <div class="form-group" style="text-align: center;">
            <button type="submit" class="btn">登录</button>
        </div>
    </form>
</div>
</body>
</html>

jsp/exam.jsp - 考试页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>Java 基础测试</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css">
</head>
<body>
<div class="container">
    <h1>Java 基础测试</h1>
    <p style="text-align: center; color: #666;">请仔细阅读题目,选择最合适的答案。</p>
    <form action="${pageContext.request.contextPath}/submit" method="post">
        <c:forEach var="q" items="${questions}" varStatus="status">
            <div class="question-card">
                <div class="question-title">
                    <c:out value="${status.count}. ${q.questionText}" />
                </div>
                <div class="options">
                    <label><input type="radio" name="answer_${q.id}" value="A"> A. <c:out value="${q.optionA}" /></label>
                    <label><input type="radio" name="answer_${q.id}" value="B"> B. <c:out value="${q.optionB}" /></label>
                    <label><input type="radio" name="answer_${q.id}" value="C"> C. <c:out value="${q.optionC}" /></label>
                    <label><input type="radio" name="answer_${q.id}" value="D"> D. <c:out value="${q.optionD}" /></label>
                </div>
            </div>
        </c:forEach>
        <div style="text-align: center; margin-top: 20px;">
            <button type="submit" class="btn">提交试卷</button>
        </div>
    </form>
</div>
</body>
</html>

jsp/result.jsp - 考试结果页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>考试结果</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css">
</head>
<body>
<div class="container">
    <h1>考试结果</h1>
    <p style="text-align: center; font-size: 18px;">
        同学,<strong>${student.studentName}</strong>,你的考试成绩如下:
    </p>
    <div style="text-align: center; font-size: 24px; margin: 30px 0; color: #007bff;">
        得分: <strong>${score}</strong> 分
    </div>
    <div style="text-align: center; color: #666;">
        答对 <c:out value="${correctCount}" /> 题,共 <c:out value="${totalCount}" /> 题。
    </div>
    <div style="text-align: center; margin-top: 30px;">
        <a href="${pageContext.request.contextPath}/jsp/login.jsp" class="btn">返回登录</a>
    </div>
</div>
</body>
</html>

第 7 步:配置 web.xml

虽然我们使用了 @WebServlet 注解,但最好还是配置一下欢迎页面。

src/main/webapp/WEB-INF/web.xml 中:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <welcome-file-list>
        <welcome-file>jsp/login.jsp</welcome-file>
    </welcome-file-list>
</web-app>

第 8 步:部署和运行

  1. 配置 Tomcat: 在 IntelliJ IDEA 中配置好你的 Tomcat 服务器。
  2. 部署项目: 将你的 exam-system 项目部署到 Tomcat。
  3. 启动 Tomcat: 启动服务器。
  4. 访问: 在浏览器中访问 http://localhost:8080/your-project-name/ (将 your-project-name 替换成你的项目在 Tomcat 中的实际名称)。
    • 首先会看到 login.jsp 登录页面。
    • 输入学号 2025001 和密码 123456 登录。
    • 登录成功后跳转到 exam.jsp 考试页面。
    • 答完题后点击“提交试卷”,跳转到 result.jsp 查看成绩。

总结与扩展

这个 JSP 学生考试网页已经具备了基本功能,你可以在此基础上进行扩展,

  • 增加时间限制:在 ExamServlet 中设置考试开始时间,在 SubmitServlet 中检查是否超时。
  • 题库管理:增加管理员界面,可以动态添加、修改、删除题目。
  • 多套试卷:在数据库中增加 exams 表,将题目与试卷关联,学生可以选择不同的试卷进行考试。
  • 更美观的UI:使用 Bootstrap 或 Vue.js 等前端框架来美化界面,增加交互性。
  • 防止重复提交:使用 synchronized 或令牌机制来防止学生多次提交答案。