news 2026/9/27 0:15:45

JSP+Servlet网上购物系统课设:导入排错与答辩改造全攻略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JSP+Servlet网上购物系统课设:导入排错与答辩改造全攻略

简介:这是一份用于JavaWeb期末课程设计的网上在线购物系统源码,采用JSP+Servlet+Mysql经典技术栈,按MVC分层组织,适合计算机相关专业学生作为课程作业提交或阶段性入门参考。资源共2000个文件,压缩包33.8MB,主要包括484个html、436个css与342个js前端页面,77个java与77个class后端核心代码,43个jsp动态视图,43个jar依赖库,以及sql数据库脚本、xml配置、docx课程报告模板、png/gif图片素材等,结构完整,导入MyEclipse并配置数据库即可运行。系统覆盖首页商品展示、购物车、订单、用户管理、菜单与角色权限、商品分类等常见业务模块,并带有验证码工具类,适合练习Servlet + JSP + MySQL的联动开发。已有99人学习下载,代码无需修改,只需在说明文档中替换学号姓名等信息即可交付使用,也可作为深入阅读Servlet控制器与JSP页面的实用素材。

1. 网上在线购物系统课程设计:JSP+Servlet 这套老组合为什么还是期末首选

网上在线购物系统是 JavaWeb 期末课程设计里出现频率最高的题目之一,JSP+Servlet+MySQL 这套组合也几乎是这类项目的默认起点。当拿到一个名为“JavaWeb期末课程设计(JSP+Servlet)(mysql)-网上在线购物系统SSM+MyEclipse.rar”的资源包时,先别急着解压导入:你要判断包里到底是纯 JSP+Servlet 的三层结构,还是真的塞进了 SSM 框架,这个判断直接决定你今晚能不能把它跑起来。这篇笔记按一名老工程师的习惯,把解压、导入、改数据库、读业务代码、排错到答辩改造的完整路径整理出来,适合三类人:正在赶期末课程设计的学生、想拿购物系统练手的初级开发者、以及准备把课设写进简历的求职者。目标是让你花最短时间跑通,并在别人问“这里为什么这么写”时,不露怯。

2. 把 rar 里的网上购物系统导入 MyEclipse:环境版本搭配与首次启动清单

2.1 先别急着导入:解压后先确认工程类型与目录结构

大多数课程设计包解压出来有两种形态:一种是带.project、.classpath的完整 Eclipse 工程,可以直接 Import;另一种只有src和WebRoot两个裸文件夹,导入时要手工搭工程。无论哪种,先把 rar 解压到一个没有中文、没有空格的路径,例如D:\course\shop。老项目对路径里的中文和空格非常敏感,MyEclipse 发布时会把 workspace 路径写进配置文件,路径一特殊,后面就是一堆莫名其妙的问题。

# Windows 下用 7-Zip 或 WinRAR 解压,命令行可以这样 7z x "JavaWeb期末课程设计(JSP+Servlet)(mysql)-网上在线购物系统SSM+MyEclipse.rar" -oD:/course/shop # 解压后看顶层结构,判断工程形态 tree /F # Windows 自带 tree,/F 显示文件 ls -la # Linux/macOS 下用这个

解压完先找三样东西:.project和.classpath文件、Web 根目录是WebRoot还是src/main/webapp、WEB-INF/lib下有哪些 jar。这三样决定了你用 MyEclipse 还是 IDEA、用老式 Web Project 还是 Maven 结构。一个快速判断方法:打开WEB-INF/web.xml看头部,老工程是version="2.5",新一点的是3.0或3.1,这直接对应你本地 Tomcat 的版本范围。

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>OnlineShop</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>

这里version="3.1"的意思是工程按 Servlet 3.1 规范写,Tomcat 8.5 和 9.0 都支持;如果是2.5,则更适合 Tomcat 7,但放 Tomcat 8.5 里通常也能跑。真正要小心的反而是 Tomcat 10 及以上——它把javax.servlet换成了jakarta.servlet,很多课程设计代码没做迁移,导入后直接 ClassNotFound。这类老工程,老老实实用 Tomcat 8.5 最稳。

2.2 环境版本搭配:这张表能帮你少走一半弯路

网上购物系统的课程设计代码大多是七八年前的教学模板,环境版本太高反而跑不起来。我一般按下面这张表搭,能覆盖绝大多数 JSP+Servlet 老工程的启动需求:

组件推荐版本原因
JDK1.8(8u 以上补丁随意)老代码编译产物兼容性最好,切换成本最低
IDEMyEclipse 2014~2019 或 Eclipse EE标题点名 MyEclipse,老模板与它兼容性最高
Tomcat8.5 或 9.0不要用 10+,javax到jakarta迁移会卡住你
MySQL5.7 或 8.0两种版本都常见,连接串参数有差异
JDBC 驱动mysql-connector-java 5.1.49 对应 MySQL 5.7;8.0.x 对应 MySQL 8.0驱动和数据库版本不匹配会报各种玄学连接错误

先确认本机 JDK 是不是 8,命令如下。如果是 JDK 17 或 21,不是不能用,但老项目的反射、代理、Class.forName写法容易出现 IllegalAccess 问题,课程设计阶段没必要跟它较劲。

java -version # 期望看到 1.8.0_xxx,比如 java version "1.8.0_202"

提示:如果本机已经装了高版本 JDK,不用卸载,在 MyEclipse 的 Window → Preferences → Java → Installed JREs 里添加一个 JDK 8 的路径,再把项目的 Compiler 级别设为 1.8 就行。改这个比重装环境省事得多。

2.3 两种导入方式与构建路径修正

完整工程导入很简单:File → Import → Existing Projects into Workspace,选解压目录,MyEclipse 会自动识别.project。麻烦的是那种没有.project的裸目录,需要先新建一个 Dynamic Web Project,名字随意,然后把src和WebRoot里的内容覆盖进去。覆盖时注意WebRoot/WEB-INF/web.xml也要带过来,否则新建工程会生成一个空壳 web.xml,把页面配置丢了。

导入后最常遇到的问题是构建路径里的输出目录和实际编译输出不一致。典型.classpath里应该有下面这一行,指向 Web 应用的类输出目录:

<classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v8.5"/> <classpathentry kind="output" path="WebRoot/WEB-INF/classes"/> </classpath>

这里的output决定了编译后的.class文件去哪。如果它是build/classes,而实际 Web 应用读的是WebRoot/WEB-INF/classes,就会出现改完 Java 代码、重启 Tomcat、行为还是旧的“鬼打墙”现象。遇到这种,右键项目 → Properties → Java Build Path → 把 Default output folder 改回WebRoot/WEB-INF/classes,Project → Clean 一次,再重新发布。

2.4 配置 Tomcat、数据源与首次启动命令

MyEclipse 里先添加 Tomcat 运行时:Window → Preferences → Server → Runtime Environment → Add → 选 Apache Tomcat v8.5,指向本地 Tomcat 安装目录。项目右键 Properties → Targeted Runtimes,勾上这个 Tomcat。再把 Project Facets 里的 Dynamic Web Module 版本调到和 web.xml 一致,3.0 或 3.1 都可以。

正常开发用 Exploded Archive 方式部署,也就是把 WebRoot 目录直接映射到 Tomcat,这样改 JSP 不用反复打包。如果项目在 MyEclipse 里部署不上,可以用一种兜底做法:手动在 Tomcat 的server.xml里加 Context,把 Web 根目录指过去。

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="/OnlineShop" docBase="D:\course\shop\WebRoot" reloadable="true"/> </Host>

参数含义:path是访问路径,启动后通过http://localhost:8080/OnlineShop/访问;docBase指向工程的 Web 根目录;reloadable="true"表示类文件变更后自动重新加载,调试期开着方便,演示时可以关掉。启动顺序务必要记住:先启动 MySQL,再启动 Tomcat,否则项目首页一打开就报数据库连接失败,新手会误以为是代码问题。

如果你用的不是 MyEclipse 而是 IDEA,本质逻辑相同:Project Structure 里把WEB-INF/lib下的 jar 全部加入 Libraries,Artifacts 里确保这些 jar 被打进WEB-INF/lib,Server 选本地 Tomcat 8.5。跑通后如果老师要求交 war 包,MyEclipse 里右键项目 Export → WAR file,IDEA 里 Build → Build Artifacts → 选 war 即可。

3. MySQL 建库与连接串配置:购物系统跑通前必改的三处参数

3.1 建库脚本里的几个“默认值”:utf8mb4、InnoDB 与金额字段

网上购物系统的数据库通常有用户表、商品表、订单表、订单明细表、购物车表这几类。打开 SQL 脚本先别急着执行,看两处:表引擎是不是 InnoDB,字符集是不是 utf8mb4。老脚本常写ENGINE=MyISAM DEFAULT CHARSET=utf8,MyISAM 不支持事务,期末项目里下单流程很容易出现“订单插入了但库存没扣”的尴尬;utf8 和 utf8mb4 的区别则在商品名出现 emoji 或特殊符号时才会暴露。

CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE shop; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT '课程设计常用明文或MD5', nickname VARCHAR(50), role TINYINT DEFAULT 0 COMMENT '0-买家 1-管理员', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE t_product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, image VARCHAR(255), status TINYINT DEFAULT 1 COMMENT '1-上架 0-下架' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

参数说明:DECIMAL(10,2)是金额字段的标配,总长度 10 位、小数 2 位,最大能存 99999999.99,课程设计的订单金额完全够用。千万别用FLOAT或DOUBLE存钱,浮点数在二进制里表示不精确,累计金额时会出现 0.1 + 0.2 = 0.30000000000000004 这种答辩时说不清的问题。role TINYINT用 0/1 区分买家和管理员,比varchar存“admin/user”更省空间,这也是一个可以讲给老师听的细节。

3.2 初始账号从哪来:明文密码与 MD5 的翻车点

课程设计的 SQL 脚本通常会带一个管理员账号和几个测试商品。常见做法是用户名 admin、密码 123456,但这里有个高频陷阱:脚本里插的是明文密码,而 LoginServlet 比对时用了 MD5,导致你拿 admin/123456 永远登不进去。先看代码再插数据,顺序不能反。

INSERT INTO t_user (username, password, role) VALUES ('admin', '123456', 1); INSERT INTO t_user (username, password, role) VALUES ('test', '123456', 0); INSERT INTO t_product (name, price, stock) VALUES ('Java核心技术 卷I', 119.00, 50), ('Head First Java', 98.00, 80), ('深入理解Java虚拟机', 128.00, 30);

如果代码里UserDao.findByNameAndPwd()是拿用户输入的密码直接和数据库比对,那明文插入没问题;如果它先把密码做了一次 MD5 再比对,这里就要改成插入 MD5 后的值,也就是MD5('123456')的密文。判断方法很简单:在项目里搜MessageDigest或md5,搜得到就插密文,搜不到就插明文。顺便说一句,答辩时如果老师问“密码怎么是明文”,就回答“这是演示项目,真实生产环境要用加盐哈希存密码”,这比狡辩有用得多。

3.3 jdbc.properties 连接串参数:useSSL、时区与驱动类名

跑通项目的第二道关卡是数据库连接配置。这类工程一般把连接信息放在src/jdbc.properties或WEB-INF/classes下的db.properties里。打开后先改三个值:数据库 IP、用户名、密码,再看连接串参数够不够。

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true jdbc.username=root jdbc.password=你自己数据库的密码

参数逐个说:useUnicode=true&characterEncoding=utf8保证从 Java 到 MySQL 的中文不乱码;useSSL=false关掉 SSL 握手,少了证书相关的告警和报错;serverTimezone=Asia/Shanghai是 JDBC 8 驱动的必填项,不写会报 CST 时区无法识别;allowPublicKeyRetrieval=true是针对 MySQL 8 的caching_sha2_password认证插件,不写可能报 Public Key Retrieval is not allowed。驱动类名也有讲究:com.mysql.jdbc.Driver是 5.x 的老驱动,com.mysql.cj.jdbc.Driver是 8.x 的新驱动,别只改数据库不改驱动类。

改完先在命令行验证一遍数据库账号密码,把配置问题挡在启动之前:

mysql -uroot -p USE shop; SELECT DATABASE();

如果这里能进库,再回 MyEclipse 里启动 Tomcat,登录页调接口时才不会报Communications link failure。MySQL 8 安装时如果选的是默认认证插件,还要注意项目驱动必须是 8.x;用 5.1.49 的驱动连 MySQL 8,大概率连接闪断。

3.4 DAO 层两种拿连接的方式:DriverManager 与连接池

课程设计里的数据库工具类一般长这样:静态块读 properties,然后每次DriverManager.getConnection()。这是老项目最普遍的写法,简单、直观,但有个明显的短板——每次请求都新建一个物理连接,高并发下连接数直接打满。

public class JdbcUtils { private static String url; private static String user; private static String password; static { // 常见做法:Properties 加载 jdbc.properties 并赋值 // 有的工程把这段直接写在每个 Dao 里,代码会很冗余 } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } }

如果包里的 lib 目录下有commons-dbcp、commons-pool这类 jar,说明作者用了连接池。连接池的核心思路是启动时创建一批连接放在池里,用完归还而不是关闭,避免频繁 TCP 握手。答辩时能说清“为什么不用 DriverManager 而用连接池”,是一个实打实的加分点。但要注意:用连接池就必须在配置里写maxTotal、maxIdle、initialSize这些参数,否则默认值可能只有 8 个连接,商品列表一刷新就卡住,这又是另一类坑。

4. 读透 JSP+Servlet 代码结构:三层架构、购物车流程与 SSM 识别

4.1 从 web.xml 和 Servlet 映射读项目入口

读任何 JavaWeb 老工程,第一步永远是web.xml,不是index.jsp。web.xml里写着项目怎么被装配起来:监听器、过滤器、Servlet 映射、欢迎页。网上购物系统的入口一般是index.jsp,跳转到商品列表,但真正的逻辑入口是LoginServlet或IndexServlet这种类。看<servlet-mapping>就能知道哪个 URL 对应哪个类。

<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.shop.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>

这里的/login是虚拟路径,JSP 里表单的action必须拼上项目上下文路径,常见写法是${pageContext.request.contextPath}/login。如果写成固定的/login,项目部署名一旦不是根路径,立刻 404。这类工程里还有一种写法是给 Servlet 加@WebServlet("/login")注解而不写在 web.xml 里,两种都存在。先全局搜@WebServlet,搜到一大片说明它走注解路线,web.xml 里反而没有 Servlet 映射,别找半天以为自己下错了包。

4.2 登录流程里的 Servlet 生命周期:init 到 destroy

Servlet 生命周期是 JavaWeb 的经典考点,网上购物系统里最容易对照它讲的就是登录流程:Tomcat 启动时或首次访问时加载 Servlet 类、实例化、调用init(),之后每个请求由service()方法分发到doGet()或doPost(),容器关闭时调用destroy()。课程设计里,init()最常见的用途是读取一次全局配置,destroy()大多不重写。

@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override public void init() throws ServletException { // 常见做法:在这里读一次配置,比如验证方式、分页大小 System.out.println("LoginServlet init"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); // 常见做法:调 Dao 查询用户,判断账号密码 User user = new UserDao().findByNameAndPwd(username, password); if (user != null) { req.getSession().setAttribute("loginUser", user); resp.sendRedirect(req.getContextPath() + "/index"); } else { req.setAttribute("msg", "用户名或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } }

重点看登录成功和失败走了两条不同的路:失败时setAttribute("msg", ...)然后forward到 login.jsp,地址栏不变,request作用域里的msg能在 JSP 里用${msg}取到;成功时用sendRedirect重定向到首页,浏览器会重新发起一次请求。重定向和转发的区别是答辩最爱问的:转发是服务器内部跳转,地址栏不变,request里的数据还活着;重定向是服务器告诉浏览器“你去另一个地址”,地址栏变化,要传数据就得挂到 session 或 URL 上。很多学生在这里答混,其实就是没把这两个动作和登录代码对应起来。

4.3 购物车与下单的事务边界:同一个 Connection 才是关键

网上购物系统的核心业务是购物车和下单。购物车最常见的实现是 session 里放一个HashMap<Product, Integer>,key 是商品对象或商品 ID,value 是数量。为什么放 session 而不是放在某个 Servlet 的成员变量里?因为 session 是每个用户一份,Servlet 实例是全局共享的,把购物车放成成员变量,所有用户就共用一个购物车了——这是典型的并发问题。

下单流程则要抓住“事务”两个字。一个订单的落地包含扣库存、插入订单表、插入订单明细表,这三步必须用同一个Connection,要么全成功,要么全失败。

Connection conn = null; try { conn = JdbcUtils.getConnection(); conn.setAutoCommit(false); // 关掉自动提交,手动控制事务 // 常见做法:扣库存,受影响行数为 0 说明库存不足 int rows = productDao.deductStock(conn, productId, num); if (rows == 0) { throw new RuntimeException("库存不足"); } int orderId = orderDao.insertOrder(conn, userId, totalPrice); orderDao.insertOrderItem(conn, orderId, productId, num, price); conn.commit(); // 三步都成功,一起提交 } catch (Exception e) { if (conn != null) conn.rollback(); // 任何一步失败,全部回滚 throw e; } finally { if (conn != null) conn.close(); }

扣库存的 SQL 常见写法是UPDATE t_product SET stock = stock - ? WHERE id = ? AND stock >= ?,这里把“库存够不够”的判断直接写进 SQL 的条件里,用受影响行数来判断,比先 SELECT 再 UPDATE 更可靠。因为并发情况下,两个请求都查到了库存 5,都去扣 3,最后库存变负数;而用这条 SQL,数据库行锁会保证只有一个请求能受影响,另一个受影响行数为 0,直接提示库存不足。这个细节讲出来,老师会认为你真正理解了并发。

4.4 明明叫 SSM 却没有 Spring:怎么识别框架真假

标题里挂着 SSM,但这类资源包经常只是命名需要,代码里还是纯 JSP+Servlet+JDBC。识别方法很直接:看WEB-INF/lib里有没有spring-webmvc、mybatis相关的 jar,看有没有applicationContext.xml或spring-mvc.xml配置文件,看web.xml里有没有ContextLoaderListener。

# 项目根目录执行,看 lib 里有没有 spring 和 mybatis ls WebRoot/WEB-INF/lib | grep -i -E "spring|mybatis" # 找框架配置文件 find . -name "applicationContext*.xml" -o -name "spring-mvc*.xml"

两条命令都没输出,说明这就是个 JSP+Servlet 工程,文件名里的 SSM 是“包装”。这反而省事:课程设计的评分重点在三层架构和业务逻辑,不是框架注解。如果包里真的有 Spring 和 MyBatis,就要把阅读重点放在service层和mapper文件上,看@Service、@Autowired、@Repository这些注解怎么把对象串起来。无论哪种情况,我的建议都一样:先把 JSP+Servlet 这条线读透,再去看框架,顺序反了会觉得每个文件都是黑匣子,越看越慌。

5. JavaWeb 购物系统跑不起来的排查笔记:启动报错、中文乱码与数据库连接

这一章的每一条都是我自己在学生项目里见过、也亲手填过的坑,按“现象 → 原因 → 解决”写。遇到问题先不要瞎改代码,按下面五类对照排查,效率高得多。

5.1 启动 500 与 ClassNotFoundException:构建路径丢 jar 的三角关系

现象:Tomcat 启动时控制台不报错,浏览器访问首页直接 HTTP 500,日志里出现ClassNotFoundException: com.mysql.jdbc.Driver或org.springframework.web.context.ContextLoaderListener。

原因:工程里 lib 的 jar 没有进入最终的WEB-INF/lib。MyEclipse 导入工程后,Java Build Path 里能看到 jar,但 Deployment Assembly(发布配置)没把它们带上,发布到 Tomcat 时 jar 根本没被拷贝过去。还有一种情况是 jar 确实在工程目录里,但被误放到了src下而不是WebRoot/WEB-INF/lib。

解决:右键项目 → Properties → Deployment Assembly → Add → Java Build Path Entries,把 web 项目相关的 jar 全部加进去。更省事的兜底办法是把WEB-INF/lib需要的 jar 物理复制到WebRoot/WEB-INF/lib下,然后 Project → Clean,再在 Servers 视图里右键 Tomcat → Clean → Start。看日志的位置也要注意:MyEclipse 控制台和 Tomcat 的logs目录不一定同步,以 Tomcat 安装目录下的logs/catalina.out为准。

# Windows 下实时看 Tomcat 日志 tail -f D:/apache-tomcat-8.5.xx/logs/catalina.out

5.2 数据库连接失败:error 2002、时区与驱动版本的三步排查

现象:项目启动正常,一登录或一打开商品列表就报Communications link failure,页面上直接 500;命令行里可能还有ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'。

原因:三层都可能出问题:MySQL 服务没启动、端口不通、账号密码错误,或 JDBC 驱动和 MySQL 版本不匹配。尤其是 MySQL 8 配老驱动、连接串没写serverTimezone、没写allowPublicKeyRetrieval=true,都会报一堆不容易读懂的连接异常。error 2002这种带 socket 路径的报错,常见于本地通过 socket 连 MySQL,但服务没起来或my.cnf里 socket 路径不一致。

解决:按顺序排查,别一上来就改代码。先命令行验证服务和账号,再验证端口,最后才改连接串。

# 第一步:确认 MySQL 服务在跑,账号能登录 mysql -uroot -p # 第二步:确认 3306 端口可访问 telnet 127.0.0.1 3306 # 或 Windows 下用 PowerShell 的 Test-NetConnection 127.0.0.1 -Port 3306 # 第三步:查端口被谁占用 netstat -ano | findstr :3306

如果命令行能登录而 Java 连不上,重点查jdbc.url里的serverTimezone和驱动类名,改法在第 3.3 节,直接照抄参数即可。

5.3 中文乱码的三层统一:页面编码、Filter 与连接串

现象:登录用户名是中文时查不到用户;页面显示中文全是问号;数据库表里中文变成??或乱码。

原因:编码不一致发生在三层:JSP 页面本身的编码、Servlet 读写请求的编码、数据库连接串和表结构的字符集。最常见的是 JSP 页面用UTF-8,Servlet 里没设req.setCharacterEncoding("UTF-8"),Tomcat 8 默认的请求编码还是ISO-8859-1,中文参数一读到就乱。

解决:最省事且覆盖面最广的做法是写一个过滤器,统一处理全部请求的编码,然后在web.xml里注册并映射到/*。注意/*会把静态资源也过滤掉,但setCharacterEncoding对静态资源无副作用,放心用。

public class CharacterEncodingFilter implements Filter { private String encoding = "UTF-8"; @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(encoding); resp.setCharacterEncoding(encoding); chain.doFilter(req, resp); } }
<filter> <filter-name>encodingFilter</filter-name> <filter-class>com.shop.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

过滤器加完还乱的话,用命令行直接看原始表数据:SELECT id, username FROM t_user;。命令行显示正常而页面乱码,问题在 JSP 响应编码或浏览器解析;命令行本身是??,问题在连接串的characterEncoding=utf8或表字符集不是 utf8mb4。按这个思路定位,比到处试编码要快得多。

注意:JSP 页面文件本身还要有<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>,这三处配置各管一段,少一个都可能翻车。

5.4 静态资源 404 排查:CSS/图片全挂时先加 base 标签

现象:首页打开正常,点进商品详情页后 CSS、图片、JS 全部 404,页面纯文字裸奔,浏览器 F12 里全是Failed to load resource。

原因:JSP 里引资源用的是相对路径,比如<link href="css/style.css">。当浏览器地址从/OnlineShop/index.jsp变成/OnlineShop/product/detail?id=1时,相对路径解析的基准变了,它会在product这个虚拟目录下找css/style.css,自然找不到。这不是后端问题,是前端路径解析问题,跟页面写的对不对没关系。

解决:在所有 JSP 的<head>里加一行<base>标签,把基准路径固定到项目根:

<base href="${pageContext.request.contextPath}/">

加了之后,页面里所有的相对路径都会以项目根为基准去解析,商品详情页也能正确加载 CSS。如果你不想动每个 JSP,也可以把所有静态资源引用改成${pageContext.request.contextPath}/css/style.css这种绝对上下文路径,但改动量大,base标签是最小改动方案。

5.5 端口占用与发布“鬼打墙”:Tomcat 重启不干净的两种典型症状

现象:Tomcat 启动报Address already in use: JVM_Bind,或者代码改了、Tomcat 也重启了,但页面行为还是旧代码。

原因:前一种是上一个 Tomcat 进程没被杀干净,尤其 MyEclipse 里多次重复启动时,后台残留 Java 进程占着 8080 端口。后一种叫“发布没生效”,常见于修改了 Java 类但没有重新 Publish,Tomcat 加载的还是WEB-INF/classes里的旧 class,改 JSP 不用重启,改 Java 必须重新编译发布。

解决:先用命令找到占用端口的进程,杀掉,再在 MyEclipse 里右键 Tomcat → Clean 后重新发布。

# 找到占用 8080 的 PID netstat -ano | findstr :8080 # 示例输出里最后一列是 PID,比如 12345,然后杀掉 taskkill /PID 12345 /F

杀完进程后在 MyEclipse 的 Servers 视图里右键 Tomcat → Clean,再右键项目 → Clean,最后 Start。这个顺序能避免 90% 的“我明明改对了为什么还报错”类问题。血泪经验:先 Clean 再启动,这个习惯能让你少怀疑好几天人生。

6. 把课程设计改造成能讲清楚的项目:拦截器、SQL 验证与演示脚本

6.1 登录拦截 Filter:十分钟加一层“守卫”

课程设计最常见的减分项是“所有页面都能直接访问,不登录也能看订单”。加一个登录校验过滤器,让项目看起来像个真正有权限控制的系统。核心逻辑:从 session 里找登录用户,找不到就重定向到登录页。关键是要把登录页和登录请求本身放行,否则会形成重定向死循环。

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 loginUser = request.getSession().getAttribute("loginUser"); String uri = request.getRequestURI(); // 放行登录页、登录接口和静态资源,避免重定向死循环 if (uri.endsWith("/login.jsp") || uri.endsWith("/login") || uri.contains("/css/") || uri.contains("/images/")) { chain.doFilter(req, resp); return; } if (loginUser == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); } }

6.2 用 MySQL general_log 验证你的 SQL

答辩时讲“下单流程如何扣库存”,光靠嘴说没有信服力。把 MySQL 的 general log 打开,演示时一边操作页面一边让老师看真实的 SQL 执行记录,比任何流程图都直接。这种方式会把所有 SQL 都打出来,演示完记得关掉,避免日志一直膨胀。

SET GLOBAL general_log = ON; SET GLOBAL log_output = 'TABLE'; SELECT event_time, argument FROM mysql.general_log ORDER BY event_time DESC LIMIT 20;

演示时先清空一条商品库存,再走一遍下单,回查这条 SQL 就能看到UPDATE t_product SET stock = stock - ? WHERE id = ? AND stock >= ?的执行记录。对照代码讲“受影响行数为 0 就回滚”,这条链路完整且经得起追问。

6.3 答辩演练:一条下单链路的三句话

提前准备一条演示脚本,顺序建议是:更新库存 → 登录 → 加购 → 下单 → 回查数据库。操作到下单完成时,用下面两条 SQL 验证数据落库情况:

-- 看库存变化 SELECT id, stock FROM t_product WHERE id = 1; -- 看订单和明细 SELECT id, total_price, create_time FROM t_order WHERE user_id = 1 ORDER BY id DESC;

按这个顺序讲,三句话就能说清:登录后 session 里放入了用户信息;购物车在 session 里以商品 ID 为 key 计数;下单时同一个事务里扣库存、插订单、写明细。我当年做课设最大的教训是把时间花在了纠结“要不要把 JSP 改成 SSM”上,最后真正答辩时被问住的却是“订单和商品怎么关联”。现在拿到任何 JavaWeb 课程设计包,我的习惯都是先跑通、再看 web.xml 和建表 SQL、最后读业务代码,这个顺序不能反。希望你今晚跑通这套网上购物系统时,每一张表、每一个跳转都能讲得明明白白,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 0:11:21

如何在3分钟内给React项目嵌入Web终端:wterm快速上手教程

如何在3分钟内给React项目嵌入Web终端&#xff1a;wterm快速上手教程 【免费下载链接】wterm A terminal emulator for the web 项目地址: https://gitcode.com/gh_mirrors/wterm1/wterm wterm 是一款面向浏览器的 Web 终端模拟器&#xff08;terminal emulator for the…

作者头像 李华
网站建设 2026/9/27 0:10:10

2026桌面端开发框架避坑指南:Electron/Qt/WPF/WinUI实战故障域解析

1. 这份指南不是“选哪个框架最好”&#xff0c;而是帮你避开三年后才踩到的坑桌面端开发框架——这个词最近半年在技术社区的讨论热度翻了三倍。不是因为新框架爆发&#xff0c;恰恰相反&#xff1a;Electron、Qt、WinUI 3、WPF 这四套主力方案&#xff0c;各自都走到了一个临…

作者头像 李华
网站建设 2026/9/26 23:56:29

AI 智能体真正上岗前,企业为什么要先重修基础设施?

一篇关于麦肯锡《Reimagining tech infrastructure for (and with) agentic AI》的深度解读&#xff1a;从试点与规模化的落差&#xff0c;谈到执行架构、成本账本和落地顺序。 一个 AI 智能体能回答“服务器为什么报警”&#xff0c;不代表它能安全地处理一次生产事故。 回答…

作者头像 李华
网站建设 2026/9/26 23:55:44

DeskcommCRM实践:统一客户管理与通信留痕的坐席工作台设计

1. 为什么做DeskcommCRM&#xff1a;客户信息不该散落在Excel和个人微信里1.1 坐席场景下的三大痛点DeskcommCRM这个项目名字&#xff0c;拆开来看是Desk&#xff08;工作台&#xff09; Comm&#xff08;通信&#xff09; CRM&#xff08;客户关系管理&#xff09;&#xff0c…

作者头像 李华
网站建设 2026/9/26 23:54:14

Spark+Flume+Kafka+HBase实时日志处理系统搭建与调优实践

简介&#xff1a;面向计算机、人工智能、通信工程及数据科学等相关专业学生与开发者&#xff0c;这套基于SparkFlumeKafkaHBase的实时日志处理分析系统&#xff0c;完整实现了从日志模拟生成、Flume实时采集、Kafka缓存中转、Spark流式处理&#xff0c;到HBase存储与网页可视化…

作者头像 李华