简介:面向JavaWeb初学者及毕业设计学生,这套商品管理系统提供了从用户登录到后台管理的完整闭环。系统涵盖用户注册登录与角色权限区分,管理员可维护商品信息、商品分类、库存数量及订单状态,用户端支持购物车下单,后台还能对销售数据进行统计分析,是一个可用于实战的电商管理基础框架。前端采用Vue构建页面,后端基于JavaWeb分层实现,代码耦合度适中,便于二次扩展。资源压缩包共163个文件,总体仅3.29MB。其中包含26个Java源码、34个XML配置、12个JS、4个HTML和4个CSS构成的界面资源,6个JAR依赖库,以及1个SQL数据库脚本;同时附带class编译文件与字体文件。文件类型覆盖了编码、配置、样式、脚本、数据导入等完整开发环节。已有1123人学习/下载该资源。其价值在于提供了可运行的增删改查范例,结合订单处理、库存更新等业务场景,可帮助读者快速掌握JavaWeb项目的分层架构与数据流转逻辑,也可作为毕业设计初稿进一步添加促销、轮播等特色模块。 做JavaWeb开发这些年,我见过太多新手一上来就问“第一个项目做什么好”,我的答案从来没变过:做一个商品管理系统。它不花哨、不烧脑,但麻雀虽小五脏俱全——登录认证、增删改查、分页搜索、图片上传、打包部署,这些Web开发里最核心、最高频的技能点,它全都占了。你把这一个项目从头到尾跟完,JavaWeb这条线基本就打通了。
这篇文章我会从一个完整的项目视角来拆解,包含技术选型、数据库设计、核心功能实现、部署发布,还有我在实操中踩过的一些坑。这篇内容不是给你贴一堆代码让你去抄,而是告诉你每一步为什么要这么做,遇到问题该往哪个方向排查。不管你是准备做课程设计、毕业设计,还是纯粹想通过项目把JavaWeb串起来,这篇文章都值得你跟着走一遍。
1. 整体设计与技术选型
1.1 为什么还在用 Servlet + JSP
现在Spring Boot已经成为主流,新项目很少再从Servlet + JSP起步。但作为教学和课设项目,Servlet + JSP依然是最好的入门组合,没有之一。
原因是Spring Boot帮你把Tomcat内嵌了、把请求分发封装了、把依赖管理做好了,你写起来很爽,但很多底层机制你根本没机会接触。而商品管理系统这种体量,正好适合用Servlet + JSP把一条请求的完整链路走明白:浏览器发起HTTP请求,请求到达Tomcat容器,容器根据web.xml或注解找到对应的Servlet,Servlet调用Service处理业务逻辑,Service再调DAO完成数据库操作,最后把结果页面转发或者重定向回浏览器。这个过程你要是亲手用Servlet写一遍,你对HTTP协议、请求响应模型、会话机制的理解会彻底不一样。
技术栈我建议这么定:Servlet 4.0 + JSP + JSTL + MySQL 8.0 + Tomcat 9/10 + JDBC,前端用BootStrap或者纯CSS手写都行,不引入复杂框架。这样做的好处是,整条链路里的每一个组件你都能看到、能控制,排查问题时思路是清晰的。
1.2 三层架构与包结构设计
既然是练手项目,项目结构就必须规范,不然代码一多就乱。我习惯把包结构按三层架构来分,这也是JavaWeb项目最经典的组织方式。
com.example.shop ├── bean // 实体类:User、Product ├── dao // 数据访问层:UserDao、ProductDao ├── service // 业务逻辑层:UserService、ProductService ├── servlet // 控制器层:LoginServlet、ProductServlet... ├── filter // 过滤器:LoginFilter、EncodingFilter └── util // 工具类:DBUtil、PageUtil实体类对应数据库表结构,DAO只负责增删改查,Service处理业务规则,Servlet接收请求、调用Service、控制页面跳转,Filter统一处理编码和登录拦截。各层之间单向依赖,Controller -> Service -> DAO,不要反向调用,也不要出现Servlet里直接拼SQL的情况。
这种分层的价值在于,你后续想给某个功能加逻辑,非常清晰该去哪个类里改。比如你想在删除商品前检查它是否还有库存,只需要在Service层加一个判断,不用动Servlet和DAO。
2. 环境准备与项目搭建
2.1 2023版IDEA创建JavaWeb项目的两种方式
如果你用的是2023版的IDEA,按下New Project的时候会发现,以前的Java Enterprise里找“Web Application”选项的路径变了,很多教程里的截图已经对不上。这里我给两个亲测可行的方案。
方案一(推荐):直接创建Maven项目,然后手动变成Web项目。
新建Project时选择Maven,不勾选任何骨架(archetype),JDK选1.8或者11。创建完之后,在src/main下新建webapp目录,再在webapp下建WEB-INF目录,放一个web.xml。然后改pom.xml,打包方式设为war:
<packaging>war</packaging>再补上Servlet、JSP、JSTL、MySQL驱动这几个依赖。配置Tomcat时,打开Run -> Edit Configurations,点加号找到Tomcat Server -> Local,配置好Tomcat路径后,在Deployment页签里把当前项目以/shop为上下文路径部署上去。这个上下文路径就是你以后访问项目的根路径,访问首页就是http://localhost:8080/shop/index.jsp。
方案二:用IDEA的Jakarta EE模板创建。
新建Project时选择Jakarta EE,勾选Web Application,IDEA会帮你生成一个带web.xml的标准Web项目。不过这个方式生成的模板依赖是较新的Jakarta命名空间,和传统javax命名空间有区别,如果你用的教程是旧版的,代码里javax和jakarta混着来会非常痛苦。所以更稳妥的还是自己手搭。
2.2 数据库设计与初始化脚本
商品管理系统的核心表就两张:用户表和商品表。如果你要做订单,再加一张订单表和订单明细表,但课设项目通常做到商品管理加登录就够了。
用户表我建议至少包含这几个字段:
CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;商品表是核心,设计时要考虑实际场景:
CREATE TABLE t_product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT '商品名称', price DECIMAL(10,2) NOT NULL COMMENT '单价,单位元', stock INT NOT NULL DEFAULT 0 COMMENT '库存', image VARCHAR(255) DEFAULT NULL COMMENT '图片路径', description TEXT COMMENT '描述', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有个小细节,价格字段一定要用DECIMAL而不是FLOAT,浮点数在计算金额时会出现精度丢失,这是经典问题。另外,表和字段的字符集统一用utf8mb4,不然存emoji或者一些特殊字符会乱码。
2.3 JDBC基础封装与连接管理
如果你用的是纯JDBC,不建议每个DAO里都写一遍Class.forName和getConnection,太啰嗦,而且连接用完不关会直接把数据库连接池耗尽。最好封装一个DBUtil工具类,把连接获取和关闭统一管理。
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/shop?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }注意MySQL 8.0以上驱动类名是com.mysql.cj.jdbc.Driver,老教程里的com.mysql.jdbc.Driver已经过时了。连接串里的serverTimezone=Asia/Shanghai也不能少,否则会报时区错误。如果是学习用途这样写完全够用,生产环境得换Druid或HikariCP连接池,原理其实一样,都是管好连接的创建和复用。
3. 核心功能实现与踩坑记录
3.1 登录认证与权限拦截
登录逻辑本身不难:页面提交用户名密码,Servlet里查数据库比对,成功就把用户信息放进Session,失败就回登录页提示错误。但有一个关键点,登录成功之后,不能只做跳转,还得考虑权限控制——也就是不能让用户跳过登录直接访问商品管理页面。
实现思路很简单,写一个Filter拦截所有/admin/*路径,每次请求进来先检查Session里有没有登录标记:
@WebFilter("/admin/*") 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; Object user = request.getSession().getAttribute("loginUser"); if (user == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); } else { chain.doFilter(request, response); } } }这里建议用@WebFilter注解,省去了web.xml里一堆配置。但要注意,2023版IDEA创建项目时如果你选了新模板,注解包名是jakarta.servlet,而Servlet包版本又可能不一样,最容易报“找不到注解”或者ClassNotFoundException,排查时先看依赖里实际引入的是javax还是jakarta。
3.2 商品列表、条件查询与分页
商品列表是系统的门面,也是功能最密集的一块。除了把所有商品查出来循环展示,通常还要支持按名称模糊搜索和分页。
分页的实现逻辑是这样的:前端传入当前页码currentPage和每页条数pageSize,后端查两次数据库,第一次查总记录数算出总页数,第二次用LIMIT查当前页的数据:
SELECT * FROM t_product ORDER BY id DESC LIMIT ?, ?这里第一个问号是偏移量,计算方式是(currentPage - 1) * pageSize,第二个问号是每页条数。LIMIT的偏移量不能在前端直接拼进去,否则就是SQL注入漏洞,必须用PreparedStatement的setInt设定参数。这个点是面试常问,也是实际项目中容易忽略的地方。
我在做分页时遇到过一个小坑:用了request.getParameter("page"),但如果用户首次进入页面,page参数是null,直接用Integer.parseInt就会报NumberFormatException。所以一定要做空值判断,默认第一页。这个小问题排查了我好一会儿,报错信息还不直观。
列表页的展示,我建议配合JSTL的c:forEach来遍历数据,Java代码只在Servlet里写,JSP页面里不要出现<% %>代码块,既丑又难维护。逻辑和页面分离之后,后续改样式、改展示逻辑都方便很多。
3.3 新增、编辑、删除的注意事项
新增和编辑可以共用一个表单页面,通过URL参数区分是新增还是修改。Servlet里先判断参数里有没有商品ID,有就是编辑,需要把商品原数据查出来回显到表单;没有就是新增,提交后执行insert。
表单提交过来的数据,第一件事是设置编码。在Servlet里,POST请求最稳妥的做法是在读取任何参数之前先执行:
request.setCharacterEncoding("UTF-8");这个代码必须在第一次调用request.getParameter()之前执行,否则中文必乱码。我之前在Filter里统一处理编码,后来发现某些Servlet里又手动设置了,导致重复设置其实没有大问题,但为了省心,我还是建议写一个EncodingFilter统一处理所有请求的编码,这样就不会漏。
删除操作有个容易被忽视的点:建议用POST方式提交删除请求,不要用简单的<a href="delete?id=1">。因为GET请求会被浏览器预加载,搜索引擎爬虫也可能访问,如果误爬删除链接,数据就没了。更保险的做法是在页面上用表单包裹,点击时弹窗确认。
3.4 商品图片上传与回显
图片上传是很多课设项目里的加分项,但也是坑最多的地方。我用的是Apache的commons-fileupload组件,配置很简单,但要注意保存路径的问题。
很多教程会写用request.getServletContext().getRealPath("/upload")获取上传目录,这样项目部署后文件会保存在Tomcat的webapps目录下。但如果你用IDEA开发时,Tomcat会以Exploded方式部署项目,每次重新构建项目,之前上传的图片可能就丢了。这个问题坑了好多人,我以为文件丢了,其实是项目目录被重新生成,上传文件跟着被清理了。
后来我改成了把图片保存到服务器固定目录,比如Windows下的D:/upload或者Linux下的/data/upload,然后通过配置Tomcat的虚拟路径映射来访问。在Tomcat的conf/server.xml的Host标签里加:
<Context path="/upload" docBase="D:/upload" />这样商品图片的URL就是http://localhost:8080/upload/xxx.jpg,文件不会因为重新部署而丢失。如果你是发布到生产环境,这种方案也更可靠。
4. 发布部署与高频问题排查
4.1 本地打War包与Tomcat发布
项目做得差不多了,就要考虑打包发布。Maven项目直接双击package,或者命令行执行mvn clean package,在target目录下就会生成一个shop.war。把这个war包复制到Tomcat的webapps目录下,启动Tomcat,它会自动解压并部署。
这里有个细节,war包的名字决定了访问路径。shop.war对应的是http://localhost:8080/shop/,如果你改名叫abc.war,访问路径就变成/abc/。有时候我为了先快速验证功能,直接改war包名再重启,比在IDEA里改配置快很多。
启动之后第二件事是看日志。Tomcat的logs目录下有个localhost.log,里面是应用启动的详细日志。页面报错时,大部分情况下这个日志里已经写清楚了异常堆栈,比你在浏览器里盯着那个500页面有用得多。
4.2 Windows Server 环境部署要点
如果你需要部署到Windows Server上,有几个点是客户端环境不会遇到的,容易卡住。第一,防火墙必须放行8080端口,否则服务器本机访问正常,外部IP死活访问不了。第二,把Tomcat注册成Windows服务(比如用service.bat install),这样服务器重启后Tomcat能自动启动,不用手动去点startup.bat。
如果要和Apache共存,常见做法是Apache监听80端口处理静态资源,动态请求转发给Tomcat。最古老的方案是通过mod_jk走AJP协议,集成起来稍复杂。现在更常见的是直接用Apache的mod_proxy做反向代理:
ProxyPass /shop/ http://localhost:8080/shop/ ProxyPassReverse /shop/ http://localhost:8080/shop/另外,Windows Server上部署还要注意JDK环境变量是否配好。Tomcat启动脚本依赖JAVA_HOME,如果系统装了多个JDK版本,要确认用的是哪个。这些看起来都是小事,但实际部署时80%的问题是环境问题,不是代码问题。
4.3 高频问题排查速查表
做JavaWeb项目,几乎每个人都会遇到下面这些老问题。我把排查思路整理成一张表,遇到问题先照着查一遍,能省很多时间。
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
| 访问项目路径404 | 上下文路径或war包名不对 | 检查IDEA Deployment配置,或看Tomcat webapps下实际解压的目录名 |
| 页面报500,日志有ClassNotFoundException | 依赖冲突或没打包进去 | 检查Maven依赖里javax/jakarta是否混用,清理target后重新package |
| 数据库中文乱码 | 连接串编码或表字符集问题 | 连接串加characterEncoding=utf8,表用utf8mb4 |
| 驱动加载失败 | MySQL版本与驱动不匹配 | MySQL 8.x必须用com.mysql.cj.jdbc.Driver |
| 8080端口被占用 | 其他程序占用了Tomcat端口 | 用netstat -ano查占用进程,改Tomcat端口或杀掉进程 |
| 上传的图片重启后消失 | 文件存在项目部署目录被覆盖 | 改存服务器固定目录,配置虚拟路径映射 |
| Invalid date或时间格式异常 | 数据库时区问题 | 连接串加serverTimezone=Asia/Shanghai |
这里面我特别想强调第一项。404有相当一部分是IDEA的Artifact配置问题,或者war包没成功部署。你去看Tomcat的webapps目录,如果看不到解压后的项目文件夹,那问题出在部署环节,不用在代码里浪费时间。
至于端口占用,我常用的命令是netstat -ano | findstr 8080,查到进程PID后用tasklist /fi "pid eq 进程号"看是什么程序,确认是残留的Java进程就杀掉。
这个商品管理系统做完之后,你可以继续往上面加功能,比如加一个购物车模块,把订单关联上用户,或者把列表页的图标数量统计做成一个简单的后台仪表盘。每一步扩展都是对现有代码的一次重构练习。我个人做完这个项目最大的体会是,报错不可怕,真正让你进步的是学会读异常堆栈,学会根据现象定位问题所在的那一层。把这次部署中遇到的问题和解决办法记下来,下一回再做其他Web项目,你会发现自己比预想的要从容得多。
本文还有配套的精品资源,点击获取