
最近在整理一套基于 Servlet 的家乡特产织金砂锅推广平台源码包号 32911刚好赶上项目收尾阶段我把整个项目从功能拆解、数据库设计、核心代码到部署运行完整过了一遍。这套东西给我的第一感觉是它不是一个只为了交差演示的“玩具项目”而是把地方特产展示、在线下单、后台管理这几条线都打通了的完整 Java Web 应用。对于正在做 Java 课程设计、毕业设计或者想快速搭一个轻量级电商网站的初级开发者来说直接拿这套 Servlet 项目来改一改比从零开始写省太多事了。它的技术栈非常“经典”Servlet JSP JSTL MySQL Tomcat没有 Spring 全家桶那些复杂概念每一个请求怎么进来、数据怎么查出来、页面怎么渲染全程都是透明可见的。也正因为它足够简单你反而能看清 Java Web 最基本的运行逻辑。这篇文章我打算把整个项目的设计思路、核心代码、部署步骤和踩坑经验全部摊开讲一遍保证你看完不只是会运行源码还能明白为什么这么写。1. 项目拆解织金砂锅推广平台到底要做什么1.1 织金砂锅的商业痛点与网站定位织金砂锅是贵州织金一带的传统手工艺品用当地特有的无烟煤砂泥手工制作耐高温、透气性好炖汤煮粥都特别香。但这类地方特产有个通病东西好却缺少稳定的线上销售渠道。大部分卖家只能靠着线下游客、熟人介绍出货外地人想买找不到正规入口本地人又不懂怎么做推广供需两端完全断开了。所以这套推广平台的核心定位很清晰把织金砂锅的文化故事、产品展示和在线购买流程搬到网上。它不是一个大而全的电商系统不需要拼多多那样复杂的营销体系而是要解决三个基本问题——让外地人知道织金砂锅是什么、在哪里能买到、怎么下单付款。围绕这三个问题网站只需要做好四件事商品分类展示、用户注册登录、购物车结算、后台订单管理。别看功能少把这四件事用 Servlet 做扎实已经能覆盖一个小型特产商城 80% 的业务场景。我特别想强调一点这个项目选择的是 B2C 模式里的“企业展示 在线购物”混合体。前台是面向消费者的商品浏览页后台是面向管理员的商品和订单管理页两套操作逻辑完全分开。这种设计在真实的地方特产网站里太常见了所以它的业务模型非常贴近实际不是那种为了凑功能硬塞的“空中楼阁”。1.2 为什么用 Servlet 而不是 Spring Boot我知道很多人一看到 Servlet 就皱眉头觉得这技术“太老了”。但放在这个项目里Servlet 恰恰是最合适的选择。原因不复杂这套系统复杂度不高无非是增删改查、页面跳转、会话管理Servlet 写这些任务完全够用而且没有任何额外依赖部署到一个 Tomcat 就能跑。相比之下Spring Boot 虽然开发效率高但它引入了 IOC 容器、自动配置、starter 依赖这一堆概念对于刚学完 Java 基础、想搞懂 Web 原理的新手来说反而容易一头扎进“复制粘贴”的坑里。还有一个实际原因很多学校课程设计和毕业设计对技术选型有严格要求指定用 JSP Servlet 的比比皆是。不是老师守旧而是 Servlet 是 Java Web 的地基你用它写过一个电商系统之后再去看 Spring MVC、Spring Boot 的源码会发现它们的请求分发逻辑和 DispatchServlet 本质上就是在 Servlet 之上封装了一层。我当年先做了 Servlet 项目后面学 Spring MVC 的时候感觉像是“旧友重逢”一点都不陌生。另外Servlet 项目调试起来直来直去。一个请求从浏览器进来命中哪个 Servlet、调用了哪个方法、返回了哪个 JSP都清清楚楚写在 web.xml 和代码里出问题随手加个 System.out 就能定位。Spring Boot 里一个 Bean 注入报错可能绕半天在这个项目里几乎不存在这种问题。对你理解 HTTP 协议、请求响应周期、Session 机制来说这反而是最生动的教材。2. 平台功能模块与数据库设计2.1 前台功能商品浏览、用户登录、购物车与订单前台是消费者能直接看见的部分整个流程可以概括成一条线逛商品 → 注册/登录 → 加购物车 → 提交订单 → 查看订单状态。这套源码把这条线完整实现了。商品浏览模块包括首页轮播图、热门商品区、分类列表。首页通常展示几件推荐商品点击进入商品详情页后能看到大图、价格、库存、商品描述。这里的实现思路很简单商品数据从数据库查出来放到 request 域里JSP 页面用 JSTL 的c:forEach循环展示。用户模块最关键的是注册和登录的状态管理。注册时一般处理用户名重复校验、密码 MD5 加密登录成功后把用户对象放进 session同时更新用户表里的最近登录时间。这里要特别注意退出登录的按钮它的 Servlet 里得执行session.invalidate()把整个 session 作废否则用户按了退出其他人依然能用这台电脑上的旧 session 冒充他操作。购物车模块是前台的灵魂。这里有两种主流方案一种是把购物车数据存到 session 里用 Map 保存商品 ID 和数量另一种是建一张购物车表把购物车数据持久化到数据库。这套源码采用的是 session 方案理由很简单——购物车本来就是临时性的用户没下单之前让数据库存着一堆半成品数据意义不大。session 方案处理流程是点击“加入购物车” → 把商品 ID 和购买数量封装进 CartItem 放到 session → 购物车页面读取 session 里的商品列表并计算总价。订单模块则把 session 里的购物车数据清空生成一条订单记录插入数据库。提交订单时往往要做一个库存扣减的操作但要注意必须先判断库存是否充足否则可能出现超卖。另外订单表要记录下单时间、订单总金额、收货人信息、订单状态待付款 / 已付款 / 已发货 / 已完成状态字段用数字表示比较方便扩展。2.2 后台管理商品管理、订单管理与公告管理后台页面和前台是完全分离的。管理员先通过一个单独的后台登录入口验证身份然后才能进入管理界面。很多学生项目在这个环节有个通病只验证用户是不是登录了没验证是不是管理员导致注册一个普通账号就能跑到后台乱改数据。这套源码在后台过滤器中做了权限校验非管理员请求会被拦截跳回登录页这个细节非常值得学习。商品管理模块提供商品列表、新增商品、编辑商品、上架/下架、删除商品几个操作。新增和编辑都涉及图片上传Servlet 获取上传文件之后需要把图片保存到服务器指定目录比如upload/文件夹再把访问路径存入数据库。这里要特别注意中文文件名乱码后面我会专门讲。订单管理模块通常包含订单列表、订单详情、修改订单状态、删除订单。订单列表要支持按状态筛选、按时间排序。管理员的操作面板上最好显示“待发货数量”这种统计数据让管理员打开后台一眼就知道今天要处理哪些单。这部分虽然只是简单的 SQL 查询但统计的逻辑写好了后台体验会提升一个档次。公告管理模块用来发布首页公告和新闻动态。地方特产网站很适合加这个模块可以用来发布砂锅文化介绍、促销活动、物流说明等内容。公告表结构也很简单就是标题、正文、发布时间、是否置顶。2.3 数据库表结构与关键字段我从这套源码里提炼出一套最精简的数据库结构直接照着建表就行。核心表一共七张用户表、商品分类表、商品表、购物车表可选、订单表、订单明细表、公告表。如果还想做商品评论可以再加一张评论表但基础版用不到。用户表关键字段id、username、password、phone、address、role、create_time。其中role字段用0表示普通用户1表示管理员这个设计在登录判断时比再去查一张角色表简单得多。商品分类表关键字段id、name、description。商品表关键字段id、category_id、name、subtitle、price、stock、image、description、status。status用1表示上架、0表示下架商品列表只查status1的数据。订单表关键字段id、order_no、user_id、total_price、consignee_name、consignee_phone、consignee_address、status、create_time。订单明细表关键字段id、order_id、product_id、product_name、product_image、product_price、quantity、subtotal。注意到没有订单明细表里把商品名称、图片、价格这些“快照”数据也存了一份而不是只存商品 ID。这是非常重要的设计如果商品后来改价了、删除了历史订单里的金额和商品名称仍然能保持当时的下单状态不会跟着商品表一起变。很多新手在设计订单时只存了商品 ID最后订单详情页查不出商品名称还得去关联商品表商品一旦删了订单就变成“小黑人”。这里把快照存进去才是电商系统的常规玩法。下面我画一个简化版的数据库关系说明表格方便你对字段心里有数表名核心字段作用userid, username, password, role用户登录与权限判断categoryid, name商品分类productid, category_id, name, price, stock, image, status前台商品展示与库存判断ordersid, order_no, user_id, total_price, status订单主表order_itemid, order_id, product_id, quantity, subtotal订单商品明细快照noticeid, title, content, create_time公告资讯3. 核心代码实现解析3.1 Servlet 控制层设计请求分发与参数处理这套源码的 Servlet 没有用任何框架完全靠继承HttpServlet并重写doGet()和doPost()来实现。但它的类设计有一个明显的特点不是每个小功能写一个 Servlet而是按业务模块聚合。比如商品模块只有一个ProductServlet通过action参数来区分list、detail、add、update、delete等操作。请求进来之后Servlet 先获取action参数再用switch语句分发到不同的方法。这样既减少了类的数量也让代码的归属感更强。这种写法的好处是当你看到一个请求product?actiondetailid8你马上能在ProductServlet的switch (action)里找到对应分支不用在几十个 Servlet 类里翻找。对于只有几十个接口的小项目来说一个模块一个 Servlet 的组织方式比一个功能一个 Servlet 更清晰。当然等以后项目大起来了一个 Servlet 里塞十几个 action 方法会变得臃肿那就该考虑引入 Spring MVC 的注解路由了。在现阶段这套代码的逻辑是合理的。处理请求时有个细节要注意如果前台用的是表单提交提交按钮是 POST 方法但页面链接跳转是 GET 方法所以 Servlet 里通常会把doGet和doPost都指向同一个process方法保证两种请求方式都能正确处理。别小看这个操作很多初学者因为只写了doPost结果在浏览器地址栏直接访问 Servlet 链接时报 405 错误一脸懵。3.2 JSP 页面与 Session 状态管理JSP 在项目里同时承担了“模板”和“页面逻辑”两个角色。为了减少重复代码页面通常会把头部导航、底部版权声明封装成单独的 JSP 片段再用jsp:include引入。比如每个页面都要显示“欢迎你XXX”以及购物车数量这段逻辑放在头部公共片段里主页面就不用重复写了。Session 状态管理是这套系统的核心。用户登录成功后把User对象放进 session后面的每个请求都能通过session.getAttribute(user)获取当前用户。在 JSP 页面里可以用 JSTL 的${sessionScope.user.username}直接输出用户名。判断用户是否登录就用c:if test${empty sessionScope.user}来区分未登录时显示登录按钮已登录时显示用户中心。这里有个很重要的安全细节千万不要把用户 id 直接通过 URL 参数传递比如/order/delete?userId2这种。如果后台逻辑里拿到的 userId 是 URL 传的别人随便改参数就能操作别人的订单。正确的做法是从 session 里取当前登录用户的 id然后用 SQL 的 where 条件限制只能操作属于当前用户的数据。这套源码在订单列表查询时就是这么做的这是很多学生项目会漏掉的地方。Session 还有一个隐性问题HTTP 协议本身是无状态的Session 依赖 Cookie 里携带的 JSessionID 来识别用户。如果在 JSP 里出现了c:out和 HTML 结构混写的情况要留意 Cookie 是不是被禁用了。不过现在的浏览器默认都开着 Cookie这个坑出现的概率极低了解一下原理就行。3.3 购物车与订单生成的业务逻辑购物车模块是最容易出现“假逻辑”的地方。我见过不少项目点“加入购物车”以后就是弹个窗刷新下数据其实并没有真正加进去。这套源码的购物车实现比较实在使用HttpSession存一个MapInteger, Integerkey 是商品 IDvalue 是数量。每次加入购物车时先检查 Map 里有没有这个 key有就在原数量上加一没有就放一条新记录。在购物车页面遍历 Map 的 keySet逐个从数据库查商品最新价格再累加总价。生成订单的过程必须是一个“扣库存 → 插入订单 → 清空购物车”的完整链路。代码里要注意执行顺序先把订单主表插进去拿到自增 id再把购物车里的每一项转换成订单明细记录插入订单明细表最后把对应商品的库存减掉。如果中途任何一步失败都应该通过事务回滚保证数据一致。但这对纯 Servlet 项目来说有点麻烦你可以选择在 MySQL 里把这三件事包在一个存储过程里或者用 JDBC 的setAutoCommit(false)手动控制事务。这套源码本身没有用完整事务我建议你有精力还是要把这块补上防止高并发场景下库存扣了订单没生成、或者订单生成了库存没扣的情况。订单编号生成也值得一提。直接用数据库自增 id 当订单号太容易暴露销量一般会生成一个yyyyMMddHHmmss 随机数的字符串比如202506121030156278。这样既保证唯一也方便后台按时间查询。3.4 图片上传与本地存储方案商品图片上传是后台必做的功能。Servlet 3.0 之后上传文件可以用MultipartConfig注解配合request.getPart(file)来接收文件不用再去解析二进制流了。代码大致是先判断上传目录存不存在不存在就创建然后用 UUID 重命名文件避免中文名或重复文件名最后把文件写入服务器磁盘把访问相对路径存到数据库。这里有两个关键点。第一重命名时千万别保留原文件名否则用户一旦上传一个名字叫1.jpg的图片第二次上传的同名文件就会覆盖第一次的用UUID.randomUUID().toString()生成的新文件名几乎不会重复。第二上传目录的物理路径要动态获取不能用写死的绝对路径。常见做法是request.getServletContext().getRealPath(/upload)这样无论 Tomcat 部署在哪个磁盘都会找到项目根目录下的 upload 文件夹。还有一个容易被忽略的坑JSP 页面里显示图片用的是数据库存的路径比如/upload/abc.jpg这个一定要以项目上下文路径开头。如果项目部署后路径变了比如项目名改成了另一个这些写死的路径会全部失效。稳妥做法是在 JSP 里用${pageContext.request.contextPath}/upload/abc.jpg拼接上下文路径这样哪怕项目改了个名字图片也能正常显示。这一点代码里如果能统一处理后台商品图片展示就不会出现一片红叉了。4. 源码导入与运行教程4.1 环境准备与工具版本选型先把运行这个项目需要的环境工具列个清单。JDK 版本建议用 1.8与老项目的兼容性最好Tomcat 也用 8.5 或 9.0这两个版本搭配 Servlet 3.1/4.0 非常稳定。MySQL 用 5.7 或 8.0 都行注意如果 MySQL 8.0数据库驱动要用com.mysql.cj.jdbc.Driver连接字符串要加serverTimezoneAsia/Shanghai否则 JDBC 连接会报时区错误。开发工具方面Eclipse 的 Java EE 版和 IntelliJ IDEA 都行。如果你还是在校学生我更推荐 Eclipse因为它对 Tomcat 的集成方式更直白添加服务器、部署 Web 项目都是傻瓜式操作适合新手。IDEA 虽然好用但它的 Web 项目部署依赖 Artifact 和外部服务器配置第一次上手容易懵弄错一步就启动不了。数据库工具直接用 Navicat 或者 MySQL Workbench建议用 Navicat 的“运行 SQL 文件”功能把附带的sql文件一次性导入省去手动建表的麻烦。源码包里一般都会附带数据库初始化文件名字类似db_zhijin_shop.sql。如果没找到你就按我上面给出的表结构自己手工建表再把几条测试数据 insert 进去也能跑起来。4.2 导入 Eclipse / IDEA 并配置 Tomcat不同工具导入项目的方式略有差异。在 Eclipse 里打开菜单File → Import → General → Existing Projects into Workspace选择源码根目录Eclipse 会自动识别出 Web 项目.project文件并导入。导入后你要检查项目结构的 build path 是否包含了 Tomcat 依赖和 JDK 库如果没包含右键项目 →Properties → Targeted Runtimes勾选 Apache Tomcat再在Java Build Path里把原来的错误引用删掉换成 Server Runtime 里的 Tomcat。IDEA 的话操作是File → Open直接选择源码文件夹弹出来的窗口选择“Trust Project”。IDEA 导入传统 Web 项目时会提示是否创建 Facets你需要在Project Structure里设置好 Web 模块的 Web Resource Directory 为WebContent或webapp再添加 Artifact类型选 Web Application Exploded最后点运行按钮选择 Tomcat 服务器。这里麻烦我强烈建议第一次跑 Servlet 项目的同学用 Eclipse省下的时间够你多调试两遍代码。4.3 配置数据库连接与 Tomcat 部署数据库连接信息一般集中在src/db.properties文件里。打开后你会看到类似下面的内容drivercom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/zhijin_shop?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai usernameroot passwordyourpassword把username和password改成你本机的数据库账号密码即可。url里的zhijin_shop是数据库名要和你导入的 SQL 数据库名保持一致。注意如果 MySQL 版本是 8.0 以上driver要写成com.mysql.cj.jdbc.Driver否则运行时提示ClassNotFoundException。数据库连好之后在 Eclipse 里右键项目选择Run As → Run on Server选择本地 Tomcat 实例项目就会自动部署到 Tomcat 的 webapps 目录下。启动成功后浏览器访问http://localhost:8080/项目名/比如http://localhost:8080/zhijin_shop/。默认管理员账号通常在 README 里写了或者在初始化 SQL 里能看到一般是admin / admin之类。4.4 常见部署报错排查速查表我整理了一份这套项目运行最常见的报错及解决办法按出现概率排序报错现象可能原因解决办法访问页面出现 404项目名不对或 Tomcat 未部署成功在 Eclipse 控制台确认 Tomcat 启动日志无报错浏览器地址栏检查是否带了项目名Tomcat 启动时端口被占用8005 / 8080 被其他程序占用把 Tomcat 的 server.xml 里三个端口改掉或关闭占用进程连接数据库超时 / Access deniedMySQL 未启动或账号密码错误启动 MySQL 服务检查 db.properties 的密码是否匹配ClassNotFoundException: com.mysql.jdbc.Driver数据库驱动 jar 没导入把mysql-connector-java的 jar 包放进项目的WEB-INF/lib提交表单后中文乱码页面编码和请求编码不一致检查 JSP 头部pageEncodingUTF-8在 Servlet 中执行request.setCharacterEncoding(UTF-8)打开页面显示 500 空指针多为数据库表名或字段名不匹配对照 SQL 里实际字段名检查代码里的查询列名上传图片后页面不显示图片路径少了项目名改用${pageContext.request.contextPath}拼接图片地址这几个问题我基本都在实际运行中遇到过。特别是数据库连接这一关十个新手有八个栽在driver版本和serverTimezone上照着表格改完基本就通了。5. 我在实际项目中的经验与避坑建议5.1 JDBC 必须用 PreparedStatement这项目里所有数据库操作都是 JDBC 写的很多代码片段里居然还出现了 Statement 字符串拼接 SQL 的情况。我知道这是相当大一部分 Servlet 项目的通病但我还是要强调直接用Statement执行拼接 SQL不但容易被 SQL 注入还特别容易因为引号、单引号、空格问题写错。改成PreparedStatement之后参数用?占位再通过setString、setInt赋值既安全又不用费劲去拼接引号代码可读性也上一个档次。举个例子写商品列表查询String sql SELECT * FROM product WHERE category_id ? AND status 1 ORDER BY id DESC; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, categoryId); ResultSet rs ps.executeQuery();看着只改了两三行但这一改解决了用户输入1 OR 11这类注入问题。对于一个练手项目来说这可能是你最早接触到“安全编程”的机会别让简单字符串拼接把这个机会浪费掉。5.2 编码统一一下少掉一半头发这套源码如果出现乱码大部分是因为 JSP、Servlet、数据库、页面显示四层的编码没统一。我的建议非常简单所有 JSP 页面统一加pageEncodingUTF-8Servlet 里在方法开头加request.setCharacterEncoding(UTF-8)JDBC 连接 URL 加characterEncodingutf8MySQL 建库时统一用utf8mb4。上中下三层全部 UTF-8基本不会乱。还要注意 Tomcat 8 之后 URI 编码默认就是 UTF-8一般不用再改 connector。如果你用的是 Tomcat 7 或更早版本那还得去 server.xml 里给 Connector 加一个URIEncodingUTF-8的属性否则地址栏里的中文参数必乱。现在大家都用 8.5 了这个坑稍微少一点但万一你电脑上装的还是老版本这个配置就要检查一下。5.3 项目后续还能怎么扩展这个推广平台如果真想上线并用起来我觉得有几个方向值得扩展。第一把购物车从 Session 改成数据库表用户换电脑购物车也不丢第二加上支付宝沙箱或微信支付至少能模拟下单订单状态才能真正自动流转第三商品加一个库存下限提醒库存低于 5 件时后台飘红第四给商品加多图轮播和热门搜索词第五接入百度统计或者 CNZZ看看访客主要来自哪个地区这样才知道推广得往哪边使劲。我还特别建议把“织金砂锅文化”单独做成一个栏目页面放图片、文字、甚至视频链接用故事来带动产品销售。地方特产的核心竞争力就是文化和手工光靠普通商品列表很难让人下单加一段“砂锅是怎样炼成的”页面转化率可能会明显提升。这个项目底子已经有了花一个周末扩展成个小而美的特产电商完全做得到。最后再分享一个小技巧我在运行这套源码时习惯先把所有 Servlet 的doGet/doPost入口打一行日志System.out.println(访问了 request.getRequestURI())调试的时候看控制台就能知道请求到底到没到 Servlet、卡在哪一步。项目小日志随便打项目大了再换 log4j。这也是我大学时代调试 Servlet 项目用的笨办法但至今觉得特别好用。这套织金砂锅推广平台源码代码量不大但把 Java Web 最核心的几个模块——请求分发、会话管理、数据操作、文件上传——全部串起来了。如果你正在学 Servlet建议不要停留在运行层面亲手把购物车逻辑改一遍、给后台加一个字段、再跑一次完整流程收获会比看十篇教程都大。