说到JSP(Java Server Pages),很多刚入行或者正在做课程设计的同学可能会觉得它是个“老古董”。确实,在现在的互联网大厂里,Spring Boot、Vue、React早已是主流标配。但是,如果你回头看看国内成千上万的高校、中小学、职业院校的内部系统,你会发现一个惊人的事实:这些系统里,JSP依然占据着半壁江山,甚至是一言堂。
为什么?因为教育行业对稳定性、安全性、Java生态整合能力的要求极高,而对页面炫酷程度的追求相对次要。更重要的是,很多学校的现有资产就是基于JSP+Servlet+JDBC或者SSH/SSM架构构建的,维护成本比推倒重来低得多。
今天,我不跟你讲那些枯燥的定义,咱们直接钻进实战里,聊聊怎么用最接地气的JSP技术,把一个完整的“在线教务管理系统”+“学生信息平台”给搭起来。我会把在这个过程中踩过的坑、选过的型、写过的代码,统统掰开揉碎了讲给你听。
一、 为什么是JSP?别再纠结过时不当时,先看清场景
在动手之前,你可能想问:“老师,都用2026年了,为啥还要学JSP?”
这个问题问得好。咱们换个角度想:
- 部署简单,维护成本低:传统JSP应用打包成WAR包,扔到Tomcat或WebLogic里就能跑。不需要复杂的Node.js环境配置,不需要前端构建工具链(Webpack/Vite),对于学校IT部门来说,运维压力小了一半。
- Java EE生态完善:教育系统的核心是数据和流程(选课、成绩录入、排课)。Java在事务管理、安全性、数据库连接池方面有着无可比拟的优势。JSP作为表现层,和后端Java逻辑结合得天衣无缝(虽然现在不推荐在JSP里写Java代码,但早期的开发习惯依然影响深远)。
- 对新手友好,学习曲线平缓:对于计算机专业的学生来说,理解“请求-响应”模型,JSP是最直观的入口。你写一个HTML,里面嵌几行
<% %>,数据就出来了。这种“所见即所得”的反馈,比直接上Spring MVC的Controller-View分离要亲切得多。 - 老旧系统兼容:很多学校还在用老旧的Oracle或SQL Server数据库,Java的JDBC驱动对这些商业数据库的支持是最成熟的。
所以,咱们的目标不是“造一个新的互联网产品”,而是“构建一个稳定、可靠、可维护的教育行业内部系统”。
二、 技术选型:经典SSH/SSM vs 轻量级组合
在教育系统开发中,技术选型切忌“贪大求全”。我们需要的是稳。
方案A:经典SSM(Spring + Spring MVC + MyBatis)—— 推荐
这是目前教育行业中小规模系统最主流的选择。
- Spring:负责IOC(控制反转)和AOP(面向切面编程),比如事务管理、日志记录。
- Spring MVC:负责Web层的请求分发,解耦Controller和View。
- MyBatis:负责ORM(对象关系映射),SQL可控,对于复杂的教务查询(如多表联查成绩、选课状态)非常友好。
- JSP + JSTL + EL:负责视图层。JSTL(JSP Standard Tag Library)是神器,它能让我们尽量不在JSP里写Java脚本片段(
<% %>),保持页面整洁。
方案B:Servlet + JSP + JDBC(原生模式)
适合超小型项目,或者教学演示。但缺点也很明显:代码冗余、事务管理手动控制、容易出错。不推荐用于实际生产环境,但如果你想深入理解Web原理,可以练手。
方案C:Struts2 + Spring + MyBatis(SSH)
SSH是十年前的霸主。但Struts2有过严重的安全漏洞历史,且配置繁琐。除非你有维护老系统的硬性需求,否则不建议新项目使用。
本章结论:咱们以SSM + JSP + JSTL为核心技术栈进行实战解析。
三、 系统架构设计:教务管理 + 学生信息平台
咱们要把两个看似独立、实则紧密相关的平台整合在一起:
- 在线教务管理系统:面向教师和管理员。
- 核心功能:课程管理、学生信息管理、成绩录入、排课查询、考勤统计。
- 学生信息平台:面向学生。
- 核心功能:个人信息查看、选课系统、成绩查询、课表查看、通知公告。
3.1 数据库设计(关键!)
数据库是教育系统的灵魂。咱们先设计几个核心表:
-- 学生表
CREATE TABLE students (
student_id VARCHAR(20) PRIMARY KEY,
name VARCHAR(50) NOT NULL,
password VARCHAR(64) NOT NULL, -- 存储MD5或BCrypt加密后的密码
class_id VARCHAR(20),
email VARCHAR(100),
enrollment_year INT,
status TINYINT DEFAULT 1 -- 1:在读, 0:休学
);
-- 教师表
CREATE TABLE teachers (
teacher_id VARCHAR(20) PRIMARY KEY,
name VARCHAR(50) NOT NULL,
password VARCHAR(64) NOT NULL,
department VARCHAR(100),
title VARCHAR(50) -- 职称:教授、副教授等
);
-- 课程表
CREATE TABLE courses (
course_id VARCHAR(20) PRIMARY KEY,
course_name VARCHAR(100) NOT NULL,
credits INT NOT NULL,
teacher_id VARCHAR(20),
capacity INT, -- 课程容量
FOREIGN KEY (teacher_id) REFERENCES teachers(teacher_id)
);
-- 选课记录表(关联表)
CREATE TABLE enrollments (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
student_id VARCHAR(20),
course_id VARCHAR(20),
semester VARCHAR(20), -- 如:2024-2025-1
score DECIMAL(5,2), -- 成绩,初始为NULL
status VARCHAR(10) DEFAULT 'ENROLLED', -- ENROLLED, DROPPED, COMPLETED
FOREIGN KEY (student_id) REFERENCES students(student_id),
FOREIGN KEY (course_id) REFERENCES courses(course_id),
UNIQUE KEY unique_enrollment (student_id, course_id, semester)
);
设计要点:
- 联合唯一键:
unique_enrollment确保一个学生在一个学期不能重复选同一门课。 - 成绩字段可空:选课时成绩为空,期末录入后更新。
- 状态字段:用
TINYINT或VARCHAR管理学生状态,便于后续统计(如毕业率、挂科率)。
四、 后端开发实战:SSM框架整合
4.1 Maven依赖配置
在pom.xml中,我们需要引入以下核心依赖:
<dependencies>
<!-- Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.30</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 连接池:Druid(阿里出品,生产环境首选) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.21</version>
</dependency>
<!-- JSP相关 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
4.2 Spring配置文件(applicationContext.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描Service层 -->
<context:component-scan base-package="com.edu.service"/>
<!-- 数据源配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/edu_db?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="your_password"/>
<property name="maxActive" value="20"/>
<property name="initialSize" value="5"/>
</bean>
<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- Mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.edu.mapper"/>
</bean>
<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
4.3 MyBatis Mapper示例
以StudentMapper.xml为例,展示如何编写复杂查询:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.edu.mapper.StudentMapper">
<!-- 分页查询学生列表,支持按姓名模糊搜索 -->
<select id="selectStudentsByCondition" resultType="com.edu.model.Student">
SELECT * FROM students
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
ORDER BY student_id ASC
LIMIT #{offset}, #{pageSize}
</select>
<!-- 查询学生已选课程及成绩,含课程名和教师名 -->
<select id="selectStudentCourses" resultType="com.edu.model.CourseVO">
SELECT
c.course_id,
c.course_name,
c.credits,
t.name AS teacher_name,
e.semester,
e.score,
e.status
FROM enrollments e
JOIN courses c ON e.course_id = c.course_id
JOIN teachers t ON c.teacher_id = t.teacher_id
WHERE e.student_id = #{studentId}
AND e.semester = #{semester}
</select>
</mapper>
关键点:使用<where>和<if>标签实现动态SQL,这是MyBatis处理复杂查询的神器。CourseVO是一个视图对象,用于封装多表联查的结果,避免在JSP里处理复杂的对象嵌套。
五、 前端开发实战:JSP + JSTL + Bootstrap
这是很多后端开发者最容易忽视,也是最容易出问题的地方。咱们的目标是:页面简洁、响应式、交互友好。
5.1 页面布局框架
咱们用Bootstrap 5(兼容CDN引入,无需本地下载)作为CSS框架,搭配一个通用的头部和侧边栏模板。
common/header.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="${pageContext.request.contextPath}/">智慧校园</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="#">首页</a></li>
<li class="nav-item"><a class="nav-link" href="#">教务管理</a></li>
<li class="nav-item"><a class="nav-link" href="#">成绩查询</a></li>
</ul>
<ul class="navbar-nav">
<c:if test="${sessionScope.user != null}">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
${sessionScope.user.name} (${sessionScope.user.role == 'TEACHER' ? '教师' : '学生'})
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">个人中心</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="${pageContext.request.contextPath}/logout">退出登录</a></li>
</ul>
</li>
</c:if>
<c:if test="${sessionScope.user == null}">
<li class="nav-item"><a class="nav-link" href="${pageContext.request.contextPath}/login.jsp">登录</a></li>
</c:if>
</ul>
</div>
</div>
</nav>
注意:这里使用了sessionScope来动态显示登录状态和用户信息。这是JSP会话管理的经典用法。
5.2 核心功能页面:学生选课
view/courseSelection.jsp
”`jsp <%@ page contentType=“text/html;charset=UTF-8” language=“java” %> <%@ taglib prefix=“c” uri=”http://java.sun.com/jsp/jstl/core” %> <%@ taglib prefix=“fmt” uri=”http://java.sun.com/jsp/jstl/fmt” %> <!DOCTYPE html>
<meta charset="UTF-8">
<title>在线选课</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.course-card {
transition: transform 0.2s;
border-left: 4px solid #0d6efd;
}
.course-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.capacity-warning {
color: #dc3545;
font-weight: bold;
}
.capacity-ok {
color: #198754;
}
</style>
<%@ include file="common/header.jsp" %>
<div class="container mt-4">
<h2 class="mb-4">2024
