ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSP+Java校园二手交易平台实战:从技术选型到避坑指南

JSP+Java校园二手交易平台实战:从技术选型到避坑指南 简介本资源为基于Java与JSP技术实现的校园二手物品交易平台完整项目包面向计算机相关专业正在完成课程设计、毕业设计或需要项目实战练习的学生。项目采用MVC设计模式涵盖用户注册登录、商品发布与浏览、交易沟通、订单管理、在线支付及评价反馈等核心模块并配套论文、开发文档与数据文档便于理解设计思路与数据库结构。压缩包共1305个文件约22.74MB其中128个java源文件与119个jsp页面构成系统主体364个js、146个css及大量png、gif、jpg资源支撑前端交互与界面展示另含sql脚本、properties配置及doc文档等辅助材料。源码经本地编译调试可运行代码注重可读性与安全性对SQL注入、跨站脚本等风险有所防范。目前已有39人学习适合作为毕业设计参考或项目实战练习材料。1. 校园二手交易平台为什么 JSP Java 这套老组合还在被反复交作业每年毕业季计算机专业的课程设计题目里总有几个钉子户「基于 Java 的校园二手物品交易平台」就是其中之一。很多人第一反应是都什么年代了还用 JSP但如果你真去翻招聘要求或者接手过一些校园内部系统会发现 JSP Servlet MySQL 这套组合依然活跃在大量中小型 Web 项目里尤其是需要快速交付、部署环境受限、维护人员技术水平参差的场景。这个标题背后要解决的核心问题很具体让校内学生能发布闲置物品、按分类浏览、搜索关键词、下单联系卖家管理员能审核商品和处理举报。它适合谁适合正在做课程设计、需要一套能跑通且讲得清原理的 Java Web 入门项目的人也适合想理解「请求从浏览器到数据库再回来」完整链路的开发者。JSP 在这里的角色是视图层负责把后端查出来的商品列表渲染成 HTML而不是什么过时技术展览。2. 技术选型与分层JSP 当视图、Servlet 做控制、JDBC 管数据2.1 为什么是 JSP 而不是纯 HTML AJAX校园二手平台的功能边界很清晰列表页、详情页、发布表单、个人中心、后台审核。这些页面大部分是「请求 → 查库 → 渲染」的同步流程用 JSP 直接在服务端拼 HTML 反而比前后端分离少一层跨域和接口联调的麻烦。JSP 的本质是 Servlet容器第一次访问时会把它编译成.java再编译成.class所以性能上并不比手写 Servlet 差多少。常见做法是把 Java 代码用% %脚本块嵌在页面里但更规范的方式是用 JSTL 标签库配合 EL 表达式把业务逻辑挡在 JSP 之外。我一般会要求所有 JSP 页面顶部引入% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %然后用c:forEach遍历商品列表这样页面里几乎看不到 Java 代码后期改样式不会误删逻辑。2.2 三层结构怎么切entity、dao、servlet 各管什么项目目录按包名分层是最省心的做法。entity包放User、Goods、Category、Order这些和数据库表一一对应的类字段名和列名保持驼峰与下划线的映射关系。dao包负责所有 SQL 操作每个方法只做一件事findById、findByCategory、insert、updateStatus。servlet包按业务模块拆比如GoodsListServlet、GoodsPublishServlet、LoginServlet每个 Servlet 的doGet或doPost里只做参数校验、调 dao、转发或重定向。这种切法的好处是排错时能快速定位页面显示不对先看 JSP 的 EL 表达式数据不对看 dao 的 SQL请求进不来就看web.xml或注解映射。下面是一个典型的商品列表 Servlet 骨架WebServlet(/goods/list) public class GoodsListServlet extends HttpServlet { private GoodsDao goodsDao new GoodsDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 取分类参数默认查全部 String categoryId req.getParameter(categoryId); ListGoods goodsList; if (categoryId null || categoryId.isEmpty()) { goodsList goodsDao.findAllOnSale(); } else { goodsList goodsDao.findByCategory(Integer.parseInt(categoryId)); } // 2. 放入请求域转发给 JSP 渲染 req.setAttribute(goodsList, goodsList); req.getRequestDispatcher(/WEB-INF/jsp/goods_list.jsp) .forward(req, resp); } }这段代码里WebServlet注解替代了web.xml里的servlet-mappingServlet 3.0 以后都推荐这么写。forward是服务端跳转浏览器地址栏不变请求域里的goodsList能被 JSP 直接读到。注意 JSP 放在WEB-INF下是常见安全习惯这样用户没法直接通过 URL 访问 JSP 文件必须经过 Servlet 转发。2.3 数据库表设计四张核心表撑起整个交易流程表结构不用搞太复杂校园二手平台的核心就四张表。user表存学号、昵称、密码哈希、联系方式、角色普通用户/管理员。category表存分类名和排序权重方便后台调整首页分类顺序。goods表是主表字段包括标题、描述、价格、成色、图片路径、发布者 ID、分类 ID、状态待审核/在售/已下架/已售出、发布时间。order表记录买家对商品的意向字段有商品 ID、买家 ID、卖家 ID、留言、创建时间、处理状态。这里有个容易翻车的地方goods表的status字段一定要用整型枚举而不是字符串否则后期加状态时改 SQL 条件会漏掉大小写。下面这张表把关键字段和取值说清楚表名关键字段类型说明userid, student_no, nickname, password, phone, roleint, varchar, varchar, varchar, varchar, tinyintrole0 普通用户role1 管理员goodsid, title, price, condition_level, image_path, user_id, category_id, statusint, varchar, decimal, tinyint, varchar, int, int, tinyintstatus0 待审核1 在售2 已下架3 已售出orderid, goods_id, buyer_id, seller_id, message, statusint, int, int, int, varchar, tinyintstatus0 待处理1 已联系2 已完成categoryid, name, sort_orderint, varchar, intsort_order 越小越靠前price用decimal(10,2)而不是float避免金额计算出误差。image_path存相对路径比如/upload/2025/03/abc.jpg不要存绝对路径否则换服务器部署时全部失效。3. 从零跑通最小闭环环境、建表、发布与浏览3.1 环境配置JDK、Tomcat、MySQL 的版本对齐这一步是新手翻车最集中的地方。JDK 用 8 或 11 都行但 Tomcat 版本要和 JDK 匹配Tomcat 9 支持 JDK 8Tomcat 10 开始把javax.servlet改成了jakarta.servlet如果你网上抄的代码用的是javax包部署到 Tomcat 10 会直接报ClassNotFoundException。我一般建议课程设计统一用 JDK 8 Tomcat 8.5 或 9MySQL 用 5.7 或 8.0。环境变量配置时注意JAVA_HOME指向 JDK 根目录而不是bin目录CLASSPATH里加上.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jarWindows 下。验证命令java -version javac -version # 两个版本号必须一致不一致说明 PATH 里有多个 JDK如果java -version和javac -version输出不同去PATH里把旧版本路径删掉。Tomcat 启动失败先看logs/catalina.out最常见的是端口 8080 被占用改conf/server.xml里的Connector port8080为 8081 即可。3.2 建库建表与 JDBC 连接工具类建库语句直接执行字符集用utf8mb4支持 emojiCREATE DATABASE campus_trade DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE campus_trade; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) UNIQUE NOT NULL, nickname VARCHAR(30) NOT NULL, password VARCHAR(64) NOT NULL, phone VARCHAR(15), role TINYINT DEFAULT 0 ); CREATE TABLE goods ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, condition_level TINYINT DEFAULT 1, image_path VARCHAR(200), user_id INT NOT NULL, category_id INT NOT NULL, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );JDBC 连接不要每次new一个 Connection写一个工具类从db.properties读配置public class DBUtil { private static String url; private static String username; private static String password; static { try (InputStream in DBUtil.class.getClassLoader() .getResourceAsStream(db.properties)) { Properties props new Properties(); props.load(in); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); Class.forName(props.getProperty(jdbc.driver)); } catch (Exception e) { throw new RuntimeException(数据库配置加载失败, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } }db.properties放在src根目录下内容四行jdbc.drivercom.mysql.cj.jdbc.Driver、jdbc.urljdbc:mysql://localhost:3306/campus_trade?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8、jdbc.usernameroot、jdbc.password你的密码。注意 MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver5.x 是com.mysql.jdbc.Driver写错会报Loading class ... deprecated然后连接失败。3.3 商品发布与列表渲染的完整链路发布流程分两步先上传图片再提交表单。图片上传用 Servlet 3.0 的MultipartConfig注解在GoodsPublishServlet上加上MultipartConfig(maxFileSize 5 * 1024 * 1024)然后在doPost里用request.getPart(image)拿到文件。保存路径建议用日期分目录避免单目录文件过多Part filePart request.getPart(image); String fileName System.currentTimeMillis() _ filePart.getSubmittedFileName(); String dateDir new SimpleDateFormat(yyyy/MM).format(new Date()); String saveDir getServletContext().getRealPath(/upload/ dateDir); new File(saveDir).mkdirs(); filePart.write(saveDir File.separator fileName); String imagePath /upload/ dateDir / fileName;getRealPath拿到的是部署目录的绝对路径项目重新部署后上传的文件会丢所以生产环境要配外部存储目录。列表页 JSP 用 JSTL 遍历c:forEach items${goodsList} varg div classgoods-card img src${pageContext.request.contextPath}${g.imagePath} alt${g.title} h3${g.title}/h3 p classprice${g.price}/p a href${pageContext.request.contextPath}/goods/detail?id${g.id}查看详情/a /div /c:forEach${pageContext.request.contextPath}是项目上下文路径部署到 Tomcat 时如果项目名不是 ROOT不加这个前缀图片和链接全会 404。这是 JSP 项目里最高频的翻车点之一。4. 避坑与排查JSP 项目里那些让人血压升高的瞬间4.1 中文乱码从请求到响应要设三道关现象是表单提交后数据库里存的是问号或者页面显示乱码。原因通常是三处没设对第一JSP 页面顶部没写% page contentTypetext/html;charsetUTF-8 languagejava %第二Servlet 里request.setCharacterEncoding(UTF-8)没在读取参数之前调用第三数据库连接 URL 没加characterEncodingutf8。解决顺序是先在 JSP 和 Servlet 里统一设 UTF-8再检查db.properties的 URL最后确认 MySQL 建库时用了utf8mb4。Tomcat 8 以后 GET 请求默认 URI 编码是 UTF-8但 POST 请求体编码必须手动设这个坑每年都有人踩。4.2 图片上传后不显示路径拼接与部署目录的坑现象是上传成功但列表页图片裂开。原因通常是imagePath存的是绝对路径或者 JSP 里src没加contextPath。解决方法是数据库只存相对路径JSP 里统一用${pageContext.request.contextPath}${g.imagePath}拼接。另外如果项目部署在 Tomcat 的webapps下getRealPath(/upload)指向的是webapps/项目名/upload重新部署 war 包时这个目录会被覆盖所以测试阶段可以接受正式用要改成外部目录并在 Tomcat 里配虚拟路径映射。4.3 数据库连接泄漏Connection 没关导致 Tomcat 假死现象是运行一段时间后页面一直转圈Tomcat 日志里出现Cannot get a connection, pool exhausted。原因是 dao 里Connection、PreparedStatement、ResultSet没有在finally块里关闭。解决方法是每个 dao 方法都用 try-with-resourcespublic ListGoods findAllOnSale() { ListGoods list new ArrayList(); String sql SELECT * FROM goods WHERE status 1 ORDER BY create_time DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Goods g new Goods(); g.setId(rs.getInt(id)); g.setTitle(rs.getString(title)); g.setPrice(rs.getBigDecimal(price)); list.add(g); } } catch (SQLException e) { e.printStackTrace(); } return list; }try-with-resources 会在代码块结束时自动关闭资源顺序是 ResultSet → PreparedStatement → Connection不用手写finally。如果项目并发量稍大建议引入 Druid 或 HikariCP 连接池在db.properties里配initialSize、maxActive、maxWait避免每次请求都新建连接。4.4 JSP 编译报错javax 与 jakarta 的包名冲突现象是 Tomcat 10 部署后访问页面报java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet。原因是 Tomcat 10 把 Servlet API 的包名从javax.servlet改成了jakarta.servlet而你项目里 import 的还是旧包。解决办法有两个要么把 Tomcat 降到 9要么把所有javax.servlet替换成jakarta.servlet并换用对应版本的 JSTL。课程设计阶段我一般直接建议用 Tomcat 9省去改包的麻烦。4.5 表单重复提交刷新页面就多一条商品现象是用户发布商品后按 F5 刷新数据库里出现两条相同记录。原因是表单提交后直接forward到了列表页浏览器地址栏还停在 POST 请求上。解决办法是用「重定向后取」模式发布成功后response.sendRedirect(request.getContextPath() /goods/list)这样浏览器地址栏变成 GET 请求刷新不会重复提交。这个习惯从写第一个 Servlet 就该养成。5. 进阶技巧用过滤器统一登录校验与字符编码项目做到后期你会发现每个 Servlet 里都在重复写request.setCharacterEncoding(UTF-8)和登录判断代码又臭又长。这时候该上过滤器了。写一个EncodingFilter实现javax.servlet.Filter在doFilter里设编码后放行再写一个AuthFilter拦截/goods/publish、/user/center、/admin/*这些路径检查session.getAttribute(user)是否为空为空就重定向到登录页。在web.xml里配filter-mapping时注意顺序编码过滤器要放在最前面。WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }WebFilter(/*)表示拦截所有请求chain.doFilter是放行到下一个过滤器或目标 Servlet。注意过滤器的执行顺序在注解方式下不保证如果编码和登录校验有依赖关系建议在web.xml里用filter-mapping显式声明顺序。另外登录校验过滤器里要放行登录页、注册页和静态资源否则用户永远登不进来。验证过滤器是否生效有个简单办法在doFilter里打一行System.out.println(filter hit: ((HttpServletRequest)req).getRequestURI())启动 Tomcat 后访问任意页面控制台应该输出对应 URI。如果没输出检查WebFilter的 urlPatterns 是否写对或者web.xml的metadata-completetrue是否把注解扫描关掉了。最后说个我自己的习惯每次改完 dao 层的 SQL一定先在 MySQL 客户端里把语句粘过去跑一遍确认能查出数据再写进 Java 代码。JSP 页面改完样式后强制刷新用Ctrl F5而不是F5避免浏览器缓存旧 CSS 导致「改了没效果」的玄学问题。这套东西不复杂但细节密度高能完整跑通一遍Java Web 的请求链路就算真正入门了。希望帮到你。本文还有配套的精品资源点击获取
返回列表