
简介基于Java Web与SSM架构的校园驿站管理系统是面向Java毕业设计、课程设计及期末大作业的完整项目方案。系统围绕快递驿站业务实现管理员、员工、用户三类角色的协同管理涵盖快递仓库、待发货、已收快递、物流跟踪、留言及公告等核心模块代码含详细注释并在JDK1.8、MySQL5.7环境下通过测试配合部署说明可快速运行。资源包共2000个文件大小约43.85MB主要包含Java源码、JSP页面、JS脚本、CSS样式、XML配置、依赖JAR包、SQL数据库脚本、项目文档及PPT等其中111个Java类、82个JSP页面和466个JS文件构成业务逻辑与前端交互主体4个SQL文件用于初始化数据库万字报告文档则完整阐述了系统设计与实现过程。包内结构清晰部署说明详细适合需要参考完整项目或进行二次开发的学习者。已有119人浏览学习下载后可直接用于毕业设计答辩或课程设计提交。1. 基于 Java Web 的校园驿站管理系统毕业设计到底在做什么每到毕业季总有大三升大四的同学问我Java Web 课程设计做什么题目好我的答案一直是“校园驿站管理系统”——它既有真实的业务场景又能把 Java 基础、Servlet、JSP、JDBC、前端交互这些课程里学过的东西全部串起来而且工作量正好卡在一个学期能完成、答辩能讲透的区间。所谓“校园驿站”就是学校里代收快递的小站点快递到了录入系统学生凭取件码拿走管理员能统计库存。你做的不是一个玩具而是一个能运行、能演示、能写进简历的完整管理系统。这套系统天然包含三类角色学生、驿站管理员、系统管理员。学生端要能查快递、取件、留评价管理员端要能录入快递、批量导入、管理货架系统管理员管用户和权限。从技术上讲它要覆盖注册登录、会话管理、CRUD、分页搜索、文件上传这几个 Java Web 必考点。更关键的是它适合用“传统”的 JSP Servlet 来实现而不是一上来就上 Spring Boot——毕业设计的评委更看重你能不能讲清楚请求从浏览器到数据库再返回的整个过程而不是你背了几个注解。接下来我们就把这套系统从零拆开讲清楚每一步怎么做、参数怎么设、坑在哪里。2. 为什么是 Java Web 而不是 Spring Boot选型逻辑与项目骨架2.1 毕业设计场景下 Java Web 与 Spring Boot 的真实取舍很多同学一上来就想用 Spring Boot理由很直接开发快、不用配 Tomcat、简历好看。但“基于 Java Web”这个标题本身已经限定了方向它默认你用的是 Servlet、JSP、JDBC 这一套“上古”技术栈。你可能会觉得这过时了但从毕业设计答辩的角度看这反而是优势。用传统 Java Web你所有的代码都是自己写的Servlet 里处理请求参数、JSP 里用 JSTL 循环数据、JDBC 里关闭连接每一步都能讲清楚原理。用 Spring Boot 的话很多环节被框架“黑匣子”掉了评委问“你这个事务是怎么生效的”、“拦截器是怎么注册的”你答不上来反而扣分。所以选型的第一原则不是“最新最热”而是“你能讲透”。如果你想在简历上同时提 Spring Boot可以额外加一个简单模块但主项目还是保持 Java Web 原教旨主义这样既有深度又有说服力。另外从开发效率上说Java Web 并没有你想象的那么慢。现在有 Maven 帮忙管依赖有 Tomcat 插件一键启动前端模板可以直接用现成的 Bootstrap 或者 Layui真正需要手写的只有业务代码。下面我们用一个最小可运行的工程来打底。2.2 用 Maven 搭出可运行的最小工程pom.xml 与目录结构不管你用 Eclipse 还是 IDEA请务必用 Maven 创建工程不要手工去拷 jar 包。Maven 能解决依赖版本冲突也让你的部署说明更接近真实企业项目。先看最小可用的 pom.xmlproject xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.campus/groupId artifactIdcampus-station/artifactId version1.0.0/version packagingwar/packaging properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies !-- Servlet 与 JSP 依赖容器已提供scope 用 provided -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependency dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.1/version scopeprovided/scope /dependency !-- JSTL -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- MySQL 驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency /dependencies build finalNamecampus-station/finalName plugins plugin groupIdorg.eclipse.jetty/groupId artifactIdjetty-maven-plugin/artifactId version9.4.31.v20200723/version /plugin /plugins /build /project这段配置里有两个关键点。第一Servlet 和 JSP 的依赖 scope 必须是provided因为 Tomcat 容器自己带了一份如果你打包时把这两个 jar 塞进 WEB-INF/lib启动时可能直接冲突报错。第二Jetty 插件是给你开发时用的运行mvn jetty:run就能在浏览器里访问不用每次手动启动外部 Tomcat。当然最终毕业设计答辩用的还是打包成 war 放进 Tomcat所以packaging必须是 war。创建完 pom 之后目录结构要严格遵循 Maven 约定campus-station ├── pom.xml ├── src │ └── main │ ├── java │ │ └── com/campus/station │ │ ├── servlet // 控制层 │ │ ├── dao // 数据访问层 │ │ ├── entity // 实体类 │ │ ├── service // 业务逻辑层可选 │ │ └── util // 工具类 │ ├── resources │ │ ├── db.properties // 数据库配置 │ │ └── c3p0-config.xml // 连接池配置 │ └── webapp │ ├── WEB-INF │ │ ├── web.xml │ │ └── jsp // 受保护的 JSP │ ├── static // CSS、JS、图片 │ └── index.jsp注意web.xml必须放在WEB-INF下它负责配置 Servlet 映射、过滤器、欢迎页。很多新手会把 JSP 直接放在 webapp 根目录虽然能访问但不安全——比如WEB-INF下的 JSP 不能被浏览器直接 URL 访问必须经过 Servlet 转发这样你可以在过滤器里做权限校验。习惯上把 JSP 丢进WEB-INF/jsp是更稳妥的做法。2.3 三层架构与 MVC 在驿站业务里的落点校园驿站系统的业务逻辑并不复杂但你要在报告文档里把架构讲清楚。常见的做法是严格分层Servlet控制层只负责接收请求、调用 Service、跳转 JSPService业务层处理业务规则比如取件时校验取件码是否正确、快递状态是否已取DAO数据访问层只做增删改查。实体类对应数据库表工具类负责数据库连接、字符串处理。以“学生取件”这个动作为例请求流向是这样的浏览器 POST 请求/takePackage参数是trackingNo和pickupCode。过滤器先检查用户是否登录没登录就跳去登录页。TakePackageServlet拿到参数调用PackageService.takePackage(trackingNo, pickupCode)。Service 里先按 trackingNo 查快递再比对取件码一致才更新状态并返回结果。Servlet 根据 Service 的返回值决定转发到前端提示成功还是失败。这样的好处是每一层都能独立测试你写万字报告的时候也能多凑一章架构设计。更重要的是如果后来你想把它改造成 Spring Boot 版本Service 层和 DAO 层几乎可以原封不动搬过去只把 Servlet 换成 Controller 注解就够了。所以现在多花点时间把 DAO 写规范后面你会感谢自己。3. 把驿站业务拆成表结构和核心模块从需求到可写代码3.1 核心表设计用户、快递、取件码、站点毕业设计的数据库表不需要多五到六张足矣但每张表都要经得起“为什么这么设计”的追问。我一般会建这几张user用户表包含 id、username、password、role1 学生、2 管理员、3 系统管理员、student_no学号、phone。station驿站站点表包含 id、name、location、manager_id。package快递表核心字段是 tracking_no运单号、student_id收件人、station_id、status、pickup_code、arrive_time、take_time。pickup_record取件记录表记录哪个用户在哪一站、什么时候取走了哪个包裹方便审计。notice公告表管理员发通知用的。设计快递表是重点。pickup_code是终端随机生成的四到六位数字或字母组合用一条 SQL 生成后插入同时要保证同一站点下不重复。status建议用 int 而不是字符串0 表示在库1 表示已取2 表示退回这样查询更快后续扩展也方便。下面给出建表 SQL 的核心片段CREATE TABLE package ( id int NOT NULL AUTO_INCREMENT, tracking_no varchar(32) NOT NULL COMMENT 运单号业务唯一键, student_id int NOT NULL COMMENT 收件人用户id, station_id int NOT NULL COMMENT 所属驿站id, status tinyint NOT NULL DEFAULT 0 COMMENT 0在库 1已取 2退回, pickup_code varchar(8) NOT NULL COMMENT 取件码, arrive_time datetime NOT NULL COMMENT 入库时间, take_time datetime DEFAULT NULL COMMENT 取件时间, PRIMARY KEY (id), UNIQUE KEY uk_tracking_no (tracking_no), KEY idx_student_status (student_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT快递包裹表;这里有几个设计细节值得在报告里写一笔。tracking_no加了唯一索引模拟真实快递的运单号唯一性student_id和status建了联合索引因为最频繁的查询是“某个学生有哪些待取的包裹”取件码没有设为全局唯一因为它的作用域只在单个站点内全局唯一会导致生成麻烦没那个必要。3.2 用 JDBC 封装 DAO 层查询、分页与事务DAO 层是 Java Web 项目里最容易写脏的地方。很多同学的代码里到处都是Class.forName(com.mysql.jdbc.Driver)然后每次查询都打开连接、用完不关性能差不说数据库连接一会儿就爆了。常见做法是用一个数据库连接池比如 C3P0 或 Druid把连接管理统一收口。先看一个用 C3P0 封装的数据库工具类package com.campus.station.util; import com.mchange.v2.c3p0.ComboPooledDataSource; import java.sql.Connection; import java.sql.SQLException; public class DBUtil { // 静态成员整个应用只初始化一次 private static ComboPooledDataSource ds new ComboPooledDataSource(); public static Connection getConnection() throws SQLException { return ds.getConnection(); } public static void close(Connection conn, java.sql.Statement stmt, java.sql.ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } }ComboPooledDataSource会在启动时读取 classpath 下的c3p0-config.xml自动加载 MySQL 驱动并把连接池初始化好。注意每次用完之后必须把连接关掉这里说的“关掉”其实是归还给连接池不是真的断开所以不用担心性能。有了连接工具写一个快递 DAO 就顺理成章了。以“分页查询待取包裹”为例public ListPackage findPendingByStudent(int studentId, int page, int pageSize) { ListPackage list new ArrayList(); String sql SELECT * FROM package WHERE student_id ? AND status 0 ORDER BY arrive_time DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, studentId); ps.setInt(2, (page - 1) * pageSize); // 计算 offset ps.setInt(3, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Package p new Package(); p.setId(rs.getInt(id)); p.setTrackingNo(rs.getString(tracking_no)); p.setPickupCode(rs.getString(pickup_code)); p.setStatus(rs.getInt(status)); list.add(p); } } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码用了 Java 7 的 try-with-resources 语法Connection、PreparedStatement、ResultSet都会自动关闭不用在 finally 里手工写。分页用的是 MySQL 的LIMIT ?, ?第一问是偏移量第二问是每页条数注意偏移量要从 0 开始计算所以(page-1)*pageSize。事务也很重要比如“批量导入快递”这个功能解析 Excel 时可能有些行失败不能让一部分插进去、另一部分没插。正确的做法是让业务层控制事务边界public boolean batchImport(ListPackage packages) { Connection conn DBUtil.getConnection(); try { conn.setAutoCommit(false); // 开启事务 for (Package p : packages) { insert(conn, p); } conn.commit(); // 全部成功才提交 return true; } catch (SQLException e) { try { conn.rollback(); } catch (SQLException ignored) {} return false; } finally { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException ignored) {} } }注意这里不能直接调用原来的 DAO 方法因为那个方法内部会再拿一个连接导致事务断裂。常见做法是写一个接受Connection参数的私有方法或者把事务放到 Service 层统一管理。这也是答辩时评委最爱问的点“你的事务是加在哪一层的”3.3 登录拦截与角色权限过滤器实现的三种写法权限控制是校园驿站系统里不能跳过的功能。学生只能看到自己的包裹管理员只能管理自己站点的包裹系统管理员才能改用户角色。用 Filter过滤器做统一拦截是最传统也最适合答辩的做法。先看一个最基础的登录过滤器package com.campus.station.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; WebFilter(urlPatterns /*) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); String uri request.getRequestURI(); // 放行登录、注册、静态资源、首页 if (uri.endsWith(/login) || uri.endsWith(/register) || uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.png) || uri.endsWith(.jpg)) { chain.doFilter(req, resp); return; } if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }注意几个细节。request.getSession(false)表示如果当前没有 session 就返回 null而不会强制创建这样能减少无效 session 的生成。放行条件里要把静态资源和登录接口排除掉否则连登录页的 CSS 都加载不出来你会以为前端坏了。对于更细粒度的角色权限有两种常见做法一种是在 Filter 里检查 session 里用户对象的 role然后和请求路径匹配比如/admin/开头的 URL 必须 role 2 或 3另一种是在 Servlet 里每个请求方法前自己判断。我倾向把管理员相关的 Servlet 单独放在/admin路径下用一个AdminFilter做二次过滤WebFilter(urlPatterns /admin/*) public class AdminFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpSession session ((HttpServletRequest) req).getSession(false); Object user session null ? null : session.getAttribute(user); if (user null) { ((HttpServletResponse) resp).sendRedirect(((HttpServletRequest) req).getContextPath() /login.jsp); return; } // 假设 user 对象有 getRole() 方法管理员角色为 2 或 3 if (!admin.equals(((User) user).getRole())) { resp.getWriter().write(无权访问); return; } chain.doFilter(req, resp); } }第三种写法是在 web.xml 里配置filter-mapping与注解方式效果一样但把映射关系集中到配置文件里更 “Java Web” 原教旨。写作时建议用注解因为代码量少报告里也能清楚地展示注解用法。注意过滤器顺序登录过滤器要放在最外层角色过滤器放在内层这样先验证有没有登录再验证有没有权限不会出现“未登录却能调管理员接口”的漏洞。4. 前端页面与后端接口的联通JSP Servlet 的请求流转4.1 JSP 页面如何拿到数据并回显JSP 虽然老但它是 Java Web 毕业设计的主旋律而且能让你把列表页、详情页、表单回显的逻辑看得很清楚。以快递列表为例Servlet 查询完数据后用请求转发把数据塞进 request 域JSP 再用 JSTL 循环输出。Servlet 端代码WebServlet(/student/packages) public class StudentPackageListServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { User user (User) req.getSession().getAttribute(user); int page Integer.parseInt(req.getParameter(page) null ? 1 : req.getParameter(page)); int pageSize 10; PackageService service new PackageService(); ListPackage packages service.findPendingByStudent(user.getId(), page, pageSize); req.setAttribute(packages, packages); req.setAttribute(page, page); req.getRequestDispatcher(/WEB-INF/jsp/package/list.jsp).forward(req, resp); } }这里有个新手常犯的错用重定向sendRedirect传数据结果 request 域里的数据全丢了。转发是服务端内部跳转重定向是浏览器重新发一次请求所以要在转发后展示的数据必须存 request 或 session不能用重定向。JSP 页面里拿数据% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title我的待取包裹/title /head body table border1 tr th运单号/th th取件码/th th到达时间/th th操作/th /tr c:forEach items${packages} varp tr td${p.trackingNo}/td td${p.pickupCode}/td td${p.arriveTime}/td tda hreftake?trackingNo${p.trackingNo}取件/a/td /tr /c:forEach /table /body /html${p.trackingNo}会调用Package实体类的getTrackingNo()方法所以实体类属性名必须和 getter 的命名对应上。另外 JSP 第一行的pageEncodingUTF-8不能省否则中文内容在浏览器里就是一片乱码。4.2 Servlet 接收参数与 JSON 输出现在很多页面开始用 Ajax 和 JSON 交互Servlet 端同样要支持。比如“取件码校验”这个动作我们希望不刷新页面就提示用户操作成功或失败这时候就返回 JSON。WebServlet(/api/checkPickupCode) public class CheckPickupCodeServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String trackingNo req.getParameter(trackingNo); String pickupCode req.getParameter(pickupCode); resp.setContentType(application/json;charsetUTF-8); PrintWriter out resp.getWriter(); PackageService service new PackageService(); boolean ok service.verifyPickupCode(trackingNo, pickupCode); // 手动拼接 JSON避免引入 JSON 库 out.print({\success\: ok }); out.flush(); } }这里手动拼 JSON 只适合两三个字段如果返回的数据结构复杂建议用 Gson 或 Fastjson。resp.setContentType(application/json;charsetUTF-8)一定要写charsetUTF-8否则前端拿到的中文就是乱码。前端 Ajax 请求时如果你的页面是用 JSP 渲染的注意contextPath要用%request.getContextPath()%拼在 URL 前面否则部署到非根路径时会 404。4.3 文件上传与图片展示的落地细节校园驿站系统里有一个常见功能用户上传快递凭证或者头像、管理员上传公告封面。Java Web 处理文件上传有三个时代commons-fileupload、Servlet 3.0 自带的上传 API、以及前后端分离。毕业设计建议用 Servlet 3.0 自带的MultipartConfig省去引第三方 jar 的麻烦。WebServlet(/upload) MultipartConfig(maxFileSize 5 * 1024 * 1024, maxRequestSize 20 * 1024 * 1024) public class UploadServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Part part req.getPart(file); String fileName Paths.get(part.getSubmittedFileName()).getFileName().toString(); String suffix fileName.substring(fileName.lastIndexOf(.)); // 重命名防止中文或路径穿越问题 String newName System.currentTimeMillis() suffix; String uploadPath getServletContext().getRealPath(/uploads); File dir new File(uploadPath); if (!dir.exists()) dir.mkdirs(); part.write(uploadPath File.separator newName); resp.getWriter().write(/uploads/ newName); } }这里有两个必须处理的坑。第一part.getSubmittedFileName()在老版本 Tomcat 上可能返回 null建议加非空判断第二文件名不要直接用于保存路径不然有人传../../xxx.jpg就能把文件写到目录外。第二个坑也是答辩安全类问题的高频考点你写报告时把这段代码的注释写清楚能明显加分。图片展示相对简单上传后把相对路径存到数据库JSP 里直接用img src${package.imageUrl}即可。需要注意的是 Tomcat 的运行目录和项目源码目录是两回事getRealPath返回的是 webapp 部署后的路径重新部署时上传文件可能被清空如果想要持久化应该把文件存到服务器固定目录并配置虚拟路径映射。5. 避坑Java Web 项目从开发到部署的常见问题5.1 现象Tomcat 启动后访问 404 / 500404 有两种情况。一种是项目本身没部署上去 Tomcat 的webapps目录看看有没有对应的 war 解压目录另一种是 URL 写错比如忘记加项目上下文路径。用 IDEA 和 Tomcat 插件开发时默认 context 是项目名访问地址是http://localhost:8080/你的项目名/xxx。500 则多半是数据库连接失败、类找不到或者 JSP 编译错误看 Tomcat 的logs/catalina.out最后几十行基本能定位。解决如果是 404先把地址改成http://localhost:8080/项目名/index.jsp试。如果 500翻日志看有没有ClassNotFoundException或者SQLException。最常见的ClassNotFoundException是 MySQL 驱动缺失把 jar 放到 Tomcat 的lib目录而不是你的WEB-INF/lib。5.2 现象JSP 里中文乱码乱码有三个来源请求参数乱码、响应输出乱码、数据库存储乱码。请求参数乱码在 Tomcat 8 之前是个大坑8 以后默认 GET 请求用 UTF-8 解码但 POST 请求还是要你在doPost开头写request.setCharacterEncoding(UTF-8)。响应乱码要同时设置 JSP 文件的pageEncoding和contentType。数据库乱码则要确认建表时字符集是不是utf8mb4以及在连接 URL 上加了characterEncodingutf8。解决在 Servlet 里统一加一个 EncodingFilter对所有请求和响应设置 UTF-8。注意要在读取任何参数之前设置request.setCharacterEncoding否则对已解码的请求无效。平时我写代码会先确认数据库连接 URL 里有useUnicodetruecharacterEncodingutf8没有就先补上大部分乱码问题都能在这一步解决。5.3 现象JDBC 驱动找不到或数据库连不上项目在本地好好的一部署到别的机器就连不上数据库。常见原因有MySQL 服务没启动、防火墙没放行 3306 端口、驱动版本和数据库版本不匹配、连接 URL 写了localhost但目标机器的数据库在另一台服务器上。连接池配置文件的用户名密码写错也会报Access denied但控制台会打印明显的Access denied for user rootlocalhost。解决先用命令行mysql -u root -p在服务器本地验证数据库能连再检查 URL 里的 IP 是否允许远程访问。MySQL 默认只监听本地需要改配置文件里的bind-address0.0.0.0同时给用户授权root%。授权命令GRANT ALL PRIVILEGES ON *.* TO root% IDENTIFIED BY 密码; FLUSH PRIVILEGES;。注意别在生产环境这么干毕业设计环境这样能省掉很多折腾。5.4 现象部署后 CSS/JS 加载不出来JSP 页面引用的静态资源路径写成了相对路径比如css/style.css当 URL 从/index.jsp变成/student/packages时相对路径会解析成/student/css/style.css自然找不到。这是新手最常踩的坑。解决方式是在所有静态资源路径前加%request.getContextPath()%或者用 JSP 里的${pageContext.request.contextPath}统一拼前缀。另外一个隐蔽问题是过滤器把静态资源也拦截了。虽然我在 3.3 里写了放行规则但如果你用urlPatterns /*并且忘记放行.css .js .png浏览器里样式全废。解决在过滤器的放行条件里把/static/这个目录整体放行掉比逐个后缀更稳妥。5.5 现象数据库连接池配置导致启动极慢或超时C3P0 初始化时默认会从配置读acquireIncrement、maxPoolSize等参数。如果服务器网络环境特殊连接池尝试建连超时会把整个应用卡住。你在本地跑没问题但部署到学校机房可能就启动几十秒甚至报错。这是典型的分布式环境坑但毕业设计里也会遇到。解决在c3p0-config.xml里显式配置连接超时和重连次数c3p0-config default-config property namejdbcUrljdbc:mysql://localhost:3306/campus?useUnicodetrueamp;characterEncodingutf8/property property nameuserroot/property property namepassword123456/property property nameinitialPoolSize3/property property nameminPoolSize1/property property namemaxPoolSize10/property property nameacquireRetryAttempts3/property property namecheckoutTimeout5000/property /default-config /c3p0-config注意 XML 里要写成amp;这个细节很坑写错直接启动失败。如果你用的是 SSM 教学里的 Druid也有类似参数侧重点是一样的开发环境追求省事部署环境要控制超时和重试次数。6. 从能跑到答辩源码交付前的检查清单与一个提分技巧6.1 万字报告文档怎么写才对得上源码毕业设计交付物里那份万字报告文档不要从网上下载现成的评委一眼就能看出来。正确做法是你先把代码写完再对照代码写文档每一章都能举出自己项目里的真实细节。比如“系统设计”里画一张包结构图列出 Servlet、Service、DAO 各有哪些类“数据库设计”里把表结构做成表格要写出每个字段的含义和索引设计理由“测试与部署”里附上截图展示你确实把 war 包部署到了 Tomcat。报告的价值在于能回答“为什么这么设计”而不是堆概念。6.2 部署说明里必须写清的三件事部署说明是许多同学最后才补的结果搞得一塌糊涂。我一般会按三个模块来写环境准备、部署步骤、验证方法。环境准备要写 JDK 版本、Tomcat 版本、MySQL 版本以及环境变量 JAVA_HOME 和 PATH 的配置方式很多老师就会照着这个步骤自己装一次。部署步骤用编号列表导入 SQL 脚本、把 war 包放到 webapps、修改数据库连接配置、启动 Tomcat。验证方法要写清楚“看到什么页面说明部署成功”比如访问登录页、用测试账号登录、创建一个快递包裹。6.3 给演示加分的“运单跟踪”小功能如果你的系统已经有快递列表、取件、管理这些常规功能那我建议加一个“运单跟踪记录”的小模块一张track_log表记录每个运单号在每个时间点的状态描述管理端录入物流轨迹客户端展示成时间轴。这个功能不难但它能让你在答辩演示时有一个“这个系统不是简单 CRUD”的说服点教师提问时也能引出“你如何处理多表关联查询”这类问题。CREATE TABLE track_log ( id int NOT NULL AUTO_INCREMENT, tracking_no varchar(32) NOT NULL, status_text varchar(100) NOT NULL COMMENT 轨迹描述, create_time datetime NOT NULL, PRIMARY KEY (id), KEY idx_tracking (tracking_no, create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;演示时先录入一条包裹再往 track_log 里插入两条轨迹前端按时间正序展示整个系统的数据流转就一目了然了。我当年答辩时就是靠这个小功能让老师多问了三个问题我全部接住了。最后想提醒你毕业设计最忌“看起来都会做起来都不会”你现在把 Servlet、JDBC、JSP 的手写代码练熟了后面学框架才会真正懂底层原理。希望这次拆解能帮到你祝答辩顺利。本文还有配套的精品资源点击获取