简介这份基于JSP的二手交易平台管理系统源码面向Java Web学习者和毕业设计学生完整演示从用户注册、登录、商品发布到订单交易的业务闭环并给出分层设计的Web工程组织方式。压缩包共包含2000个文件以JS、JSP、CSS居多另有Java类、XML配置、SQL脚本、图片等资源分别承担前端交互、页面渲染、业务逻辑、数据库结构和界面展示整体约52.57MB。目前已有90人学习浏览适合用作毕设参考、源码阅读或JSP项目复盘。源码覆盖用户界面、控制层、服务层与数据访问层涉及JDBC数据操作、用户认证、权限控制、防SQL注入、异常处理等关键点数据库表设计围绕用户表、商品表、订单表展开页面中还引入AJAX实现异步更新体现常用性能优化思路。通过完整目录和Eclipse工程配置读者可快速定位关键模块理解注册验证、商品发布流程以及前后端数据交互的常用写法对后续开发同类交易或管理平台具有实用参考价值。1. 这份 JSP 二手交易平台源码先弄清楚它解决什么问题拿到一份「基于 JSP 的二手交易平台管理系统源码.zip」多数人的第一反应是赶紧解压、导入 IDE、跑起来看效果。但跑通之后真正的问题往往是它能改吗改哪里答辩时怎么讲这个技术方向还值不值得投入JSP 作为 Java Web 的“老牌”技术栈今天在商业项目里确实用得少了但在高校课程设计、毕业设计选题和中小型老系统维护中它依然是出现频率最高的方案。二手交易场景里天然包含用户、商品、订单、留言这几组实体能比较完整地演示 JSP Servlet JavaBean JDBC 的整套交互流程代码量不大适合快速上手。这篇文章就顺着“结构拆解 → 本地跑通 → 核心模块改造 → 避坑 → 演示升级”这条路线把这个方向彻底讲透。2. 拆解项目结构从目录、配置到三张核心表的来龙去脉2.1 为什么 JSP 项目依然值得看选型理由与真实适用场景先回答一个最实在的问题为什么今天找二手交易平台的源码搜出来的结果反而大多是 JSP而不是 Spring Boot因为高校课程设计的题目库更新速度远慢于技术栈迭代JSP 在 Java Web 教学中长期占据核心位置——它把 HTTP 请求、Session 状态、数据库交互这三件事用最直接的方式暴露在代码里没有 Spring 全家桶把细节层层包装。你看到的每一个request.getParameter()、每一次out.println()、每一条 JDBC 查询都是理解 Spring MVC 的基础。在业务层面二手交易场景恰好落在 JSP 最擅长的 CRUD 范畴。它不像商城系统那样需要复杂的库存扣减和订单状态机核心流程只有“用户发布商品 → 浏览搜索 → 下单联系 → 交易完成”数据模型规整业务流程短权限也简单。做课程设计最怕的不是功能难而是卡在一个环境问题或一个诡异的 Bug 上没人能问。JSP 项目的好处是资料多、示例多、踩过的坑在搜索引擎里都能找到答案自给自足的概率大得多。提示如果你将来要做 Spring Boot也别直接跳过 JSP。Spring Boot 早期版本支持 JSP 视图层很多老项目在迁移的过程中仍然混合使用 JSP 页面。处理这类“过渡期”项目时懂 JSP 的渲染机制和请求流转能让你快速定位问题。2.2 典型目录结构src、WebContent、配置文件的职责分工解压一份 JSP 项目源码第一件事不是打开 IDE而是先看目录结构。绝大多数 JSP 课程设计项目都采用 Eclipse 时代的经典布局也就是 src WebContent 双层结构。展开后大致是这样SecondHandPlatform/ ├── src/ │ ├── com.secondhand.dao/ // 数据访问层每个表一个 DAO │ ├── com.secondhand.model/ // 实体类对应数据库表字段 │ ├── com.secondhand.servlet/ // 控制器接收请求并转发 │ ├── com.secondhand.util/ // DBUtil、MD5Util 等工具类 │ └── db.properties // 数据库连接配置 ├── WebContent/ │ ├── css/ // 静态样式 │ ├── js/ // 前端脚本 │ ├── images/ // 商品图片上传目录 │ ├── jsp/ // 页面文件 │ ├── WEB-INF/ │ │ ├── web.xml // 核心配置文件 │ │ └── lib/ // 依赖 jar 包 │ └── index.jsp // 入口页面 └── sql/ └── secondhand.sql // 数据库初始化脚本这段结构里的主次关系必须分清model包放实体类字段基本与数据库表一一对应dao包封装 JDBC 访问最典型的写法是每个表一个 DAO 类里面有增删改查方法servlet包是业务控制层接收请求、调用 DAO、再转发到 JSPweb.xml注册 Servlet、配置过滤器与欢迎页。新手最容易犯的错是一上来扎进 JSP 页面里读代码结果一头雾水——因为业务逻辑的主力在 servlet 包里JSP 只是最后做页面渲染的那一层。2.3 数据库表设计用户、商品、订单三张主表如何关联二手交易平台的数据库通常由四张主表构成用户表tb_user、商品表tb_goods、订单表tb_order和收藏表tb_favorite。商品表通过发布者 ID 与用户表关联订单表记录买卖双方之间的交易关系。多读几份源码你会发现字段命名大多相似核心逻辑也基本一致。商品表的设计尤其关键下面是我在这个场景里见过最多的一种结构CREATE TABLE tb_goods ( goods_id INT PRIMARY KEY AUTO_INCREMENT, seller_id INT NOT NULL, title VARCHAR(80) NOT NULL, description TEXT, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, original_price DECIMAL(10,2), category VARCHAR(30), cover_image VARCHAR(200), status TINYINT DEFAULT 0 COMMENT 0-在售1-已售2-下架, publish_time DATETIME DEFAULT CURRENT_TIMESTAMP, view_count INT DEFAULT 0, FOREIGN KEY (seller_id) REFERENCES tb_user(user_id) );这个表里最核心的是status字段。二手交易不同于普通商城商品没有库存数量这个概念它的状态更贴近实物的真实流转在售 → 已售 → 下架。很多人在改造代码时忽略了这个字段导致商品被下单后仍然挂在首页列表里整个流程就“穿帮”了。另一个容易忽略的字段是view_count它记录商品被浏览的次数虽然简单却能用来做“热门推荐”的排序指标也是答辩时你能主动讲到的一个业务亮点。理解表结构以后再去看 DAO 和 JSP顺序就顺了。先读表再读方法最后看页面如何渲染这是读这类 JSP 项目最快的一条路径。3. 本地跑通全过程JDK、MySQL、IDE 三关怎么过3.1 版本组合是玄学JDK 8、Tomcat 8.5、MySQL 5.7 的搭配逻辑标题里的“源码.zip”说明这是一个可以直接导入运行的 Java Web 工程但能不能跑起来很大程度取决于你本地的环境组合。我在开头把版本匹配称作“玄学”不是没道理——同一个源码在 JDK 17 上可能编都编不过换到 JDK 8 就一切正常。背后的原因不复杂JSP 老项目大多基于旧版本的 Java EE 依赖JDK 11 之后移除了 Java EE 模块老代码里用到的javax.annotation一类类库直接缺失编译期就报错。我一般会给出这样一套相对稳妥的组合# 推荐基础组合 # 1. JDK 8安装 1.8.0_202 或更早的 8u 版本 java -version # 输出java version 1.8.0_202 # 2. Tomcat8.5.x 或 9.0.x # 3. MySQL5.7 或 8.0驱动类名不同见下文 mysql -uroot -pTomcat 版本这里要单独强调。Tomcat 10 起把 Java EE 的命名空间从javax.servlet换成了jakarta.servlet老项目里所有 importjavax.servlet的代码在 Tomcat 10 上会直接抛ClassNotFoundException。如果你非要留在 Tomcat 10就得把项目里所有javax.servlet.*改写为jakarta.servlet.*但第三方库老版 JSTL、FileUpload内部也依赖老包名改起来工作量远超预期。所以我的建议很直接用 8.5别给自己找麻烦。3.2 数据库初始化与连接参数脚本怎么执行、连接串怎么改JSP 项目几乎都会附带一个.sql初始化脚本位置一般在sql文件夹或项目根目录。执行之前先把脚本打开看一遍重点确认三件事数据库名、字符集、是否包含初始数据。常见写法是-- 脚本开头的建库语句 CREATE DATABASE IF NOT EXISTS secondhand DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE secondhand; -- 建表语句与初始数据顺序一般是用户、商品、订单、收藏执行方式用命令行和 Navicat 都可以命令行操作如下# 在 MySQL 命令行中执行脚本 mysql -uroot -p --default-character-setutf8mb4 secondhand.sql # 或者进入 MySQL 后执行 source 命令 mysql source /path/to/secondhand.sql;脚本执行完接下来要改项目的数据库连接配置。大部分 JSP 项目把连接参数写在src目录下的db.properties或DBUtil.java中形式如下// DBUtil.java 中的连接配置 private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/secondhand ?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456;这里有一个高频踩坑项目发布到 Tomcat 后读取的是classes目录下的编译产物。如果你改了src里的配置但没有重新编译Tomcat 依然在用旧配置。IDEA 里执行 Build → Rebuild Project或者重启 Tomcat 时勾选重新构建才能让修改生效。另外useSSLfalse和serverTimezoneAsia/Shanghai在 MySQL 5.7 JDBC 8 的组合下必须加上否则启动时控制台会报 SSL 警告或时区错误如果你用的是mysql-connector-java 5.1.x驱动类名是com.mysql.jdbc.Driver而切换到 8.x 版本后要改成com.mysql.cj.jdbc.Driver。注意报错信息是Unknown database secondhand时先别急着翻代码检查数据库名是否和连接串一致。这类问题百分之八十是名字拼错或脚本没执行成功不需要去调试 Java 层。3.3 导入 IDEA / Eclipse编码、Web Facet、启动配置三处细节把源码导入 IDEA 时最常出问题的不是代码本身而是工程识别失败。IDEA 打开项目文件夹后勾选 Open如果它没有识别出 Web 工程结构需要手动添加 Web FacetProject Structure → Modules → 选中当前模块 → 点 → Web → 把 Web Resource Directory 指向 WebContent 或 webroot → 确认 web.xml 路径被自动识别配置好 Web 模块后在 Run/Debug Configurations 里新建 Tomcat ServerDeployment 里加入这个项目Application context 填/secondhand或留空。这里有两个细节容易被忽略一是 JRE 配置要明确选到 JDK 8二是在 VM options 里加一行-Dfile.encodingUTF-8防止 Windows 环境下控制台 Log 输出乱码。Eclipse 用户的操作路径稍有不同导入项目后右键 Properties → Java Build Path确认 JRE 和 Targeted Runtime 都指向本地的 JDK 8 与 Tomcat 8.5然后 Run As → Run on Server。Eclipse 的常见问题是更新 JSP 页面后浏览器里看到的仍然是旧页面——这是 Tomcat 的work缓存目录没有清理。解决办法是右键服务器 → Clean或者手动删除 Tomcat 安装目录下的work/Catalina文件夹后重启。启动成功后访问http://localhost:8080/secondhand/看到登录页面或者首页说明部署完成。如果出现 404优先检查欢迎页配置——很多项目把欢迎页设置成/jsp/login.jsp而index.jsp只是一层跳转如果页面提示数据库连接失败回到上一节的连接串检查路径。4. 核心模块改造登录会话、商品发布、搜索分页的常见做法4.1 登录与注册MD5 加盐和 Session 过滤器怎么改JSP 项目的登录逻辑大体一致用户提交用户名和密码Servlet 接收参数后调用 DAO 的查询方法成功就把用户对象放进 Session跳到首页失败则返回登录页并提示错误。拿到源码后第一步改造重点应当是密码加密。不少老项目直接把密码明文存在数据库里这是答辩时最明显的短板。改造方案可以先用 MD5 加盐过渡代码量小改动面可控// 注册时加盐后做 MD5 public static String md5Salt(String password, String salt) { String input password salt; return DigestUtils.md5Hex(input); } // 注册调用 String salt UUID.randomUUID().toString().substring(0, 8); String hashedPwd md5Salt(passwordInput, salt); // 将 hashedPwd 和 salt 一并存入 tb_user 表登录时按用户名查到该用户对应的 salt用同样的规则计算哈希值再与数据库比对。这个改动看起来不大但在答辩时能体现你对“密码不能明文存储”这一安全原则的意识。需要同步注意的是改造后sql脚本里的初始账号密码也要做同样的处理否则升级后的系统里老账号全部无法登录。另一个容易被忽略的点是登录状态控制。JSP 项目通常靠 Session 里的 user 对象判断是否登录但很多老代码只在自己的页面里判断其他页面裸露在外面。合理的方式是用一个 Filter 统一拦截除登录、注册、静态资源之外的请求// 登录状态过滤器 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.contains(login.jsp) || uri.contains(register.jsp) || uri.contains(.css) || uri.contains(.js) || uri.contains(LoginServlet)) { chain.doFilter(req, resp); return; } // 需要登录的请求会话失效则跳回登录页 if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /jsp/login.jsp); return; } chain.doFilter(req, resp); }过滤器在web.xml中注册url-pattern写/*。但有两个边界要注意一是商品图片目录/images要放进放行列表否则商品图片会被当成受保护资源拦截二是页面中的 ajax 请求若被拦截并跳转登录页前端拿到的不是 JSON 数据而是一个 HTML 登录页需要判断请求头X-Requested-With来做单独处理否则 console 里会出现奇怪的数据解析报错。4.2 商品发布与图片上传文件该往哪里落盘、如何防绕过商品发布是二手交易平台最核心的操作也是答辩时经常被深问的一个模块。完整流程是卖家填写表单Servlet 接收请求把图片文件保存到服务器磁盘再将图片相对路径与表单字段一起写入数据库。JSP 项目中最常见的实现基于 Apache Commons FileUpload// 基于 Apache Commons FileUpload 的图片上传 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 非 multipart 请求直接抛弃 if (!ServletFileUpload.isMultipartContent(request)) { // 跳回发布页并提示错误 return; } // 2. 设置临时文件阈值和上传大小限制 DiskFileItemFactory factory new DiskFileItemFactory(); factory.setSizeThreshold(1024 * 1024); // 超过 1MB 落临时文件 ServletFileUpload upload new ServletFileUpload(factory); upload.setFileSizeMax(5 * 1024 * 1024); // 单个文件上限 5MB upload.setSizeMax(10 * 1024 * 1024); // 单次请求总大小 10MB // 3. 解析请求分拣普通字段与文件字段 ListFileItem items upload.parseRequest(request); FileItem fileItem null; MapString, String params new HashMapString, String(); for (FileItem item : items) { if (item.isFormField()) { params.put(item.getFieldName(), item.getString(UTF-8)); } else { fileItem item; } } // 4. 生成唯一文件名避免中文名和重名 String realPath request.getServletContext().getRealPath(/) images File.separator; String originalName fileItem.getName(); String ext originalName.substring(originalName.lastIndexOf(.)); String fileName System.currentTimeMillis() _ UUID.randomUUID().toString().substring(0, 8) ext; fileItem.write(new File(realPath fileName)); // 5. 相对路径存库 String cover images/ fileName; // 调用 goodsDAO.insert(goods, cover) }这段代码有三个必须处理的边界。第一getRealPath(/)返回的是 Tomcat 部署后的实际物理路径不同 IDE 部署位置不同所以绝对不能写死磁盘路径。第二需要校验文件后缀和 MIME 类型只允许 jpg、png、gif否则攻击者可以上传一个.jsp文件到你的服务器直接影响 Web 应用安全。第三上传目录如果位于部署目录内部每次重新部署项目时该目录会被清空重建已上传的商品图片也随之消失。稳妥做法是在 Tomcat 的conf/server.xml里配置一个虚拟目录来存放上传文件但 JSP 项目为了省事多数仍然放在WebContent/images下——这时至少记得在手动重启项目前把上传目录备份到项目外的磁盘文件里留底。4.3 搜索与分页带条件 SQL 的正确写法与参数边界二手交易平台的搜索一般支持按关键词、分类、价格区间做过滤核心实现是一个搜索 Servlet 接收参数后拼接 SQL。直接用字符串拼接是最危险的做法一旦搜索关键词里出现 OR 11整张表就能被拖走。安全起见统一用PreparedStatement参数绑定SELECT * FROM tb_goods WHERE status 0 AND (title LIKE CONCAT(%, ?, %) OR description LIKE CONCAT(%, ?, %)) AND category ? ORDER BY publish_time DESC LIMIT ?, ?对应的 Java 代码如下Connection conn DBUtil.getConnection(); String sql SELECT * FROM tb_goods WHERE status 0 AND title LIKE CONCAT(%, ?, %) ORDER BY publish_time DESC LIMIT ?, ?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, keyword); ps.setInt(2, (pageNum - 1) * pageSize); ps.setInt(3, pageSize); ResultSet rs ps.executeQuery();这里要知道一个细节MySQL 的LIMIT子句不支持用占位符直接绑定常见做法是把 pageNum 和 pageSize 先强转成 int 再绑定。不要直接把 pageNum 字符串拼进 SQL否则一个非法值就可能让整个查询崩掉。从 request 中获取 pageNum 时必须做默认值处理int pageNum 1; int pageSize 8; String pageNumParam request.getParameter(pageNum); if (pageNumParam ! null pageNumParam.matches(\\d)) { pageNum Integer.parseInt(pageNumParam); }页面上渲染分页组件时用 JSTL 表达式会更简洁c:if test${pageNum 1} a hrefSearchServlet?keyword${param.keyword}pageNum${pageNum-1}上一页/a /c:if span第 ${pageNum} / ${totalPage} 页/span c:if test${pageNum totalPage} a hrefSearchServlet?keyword${param.keyword}pageNum${pageNum1}下一页/a /c:ifJSP 里取 request 参数用${param.keyword}读 Servlet 转发时设置的属性则用${pageNum}两者在转发场景下经常容易被混淆。你如果发现翻页时页码总是对不上先检查是不是把request.setAttribute和 URL 参数混用了。5. 避坑实录JSP 项目最常见的 5 个翻车现场5.1 中文乱码三个环节的解码规则不一致现象注册信息里输入中文页面显示正常存进数据库后变成乱码或者页面上直接显示问号。原因JSP 页面编码、Servlet 接收请求时的编码、数据库连接串里的字符集参数三个环节没有统一成 UTF-8。最常见的情况是页面contentType写的charsetGBK而数据库连接是 UTF-8或者是 Servlet 里漏掉了request.setCharacterEncoding(UTF-8)。解决先从页面开始排查确认每个 JSP 顶部都有标准声明% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %Servlet 里在处理 POST 请求前加上request.setCharacterEncoding(UTF-8); response.setContentType(text/html; charsetUTF-8);最后确认DBUtil连接串里的characterEncodingutf8没有丢。这三个环节统一到 UTF-8乱码问题基本就能解决。如果数据库中已经存入了乱码数据清空表重新导一遍初始化脚本就好不要在已经脏了的数据基础上做转换。5.2 Tomcat 10 的 javax 与 jakarta 命名空间差异现象项目在 Tomcat 8.5 上一切正常换到 Tomcat 10 后启动失败控制台大量NoClassDefFoundError: javax/servlet/...页面也打不开。原因Tomcat 10 起将 Java EE 迁移到 Jakarta EE 9把javax.servlet包名整体替换为jakarta.servlet。而老 JSP 项目的代码和依赖库全部基于javax.servlet编译运行时找不到对应类。解决最省事的做法是换回 Tomcat 8.5 或 9.0。如果你想留在 Tomcat 10唯一路径是把项目源码和引用的第三方 jar 包都迁移到jakarta命名空间下这不是几行替换就能完成的老版本 JSTL 和文件上传库内部同样依赖旧包名。课程设计场景下没必要做这种无回报的升级直接降级服务器版本即可。5.3 MySQL 8 的驱动类名与 SSL 时区警告现象Tomcat 能正常启动但页面一旦执行数据库操作就报ClassNotFoundException: com.mysql.jdbc.Driver。原因本地安装的是 MySQL 8.x而项目代码里还写的是旧版驱动类名com.mysql.jdbc.Driverjar 包也还是 5.1.x。解决把 MySQL 驱动 jar 包换成 8.0.x 版本后将驱动类名改为Class.forName(com.mysql.cj.jdbc.Driver);同时连接 URL 必须补上serverTimezoneAsia/Shanghai和useSSLfalse。如果不想动代码也可以直接安装 MySQL 5.7——这个方案在某些兼容性要求更高的老项目里更省心。SQL 语法层面5.7 到 8.0 的变更并不影响这四张表的 CRUD。5.4 JSP 编译报错但找不到对应 Java 文件现象页面访问时报 500 错误后台日志提示Unable to compile class for JSP或者The method xxx is undefined但你打开项目源码找不到报错里提到的.java文件。原因Tomcat 会把 JSP 动态编译成临时 Java 类并放在work/Catalina/localhost/项目名/org/apache/jsp目录下编译失败时不会保留完整的中间源码所以你在项目里翻不到对应的类。解决先去 Tomcat 的work目录下清掉缓存再重启让 Tomcat 重新编译 JSP 并输出完整错误信息。随后检查 JSP 页面里的 import 语句和 EL 表达式重点看 Servlet 转发过来的参数名是否和页面里使用的一致。日志提示The method xxx is undefined时去对应的实体类或工具类中确认方法签名是否匹配。5.5 Tomcat 端口占用启动失败的最常见元凶现象Tomcat 启动按钮点下去控制台日志一闪而过弹窗提示Port 8080 required by Tomcat v8.5 Server at localhost is already in use。原因上一个 Tomcat 实例没有完全退出或者 8080 端口已被其他程序占用。解决Windows 下用下面的命令找到占用进程并结束netstat -ano | findstr :8080 taskkill /PID 进程号 /FmacOS 或 Linux 下用lsof -i :8080 kill -9 进程号如果不想结束现有进程可以修改 Tomcat 的conf/server.xml端口号将Connector port8080改为8081同时确保访问地址与部署配置一致。6. 让系统更耐看给订单加一个自动关闭定时任务如果项目答辩时只能展示一个亮点我建议你加一个“订单超时自动取消”的机制让商品在买家下单但未付款时超过设定时间自动恢复在售状态。这个功能在二手交易场景里非常实用——买家没有付款诚意时卖家的商品不应该一直被锁单。实现方式是利用ServletContextListener在应用启动时开启定时任务不需要引入 Spring 的Scheduled完全适配 JSP 项目的轻量风格。核心代码可以这样写// 在 web.xml 中注册监听器 // listener // listener-classcom.secondhand.listener.OrderTimeoutListener/listener-class // /listener public class OrderTimeoutListener implements ServletContextListener { private ScheduledExecutorService executor; Override public void contextInitialized(ServletContextEvent sce) { executor Executors.newSingleThreadScheduledExecutor(); // 每 10 分钟扫描一次超时订单 executor.scheduleAtFixedRate(new OrderTimeoutTask(), 0, 10, TimeUnit.MINUTES); } Override public void contextDestroyed(ServletContextEvent sce) { if (executor ! null) { executor.shutdownNow(); } } }对应的定时任务类里执行一条更新 SQL把超过 30 分钟未支付的订单状态改为已取消同时将订单关联的商品状态恢复为在售。加这个功能的价值不仅在于功能本身——它还能引导评审者关注到订单表和商品表之间的状态联动而这恰恰是 JSP 课程设计里最容易被忽略的业务逻辑。验证方式也很直接在数据库里把一条刚预约订单的创建时间改成 35 分钟前重启 Tomcat观察控制台日志输出再刷新商品列表确认状态已经恢复。这个验证步骤一定要自己先在本地跑通不要等现场演示时才发现任务没有触发。调整定时周期时注意scheduleAtFixedRate的单位是毫秒测试时可以把 10 分钟临时改成 10 秒演示完再改回去。我的习惯是把这类任务日志统一打到一个独立文件中而不是和 Tomcat 的业务日志混在一起。排错时可以直接盯着日志文件看执行情况省去在茫茫日志里翻找的时间。项目跑通之后尽量把 JDK、MySQL、Tomcat 的版本号记录在项目的 README 里下次换机器重新部署时这份记录能让你少走很多弯路。希望帮到你。本文还有配套的精品资源点击获取
