☰
学生信息管理系统课设实战:JSP+Servlet+MySQL完整拆解
2026/10/15 12:47:58 网站建设 项目流程

简介:基于Java Web的简单学生信息管理系统是一份面向Java Web初学者及课程设计场景的完整项目源码,采用jsp+servlet+layui+jquery技术实现,涵盖学生注册登记、教师登录管理、信息增删改查等核心功能,部署于jdk7+tomcat8+mysql+IntelliJ IDEA环境,非常适合作为实训作业、期末项目或课程设计参考。资源包共2个文件,主体为zip压缩包,内含项目工程文件及lib目录下所需jar包;另附一个htm说明文档,帮助读者快速了解部署流程与资源下载地址,整体大小仅2.13MB,轻量易用。已有6435人学习浏览。项目数据库文件通过百度网盘单独提供,jar包已内置,按readme指引即可导入IDEA运行;代码注释清晰,jsp+servlet分层结构简单直观,可帮助读者理解请求响应机制、数据库连接及基础增删改查实现,同时结合layui美化前端界面,方便在此基础上扩展功能或调整业务逻辑。

1. 简单学生信息管理系统:课程设计里最常见的JSP+Servlet项目,到底怎么落

如果你的课程设计题目恰好是“简单学生信息管理系统”,并且要求用JSP+Servlet来写,那这套资源值得你在动手前完整过一遍。它的核心组合是JSP做页面、Servlet做请求控制、MySQL存数据,整套代码不是教学片段,而是能直接跑在Tomcat上的可运行项目。我拆完之后的感受是:这个系统真正难的地方不在代码量,而在数据库设计、请求流转和部署环境三块。适合刚做完JavaWeb基础、想拿一套完整代码换皮或逐段理解的人,也适合答辩前想排查部署问题的人。读完后你至少能分清哪些是必须改的,哪些属于加分项。

2. 系统架构与数据库设计:五张表的边界与三层的分工

2.1 JSP/Servlet/JavaBean 三层职责边界

一个“简单学生信息管理系统”,如果控制不好分层,容易写成每个JSP页面里直接用JDBC查库,表面上省事,实际上每一处都要改,而且不好回答评委提问。常见做法是把系统拆成三层:JSP只负责接收数据并渲染,Servlet负责解析请求参数、调用业务方法、决定跳转,JavaBean承担模型和数据访问。这里的模型不是只指实体类,而是DAO和Service两类对象。课程设计阶段不要求引入Spring,所以这一层靠手工维护。

为什么还要选JSP+Servlet而不是Spring Boot?因为很多课程设计的评分标准是“让学生说清楚HTTP请求从浏览器到数据库再到页面经过哪些环节”。用Servlet能直接面对HttpServletRequest和HttpServletResponse,概念不经过框架包装,答起题来更直观。Spring Boot当然更快,但评委一旦追问“拦截器、过滤器、依赖注入”的原理,基础不牢就会露馅。所以从课程设计角度,JSP+Servlet不是过时,而是一个更容易被验证的教学过程。

请求流转也很简单:浏览器访问某个URL,web.xml或@WebServlet把URL映射到对应Servlet;Servlet调用DAO操作数据库,把结果封装成JavaBean列表,通过request.setAttribute传给JSP;JSP用JSTL或表达式语言循环渲染。这套链路理解透了,后面看每一段代码都不会是黑匣子。

2.2 数据库设计:五张表的结构与约束

这套资源库里数据库一共五张表,分别是用户表、班级表、学生表、课程表和成绩表。表名建议都带 t_ 前缀,避免与MySQL保留字冲突。用户表负责管理员登录,班级与学生是父子关系,学生与课程通过成绩表关联。这样的设计不是堆表,而是让你在答辩时能解释清楚“为什么要分多张表”——一张学生表里塞班级名、课程名、成绩会导致大量冗余,且删除学生时容易丢成绩。

下面是一份可以放进MySQL直接执行的DDL,建库用utf8mb4,排序规则用utf8mb4_unicode_ci。注意外键约束我每一行都写了,这样在批量删数据时会比无外键的项目安全一些;如果课程设计模板不允许外键,去掉FOREIGN KEY也能跑,只是逻辑上的父删子留容易造成脏数据。

CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_db; -- 管理员用户表,密码保存MD5摘要 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password CHAR(32) NOT NULL ) ENGINE=InnoDB COMMENT='管理员用户表'; -- 班级表 CREATE TABLE t_class ( id INT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL ) ENGINE=InnoDB COMMENT='班级表'; -- 学生表,学号唯一 CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(30) NOT NULL, gender CHAR(1) DEFAULT '男', birth_date DATE, phone VARCHAR(20), class_id INT NOT NULL, CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES t_class(id) ) ENGINE=InnoDB COMMENT='学生表'; -- 课程表 CREATE TABLE t_course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) DEFAULT 2.0 ) ENGINE=InnoDB COMMENT='课程表'; -- 成绩表,外键关联学生和课程 CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES t_student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES t_course(id) ) ENGINE=InnoDB COMMENT='成绩表';

这里有几个参数可以按自己需求调:student_no设为UNIQUE,是为了防止同一学号重复插入,这是删除和查重时的底线;gender字段用CHAR(1),长度足够,不用VARCHAR;score用DECIMAL(5,2),最大999.99,对课设足够;ID全部用INT自增,学生量不超过几十万都可以。如果你要扩展学生照片,建议加一个VARCHAR保存相对路径,不要让数据库存base64或二进制大文件,否则页面加载很慢、备份也很痛苦。这是我处理课设时很常用的一个边界。

2.3 数据库连接方式:Druid连接池与db.properties参数

连接池是这套资源里容易被忽略但必须搞懂的部分。很多课程设计模板还是用DriverManager每次请求现连数据库,代码少,但每次握手开销大,页面上连续操作几次就变慢。这里改成Druid连接池,配置集中在db.properties里,改数据库密码不用动Java代码。下面四个参数最重要。

# MySQL 8驱动类名 driverClassName=com.mysql.cj.jdbc.Driver # 连接地址,时区必须显式声明 url=jdbc:mysql://127.0.0.1:3306/student_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username=root password=123456 # 连接池初始连接数和最大连接数 initialSize=5 maxActive=20 maxWait=3000

driverClassName是MySQL 8以上必须的驱动类名;如果是MySQL 5.7,很多老项目写的还是com.mysql.jdbc.Driver,那就会报找不到类。URL里我加了serverTimezone=Asia/Shanghai,MySQL 8连接时不给时区直接报错。initialSize表示启动时创建的连接数,maxActive是最大连接数,课程设计场景20足够。这段配置变更是最常见的部署坑,后面避坑章节会详细说。

对应的JdbcUtils工具类要写好,不然每个DAO都要重复加载配置文件。完整写法如下:

public class JdbcUtils { private static DataSource dataSource; static { try { // 从classpath读取配置文件 Properties props = new Properties(); props.load(JdbcUtils.class.getClassLoader() .getResourceAsStream("db.properties")); // 交给Druid创建连接池 dataSource = DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

注意ClassLoader.getResourceAsStream读取的是classpath根目录下的文件,所以db.properties必须放在src目录或resources目录下,而不是WEB-INF里。DruidDataSourceFactory.createDataSource会读到配置文件里的每个键,键名不能改,改了会静默使用默认值。等排查时发现连接数不对,先回头看配置名是否写错。getConnection返回的是连接池里的连接,使用后要close回收,不是真的断开。

3. 用户登录与权限控制:从登录请求到Session校验

3.1 登录流程与MD5加密存储

这个系统的登录入口是login.jsp页面,提交后由LoginServlet处理。流程上,Servlet先读取username和password两个参数,再做一次简单非空校验,然后调用UserDao查询数据库中是否存在匹配记录。这里有一个必须做的细节:密码不要明文存。课程设计中很多同学为了省事,注册管理员直接insert明文,答辩演示时打开数据库给评委看,密码是123456,不太好看。更合理的做法是在SQL脚本里预置一条MD5密文记录。

MD5加密在工具类里封装一个静态方法即可。注意只做一次MD5加密,不加盐,足够应付课程设计;生产环境还要加盐或改成BCrypt,这里不展开。如果你准备答辩,被问到为什么要加密,答案就一句:数据库泄露时,明文密码会直接泄露;MD5加密后至少不能直接还原。但也要知道MD5本身可以被彩虹表反查,所以别把它吹成安全方案。真正要防守的是越权和注入,而不是加密算法本身。

public class Md5Util { public static String md5(String input) { try { // 计算MD5摘要 MessageDigest md = MessageDigest.getInstance("MD5"); byte[] result = md.digest(input.getBytes("UTF-8")); StringBuilder sb = new StringBuilder(); for (byte b : result) { // 每个字节转两位十六进制 sb.append(String.format("%02x", b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(e); } } }

这段代码关键点在MessageDigest.getInstance("MD5")拿到摘要对象,digest返回16字节数组,再用String.format("%02x")转成32位十六进制字符串。SQL脚本里预置用户数据时,直接写Md5Util.md5("admin123")得到的值,不要写原始密码。如果登录逻辑仍然查不到用户,先检查一次密码加密是否正确,最简单的方式是写一个临时main方法打印密文,和数据库里的值比对。

登录成功后,我一般建议把用户对象存进Session,而不是只存用户名。因为后续可能要显示当前操作者的信息,或者扩展角色字段。Session里保存对象还有一个好处:Filter判断时可以直接取对象的属性,而不是只判断一个裸字符串。这个习惯能让后面的权限控制好扩展很多。

3.2 用Filter统一做Session校验

登录接口之外的所有页面都应该被保护,否则用户在浏览器地址栏直接输入student_list.jsp就能跳过登录。课程设计里常见做法是在每个Servlet里都写一段“session是否为空”的判断,代码重复且容易漏。更好的做法是用Filter统一拦截,这也是答辩时的高频问题。

资源里建议放一个AuthFilter,把所有请求拦下来,判断当前会话中是否有登录标记。下面是doFilter方法的核心逻辑:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; // 不主动创建Session,防止无效会话占用内存 HttpSession session = request.getSession(false); String uri = request.getRequestURI(); String contextPath = request.getContextPath(); // 放行登录页、登录接口和静态资源 if (uri.endsWith("/login.jsp") || uri.endsWith("/login") || uri.endsWith(".css") || uri.endsWith(".js") || uri.endsWith(".png")) { chain.doFilter(request, response); return; } // 已登录则放行,否则重定向到登录页 if (session != null && session.getAttribute("loginUser") != null) { chain.doFilter(request, response); } else { response.sendRedirect(contextPath + "/login.jsp"); } }

这里用request.getSession(false)而不是getSession(),区别在于getSession(false)在没有会话时返回null,不会主动创建Session。uri.endsWith判断是排除登录页、登录接口和静态资源,如果你用的是web.xml配置拦截路径,这里的排除逻辑就是防拦截的重中之重。把Filter放在web.xml里用/拦截,或者用@WebFilter("/")都可以,但要确保filter-mapping在servlet-mapping之前声明,这不是玄学,是部署描述符的解析顺序。

注意,登录成功后要向session中设置一个属性,比如session.setAttribute("loginUser", user),属性名要和Filter里的判断一致。否则明明登录成功了,Filter还是把你弹回登录页,这是很多同学踩过的坑。另外,登录页本身不要做这个拦截,否则会出现重定向死循环,浏览器报“页面跳转次数过多”的错。

3.3 退出与Session销毁

退出功能的代码量很少,但它能强化你对Session生命周期的理解。简单的退出Servlet里调用invalidate()方法,把整个Session作废,再重定向到登录页。很多人写成只removeAttribute("loginUser"),那只会移除当前登录属性,Session对象本身还在,浏览器关闭前如果重新登录,相关上下文可能残留。课程设计阶段用invalidate更彻底。

@WebServlet("/logout") public class LogoutServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 作废整个会话 request.getSession().invalidate(); response.sendRedirect(request.getContextPath() + "/login.jsp"); } }

退出建议用GET请求,因为它是幂等操作,不产生副作用,直接放在页面导航链接里即可。而删除操作不同,后面会讲到为什么不能把删除也做成GET。另外,退出后你想防止浏览器后退看到缓存页面,还需要在受保护页面设置no-cache响应头,或者在Filter里设置response.setHeader("Cache-Control", "no-store")。这个不算必须,但答辩时主动提出来是加分项。

4. 学生信息增删改查与分页:Servlet路由与参数传递

4.1 列表分页查询:limit参数与PageBean

列表页是整个系统体量最大的部分,难点集中在分页和模糊查询。先看分页。数据库层用MySQL的limit可以很直接地控制每页记录数和偏移量,Servlet里接收两个参数:currentPage默认1,pageSize默认5或10。要传给DAO两个整数,DAO执行“select * from student where 条件 limit ?, ?”,参数依次是偏移量和每页条数。这里要注意偏移量的计算是(currentPage - 1)* pageSize,很多第一次写的同学直接把页码当成偏移量传进去,第一页没问题,第二页就少了一半数据。

DAO层可以封装一个PageBean,成员包括list、totalCount、totalPage、currentPage、pageSize。查询时先执行count统计,再执行limit查询。两步之间如果有新增数据,会出现总数和列表差一条,课程设计不要求事务,无所谓。下面是StudentDao里分页查询的写法:

public List<Student> findPage(int currentPage, int pageSize, String keyword) throws SQLException { // 分页查询,offset由页码计算 String sql = "SELECT * FROM t_student st JOIN t_class cl ON st.class_id = cl.id WHERE st.name LIKE ? OR st.student_no LIKE ? LIMIT ?, ?"; int offset = (currentPage - 1) * pageSize; try (Connection conn = JdbcUtils.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + keyword + "%"); ps.setString(2, "%" + keyword + "%"); ps.setInt(3, offset); ps.setInt(4, pageSize); try (ResultSet rs = ps.executeQuery()) { List<Student> list = new ArrayList<>(); while (rs.next()) { Student stu = new Student(); stu.setId(rs.getInt("id")); stu.setStudentNo(rs.getString("student_no")); list.add(stu); } return list; } } }

这段代码用了join查询,班级名一并展示。注意LIKE条件里的百分号是拼在参数里的,而不是拼在SQL字符串里,这是为了保持PreparedStatement的占位符完整。try-with-resources会自动关闭Connection、PreparedStatement、ResultSet,省去finally里一堆判断。Connection从连接池拿回后,close只是归还连接,不会真的断开,所以在这个方法内关闭没有任何问题。

页面底部的页码栏需要计算总页数。totalPage = (int) Math.ceil(totalCount * 1.0 / pageSize); 在JSP里可以用JSTL的<c:if>控制上一页、下一页的显示。这里最容易翻车的是JSTL标签没引入依赖,页面直接报500。如果你不想引JSTL,就用脚本片段,但答辩观感稍差。资源里如果用了JSTL,记得把jstl相关jar放进WEB-INF/lib。

4.2 新增与修改表单:中文乱码处理与SQL注入

新增和修改放到一起讲,因为它们的表单结构和后端处理基本一样。资源里的表单提交到StudentServlet,Servlet通过一个隐藏字段id判断是新增还是修改:id为空就是添加,id不为空就是编辑。这样只需要一个doPost方法,避免复制代码。

doPost里第一行就应该是request.setCharacterEncoding("UTF-8"),并且必须在调用getParameter之前。如果放在getParameter之后,POST请求体已经按ISO-8859-1解析过了,中文参数再转也容易丢字。同时response要设置ContentType为text/html;charset=UTF-8。JSP页面顶部的pageEncoding也要保持一致,三层编码不统一,中文在某个环节变成问号,就是血泪经验。

写入数据库的部分不要用字符串拼接,而是用PreparedStatement。对比一下:拼接写法是“INSERT INTO t_student VALUES (" + id + ",'" + name + "')”,一旦name中包含单引号,要么SQL报错,要么被注入。PreparedStatement用setString绑定参数,MySQL驱动会处理特殊字符。下面给出通用的插入方法片段:

String sql = "INSERT INTO t_student(student_no, name, gender, birth_date, phone, class_id) VALUES(?, ?, ?, ?, ?, ?)"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, studentNo); ps.setString(2, name); ps.setString(3, gender); // 日期从java.util.Date转成java.sql.Date ps.setDate(4, new java.sql.Date(birthDate.getTime())); ps.setString(5, phone); ps.setInt(6, classId); ps.executeUpdate();

这里要注意日期参数:页面传来的birthDate是String,需要先用SimpleDateFormat解析成java.util.Date,再转成java.sql.Date,否则PreparedStatement没法直接setDate。如果直接setString到一个DATE字段,MySQL在严格模式下会报日期格式错误。我第一次写的时候就把日期字符串直接塞进SQL,后来发现不同MySQL版本有的不报错有的直接拒绝,依赖数据库放宽校验不是好路子。

修改操作和插入操作唯一区别是把VALUES改成SET,然后条件加WHERE id=?。dao每执行成功后,Servlet要重定向到列表页而不是forward到列表页,具体原因避坑章节会展开。

4.3 删除操作:POST提交与权限校验

删除是危险操作,不能用超链接直接访问deleteServlet?id=1这种方式。原因是搜索引擎爬虫、浏览器预加载以及用户误触都可能触发GET请求,一旦没有确认机制,一行数据就没了。课程设计虽然不涉及线上环境,但答辩老师会看你的请求设计,GET删除很容易减分。

正确做法是把删除按钮放在一个form表单里,method设为POST,提交到deleteServlet。页面上每个学生行都写一个form,会比较啰嗦,但安全。下面是在JSP中使用表单删除的示意:

<form action="${pageContext.request.contextPath}/student/delete" method="post"> <input type="hidden" name="id" value="${stu.id}"> <button type="submit" onclick="return confirm('确认删除该学生吗?')">删除</button> </form>

这个JSP片段里用了EL表达式动态拼接项目上下文路径,避免部署在不同路径下时404。onclick返回false时,表单不会提交。后端doPost里,先判断Session中的loginUser是否为空,为空就直接拒绝,然后才执行删除。删除前还可以先查一遍学生是否存在,避免删除一个已经被删过的记录时报SQL异常。

给表设计外键后,删除学生时如果成绩表有外键引用,MySQL会阻止删除,并在控制台报外键约束失败。这时要么先删成绩再删学生,要么在DELETE语句发出前,在Service层统一处理。我一般会在Service里先执行“DELETE FROM t_score WHERE student_id=?”再执行“DELETE FROM t_student WHERE id=?”,两次操作包在一个事务里,避免一个成功一个失败导致中间态。

5. 常见问题排查:部署、乱码、连接失败的五个典型坑

5.1 页面中文乱码:JSP、请求、响应三层不一致

现象:登录后学生名单页面所有中文都显示成问号,或者添加“王小明”后列表里变成“????”。

原因:JSP文件本身没按UTF-8保存,页面pageEncoding没设置,Servlet读取POST参数时没指定字符集,response输出时又用了默认编码。三处只要有一处不一致,中文就可能丢。Tomcat 7及更早版本对URL参数默认用ISO-8859-1,也是乱码来源之一。

解决:先把所有JSP页面头部的contentType改为“text/html; charset=UTF-8”,pageEncoding也改成UTF-8。登录和管理端所有Servlet的doGet/doPost第一行加request.setCharacterEncoding("UTF-8"),response也用setCharacterEncoding("UTF-8")。最后,如果还在用Tomcat 7,在server.xml的Connector节点增加URIEncoding="UTF-8";Tomcat 8.5以上默认就是UTF-8,不用再加,加了也没问题。按这个顺序排查,乱码基本一轮解决。

5.2 数据库连接失败:驱动类、时区、密码校验规则都可能是元凶

现象:启动Tomcat后访问登录页正常,登录一提交就报java.sql.SQLException或ClassNotFoundException,控制台堆栈里有Communications link failure。

原因:最常见的是mysql-connector-java.jar没有放到WEB-INF/lib里,或者放的是一个很老的5.x驱动去连MySQL 8。第二常见的是URL里没有serverTimezone,MySQL 8连接会拒绝。第三是MySQL用户的密码加密方式是caching_sha2_password,老版本驱动无法完成认证。

解决:确认JDBC URL里的driverClassName是com.mysql.cj.jdbc.Driver,URL里带serverTimezone=Asia/Shanghai。把数据库驱动jar放到WEB-INF/lib目录并重启Tomcat。如果还连不上,在MySQL命令行执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';,但要注意这个方案在MySQL 8里是兼容选项,新版MySQL 9可能移除了,所以更建议直接升级驱动jar。密码里尽量不要带@、#这类字符,因为properties文件解析URL时会把#当注释截断,这是很多同学忽略的坑。

5.3 404/405错误:Servlet路径映射不一致

现象:表单提交后浏览器地址变成http://localhost:8080/student/delete,页面返回404,或者点击删除返回405 Method Not Allowed。

原因:@WebServlet("/student/delete")里的路径和表单action写的路径不一致,或者大小写不一致。405则说明Servlet类没有重写对应的doGet或doPost方法,表单method是post,但Servlet只写了doGet。

解决:先在浏览器直接访问Servlet路径,如果能访问说明不是路由问题。资源里的代码默认用注解方式,注解值必须以/开头,表单action最好用“${pageContext.request.contextPath}/student/delete”动态加上项目名,避免换部署环境后项目上下文名称变化导致404。如果要兼容课设模板里的web.xml配置,注意web.xml里servlet-mapping的url-pattern是“/student/delete”,不能写“student/delete”,漏掉前导斜杠会直接部署报错。

5.4 数据重复提交:刷新页面把同一条学生信息插入两次

现象:新增一个学生后,按F5刷新列表页,浏览器提示“要重新发送表单吗?”,点是之后数据库中多了一条相同学号的学生。

原因:Servlet在插入成功后,使用request.getRequestDispatcher("list.jsp").forward()把请求转发给列表页面,此时浏览器的地址栏还是student/add,刷新操作会重复提交上一次的POST请求。GET刷新不会出问题,POST刷新就中招。

解决:使用Post/Redirect/Get模式,插入成功后立刻response.sendRedirect(request.getContextPath() + "/student/list"),让浏览器重新发起GET请求获取列表。这个重定向解决了重复提交,但没解决兜底问题。更保险的做法是给student_no建唯一索引,重复学号插入时数据库会直接报错。我自己的习惯是两层都做,页面层靠重定向,数据层靠唯一约束,这两条一起才能保证不翻车。

5.5 部署后看不到更新:Tomcat没有清理旧class

现象:修改了Servlet代码,重新发布后页面访问还是老功能,甚至控制台没有任何报错。

原因:IDEA或Eclipse里增量部署时,Tomcat的work目录没有完全清理,旧的Servlet class还在被使用。有时浏览器缓存也会让你误以为没更新。另外一个隐蔽情况是项目里有多个同名的Servlet class,classpath顺序导致加载了旧包里的类。

解决:先把浏览器缓存强制刷新一次,快捷键Ctrl+F5。如果无效,停掉Tomcat,在IDEA里执行Build -> Rebuild Project,再清理Tomcat的work目录。Eclipse则右键项目Clean Project,然后重新Publish。如果项目是Maven结构,先把target目录删掉再重新package。部署这类行为看起来像玄学,本质是Tomcat加载类时优先读取work目录下的方法缓存,清了缓存基本就好了。

6. 最后补两点:把项目改成Maven工程,并写一份能答辩的README

6.1 迁移到Maven:pom.xml依赖清单

你拿到的资源如果是传统Eclipse工程,lib下带着一堆jar,用起来没问题,但后续想换成Maven管理会比较费劲。如果决定迁移,只要新建一个war类型工程,把原来的src和webapp目录拷过去,再写一个最小的pom.xml。下面这个依赖块覆盖了Servlet、JSTL、MySQL驱动和Druid连接池,都是这套系统运行时需要的:

<!-- Servlet和JSP依赖由容器提供,编译期生效 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.23</version> </dependency>

注意servlet-api必须用provided scope,表示编译时提供,打包时由Tomcat容器提供,不然部署后会遇到jar包冲突。如果项目已经用注解写了@WebServlet,全Java代码无需配置web.xml也能跑,但Filter如果也想用注解,需要对应Servlet 3.0以上的Tomcat版本,建议直接在web.xml里配置,兼容性更好。

6.2 答辩前必须验证的三件事

第一件事:用一个干净的MySQL实例执行SQL脚本,不要拿你开发时已经跑过的库去演示。干净库能暴露掉缺外键、字段名不一致等隐藏问题。第二件事:把db.properties里的密码改成演示机器上MySQL的真实密码,然后重启Tomcat,登录、增删改查、退出完整走一遍。第三件事:换一个浏览器打开项目,确认没有依赖浏览器插件才能显示的内容。很多系统在本地开发机上没问题,换到教室电脑上就“不认路”,多半是因为localhost没改成127.0.0.1,或者项目名错了。

我自己的习惯是每次答辩前一天都强制自己删掉Tomcat的work目录和target目录,然后从零启动一遍,整个过程控制在十分钟内。这一遍看着简单,能避免绝大多数现场翻车。希望这次拆解能帮到你,让你在课程设计这条路上少走几段弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询