
简介面向计算机专业期末大作业与课程设计的JavaWeb购物商城项目以完整源码MySQL数据库为核心提供用户注册登录、商品展示、购物车管理、订单生成与支付等核心功能可作为课程设计、期末大作业或项目实战的完整参考。压缩包共129个文件涵盖JSP页面、Java业务类、Servlet控制类、SQL数据库脚本、JAR依赖库及CSS/JS/图片等前端资源整体约19.29MB目录结构清晰便于导入开发环境并直接运行或二次开发。目前已有630人学习下载适合正在完成大作业或希望系统练习JavaWeb开发的学习者。资源中除源码外还附有项目配置与数据库文件可帮助快速搭建运行环境结合代码理解JDBC连接、Servlet请求处理、JSP页面渲染等关键流程对梳理前后台交互与分层设计思路很有帮助。1. 期末大作业JavaWeb购物商城先搞清楚这份源码值不值期末前一周手里摊着一份从网盘或实训平台拖下来的JavaWeb购物商城完整源码配一个Mysql数据库文件这就是大部分人期末大作业现场的实况。标题里“完整源码Mysql”最值钱的不是功能全而是它给了你一套能从前端JSP点到后端Servlet再落到Mysql表的完整链路让你不用从零造轮子。这套东西本质上就是JavaWeb全栈的最小闭环JSP负责页面Servlet负责入口Mysql负责落数据外加一堆不调通就让人三天睡不着的环境配置。下面按“结构怎么认→怎么跑起来→业务逻辑怎么写→坑在哪→验收前怎么自检”的顺序把这个方向讲透。想照着复现、想在老师面前把每个功能讲明白的人按这个路径走就能省下大把时间。2. 拆解一套能交差的JavaWeb商城三层架构与数据库四张核心表很多同学第一次打开源码时是懵的src下十几个包web目录里堆着上百个JSP不知道该从哪读起。我的经验是不要从第一个类开始读先看目录结构把代码的“骨架”认出来再沿着一条用户主线——注册、登录、浏览商品、加购物车、下单——逐个环节跟一遍。这类期末项目十有八九都是JSP Servlet JDBC的老三层写法少数上了SSM或Spring Boot但核心思路一样。下面先用三层结构把源码分层再用数据库脚本把业务表还原出来最后沿着一个请求把页面和后端串起来。2.1 三层架构怎么分Servlet、Service、DAO的包结构识别老三层项目最标准的包结构是下面这样打开IDEA左侧面板对照一下就能认出来src/ ├── com.example.bean // 实体类User、Product、CartItem、Order ├── com.example.dao // 数据库访问层JDBC写在这习惯叫XxxDao │ └── impl/ // 接口实现小项目不拆也行 ├── com.example.service // 业务逻辑层登录校验、购物车计算、下单事务 │ └── impl/ ├── com.example.web // Servlet控制层收请求、调service、跳JSP ├── com.example.util // 工具类DBUtil、MD5Util、UploadUtil └── com.example.filter // 过滤器登录校验、统一编码这四层里最关键的是职责划分。web层只做三件事拿参数、调service、根据结果转发或重定向。service层做业务规则比如“库存够不够”“用户名有没有重复”。dao层只写SQL返回结果集或影响行数。三层不是玄学是防老师现场提问的基本功当被问到“哪里要改”时你能指着service把理由说完整而不是支支吾吾。看代码时还有个实用技巧先打开servlet目录找到LoginServlet这类入口顺着它调的service再看dao一条链路读完整个项目就懂了一半。如果项目里出现一个BaseServlet说明作者在复用公共的转发逻辑这类写法答辩时拿出来讲是加分项。2.2 从Mysql脚本还原业务用户、商品、购物车、订单四张核心表数据库是这份项目里最容易翻车也最好拿分的地方。先说结论任何商城项目都能浓缩成四张核心表用户、商品、购物车、订单。你拿到的SQL脚本里如果缺了其中一类要么是标题夸大要么是作者把购物车塞进了Session没建表。一个能交差的建表脚本长这样CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARACTER SET utf8mb4; USE shop; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, nickname VARCHAR(50) DEFAULT , create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, pname VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 100, image VARCHAR(255) DEFAULT , description VARCHAR(500) DEFAULT , create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE cart_item ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, UNIQUE KEY uk_user_product (user_id, product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, user_id INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段脚本里的几个设计点答辩时能说出来很加分。金额列用DECIMAL(10,2)而不是FLOAT因为浮点类型在累加时会出现精度偏差涉及钱的列宁可多写几个字符也不能埋雷。购物车表加了(user_id, product_id)联合唯一键重复加购同一商品时就该走“数量加一”的更新逻辑而不是再插一行。表名用orders而不是order是因为ORDER是Mysql的保留字直接用会给查询语句埋坑。所有表强制InnoDB因为第4章要讲的下单事务只有这个引擎才真正生效。导入时有两条路用命令行执行mysqldump或source或者用Navicat、dbx这类图形工具直接运行SQL文件。命令行导入长这样mysql -uroot -p shop.sql执行成功的标志是控制台没有任何ERROR工具里刷出四张表。脚本里已经有CREATE DATABASE的可以直接跑没有的话先手动建库再把use语句换成你自己的库名。这里提醒一句导入前先备份尤其在别人脚本上二次开发时数据是没有后悔药的。2.3 JSP页面与后端交互一次完整请求是怎么流转的以登录为例把一次请求从头到尾走一遍。前端login.jsp里一个form把username和password提交到/login这个Servlet地址Servlet拿到参数后调UserService去查库查得到就放Session并重定向到首页查不到就转发回登录页并把错误提示带回去。核心代码长这样WebServlet(/login) public class LoginServlet extends HttpServlet { 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); UserService userService new UserService(); User user userService.login(username, password); if (user ! null) { req.getSession().setAttribute(loginUser, user); resp.sendRedirect(req.getContextPath() /index.jsp); // 重定向防止刷新重复提交 } else { req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); // 转发msg仍可取到 } } }这段代码有三处地方和期末验收直接相关。req.setCharacterEncoding必须放在getParameter之前才会对请求体生效写成后面就等于没写。登录成功后用sendRedirect而不是forward是为了避免用户在订单页按F5把下单动作重复提交一次这个点老师问到“为什么用重定向”时能答出“刷新防重复”就过关了。登录失败用forward因为要借request这个临时传递带msg属相给JSP页面展示地址栏不变才是正确行为。JSP那边接收错误提示用EL表达式语法比% %干净得多% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 % !DOCTYPE html html body h2登录/h2 form action${pageContext.request.contextPath}/login methodpost input nameusername placeholder用户名/ input namepassword typepassword placeholder密码/ button typesubmit登录/button /form p stylecolor:red${msg}/p /body /html这里有个小技巧值得记住action地址里的${pageContext.request.contextPath}会自动拼上当前项目的上下文路径。很多源码直接写死form action/login等项目路径一改表单提交就404新手会在这卡到怀疑人生。用EL表达式动态拼接换项目名、换端口都不用改页面。3. 用IDEA把源码跑起来JDK、Tomcat、Mysql 的配置落地有些同学拿到源码第一件事是找main方法双击看见满屏红色就慌。JavaWeb项目跑在Tomcat容器里不能当普通Java工程跑。这一节按我实际带课设时走的路径来先装好三个基础环境再导入工程最后把数据库连接打通。整个过程最花时间的往往不是改代码而是IDEA里那几项配置。3.1 环境版本怎么选JDK 8 Tomcat 8.5 Mysql 5.7/8.0 的稳妥组合老规矩先对齐版本期末阶段别盲目追新。JDK 1.8够用且教学资源最多Tomcat 8.5.xx对Servlet 3.1和JSP支持完整和IDEA配合也稳Mysql用5.7.44或8.0.x都行两者在连接串的参数上有一个关键差别后面会讲。给一套我常用的版本组合组件推荐版本说明JDK1.8jdk-8u202老项目兼容性最好教材里的代码都能跑Tomcat8.5.xx 或 9.0.xx8.5最稳9.0是主流但是小改动Mysql5.7.44 或 8.0.x5.7更省心8.0需要多配两个连接参数IDEA2021.1 的 Ultimate社区版不支持Tomcat应用服务器配置教育邮箱可免费数据库工具Navicat / dbx 等图形工具导脚本、改表结构比命令行直观这里必须点一个到位的配置认知所谓“IDEA运行JavaWeb项目配置”核心就三步——Project SDK选到JDK 1.8Tomcat Server里指定安装目录Deployment里加上Artifact并填好Application context。教程把这一步讲得再长本质都是这三件事。建议环境一次性装对Mysql 5.7.44在Windows上的安装流程网上一搜一大把注意初始化时记好root密码后面所有配置都依赖它。3.2 导入源码后的五步配置数据库导入、连接串、依赖、Tomcat、启动第一步导入数据库。命令行执行上面那条mysql -uroot -p shop.sql或者打开dbx这类图形工具在连接上右键选“运行SQL文件”选中脚本执行。执行完刷新一下看到四张表就是成功。如果是别人给的脚本先确认里面有没有CREATE DATABASE没有就手动建一个空库再导入。第二步改数据库连接配置。这类项目一般有一个db.properties或jdbc.properties文件位置在src目录下jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456参数说明driver分两代com.mysql.jdbc.Driver是老驱动类名Mysql 8.0环境强烈建议换成com.mysql.cj.jdbc.Driverurl里的useSSLfalse用来关掉Mysql 8默认的SSL握手机制省掉一大串警告serverTimezoneAsia/Shanghai指定时区否则Mysql 8会直接报时区错误characterEncoding和useUnicode保证中文从数据库到页面不乱码。password改成本机实际的root密码这一步漏了后面全白干。第三步处理依赖。打开工程看根目录有没有pom.xml有就等IDEA右下角Maven把依赖拉完没有就把源码自带的lib目录加到项目里操作路径是File → Project Structure → Libraries → “” → Java选中lib目录确认。这里有个常见误用Maven工程依赖没拉全就急着启动启动报ClassNotFoundException时别怀疑代码先看Maven面板有没有红色报错。第四步配置Tomcat。IDEA里点Run → Edit Configurations → “” → Tomcat Server → LocalServer标签页把Tomcat安装目录选对切到Deployment标签点“”添加Artifact类型选war exploded底部的Application context改成/shop。这个上下文路径就是后续访问地址里的那一层填错了就是404第5章会细讲。第五步启动。点右上角的运行按钮观察Console输出看到Artifact is deployed successfully就是成功了。然后手动打开浏览器访问http://localhost:8080/shop/index.jsp不要用IDEA自动弹出的浏览器因为弹出的URL可能还带着默认的上下文路径。如果控制台报Port 8080 is already in use先看第5章的端口排查。3.3 最小链路验证注册→登录→商品列表跑通后别急着验收整棵功能树只验最小链路注册、登录、看商品列表。按这个顺序手动走一遍打开http://localhost:8080/shop/register.jsp填一个不重复的用户名和密码点提交。回到登录页输入刚注册的账号确认能进入首页并显示昵称。首页点进商品列表确认pname、price、stock三列都正常显示图片不裂开。顺手在数据库工具里看看user表和product表确认数据真的落库了。这步里最常暴露的是图片路径问题。很多源码把图片URL写死成uploads/1.jpg却没有把upload文件夹配成静态资源映射导致商品图全部裂图。常见做法是把图片直接丢到web目录或webapp目录下访问路径用相对项目根路径去拼。演示时出现一片裂图第一印象分就没了值得提前检查。process: The first sentence of section 3.3 says 跑通后别急着验收整棵功能树 — 口语化OK.4. 商城核心业务逻辑购物车、订单事务与后台增删改查代码全跑通只是第一步交作业时老师一定会挑一到两个业务点让你现场讲逻辑。购物车和订单是商城最常被抽查的地方后台管理主要看增删改查的规范程度。这一章把这三块拆开讲透每一段都按“实现代码→逻辑说明→参数怎么改”来写直接照着抄就能用。4.1 Session购物车还是数据库购物车期末项目选哪种先摆结论期末大作业选Session购物车原因很直接——不用额外建表代码量少演示时关闭浏览器重新打开还能借着Session解释“为什么购物车空了”这个现象。数据库购物车放进简历项目或毕设里更合适。两者的取舍用表格看更清楚对比项Session购物车数据库购物车cart_item表实现难度低一个HashMap搞定中需要读写表、联表查询数据持久性重启Tomcat就丢永久保存多设备同步不支持支持演示效果够用更接近真实电商答辩风险要能讲清Session生命周期要能讲清事务和锁Session购物车的核心代码很短加购操作就是把商品id存进Session里的Mapkey是商品idvalue是数量WebServlet(/cart/add) public class CartServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int productId Integer.parseInt(req.getParameter(productId)); HttpSession session req.getSession(); MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new HashMap(); // 第一次加购初始化空购物车 } cart.put(productId, cart.getOrDefault(productId, 0) 1); // 已有则数量加一 session.setAttribute(cart, cart); resp.sendRedirect(req.getContextPath() /cart.jsp); // 回到购物车页 } }逻辑说明getOrDefault是Java 8以后Map接口的默认方法当key不存在时返回0再1省掉了“先containsKey判断再分两路写”的啰嗦代码。整个购物车只存在当前用户的Session里不同用户互不影响。参数说明productId从前端链接的queryString里来所以购物车入口是个普通的a hrefcart/add?productId1。如果你想升级到数据库购物车就是把cart这个Map的读写换成对cart_item表的增删改查cart.jsp页面的展示逻辑基本不用动。4.2 订单模块的事务处理Mysql事务与代码异常配合老师最爱问的点就是这块“下单时扣库存、生成订单、清购物车中途失败了怎么办”答案就是数据库事务。典型的代码如下Connection conn DBUtil.getConnection(); String deductSql UPDATE product SET stock stock - ? WHERE id ? AND stock ?; String orderSql INSERT INTO orders (order_no, user_id, total_amount, status) VALUES (?,?,?,0); try { conn.setAutoCommit(false); // 关闭自动提交让多条SQL成为一组 try (PreparedStatement ps1 conn.prepareStatement(deductSql)) { ps1.setInt(1, 1); // 数量 ps1.setInt(2, productId); // 商品id ps1.setInt(3, 1); // 库存下限判断 int rows ps1.executeUpdate(); if (rows 0) { throw new RuntimeException(库存不足); // 主动抛异常触发回滚 } } try (PreparedStatement ps2 conn.prepareStatement(orderSql)) { String orderNo UUID.randomUUID().toString() .replace(-, ).substring(0, 20); // 生成简单订单号 ps2.setString(1, orderNo); ps2.setInt(2, userId); ps2.setBigDecimal(3, totalAmount); ps2.executeUpdate(); } conn.commit(); // 两条SQL都成功才提交 } catch (Exception e) { conn.rollback(); // 任何一步失败库存扣减回滚 throw new RuntimeException(下单失败, e); } finally { conn.setAutoCommit(true); DBUtil.close(conn); }这段代码里有三个关键点。setAutoCommit(false)把后面两条SQL包进同一个事务不写这行每条SQL各自提交第一条扣库存成功、第二条插订单失败时库存就白扣了。扣库存的SQL里带上了AND stock ?作用是防超卖库存不够时更新影响行数为0通过抛异常走回滚比先查库存再扣减更稳。第三是订单号用UUID生成去掉横线取前20位期末演示版够用别用自增id当订单号会显得很业余。注意事务只在InnoDB引擎下生效。如果建表脚本用的是MyISAM上面的commit和rollback全是空话前面第2章的建表脚本已经强制写了ENGINEInnoDB专门就是为了这一步。4.3 后台管理的通用CRUDBaseDao与PreparedStatement商城后台管理——商品增删改查、用户列表、订单列表——本质上就是一套通用CRUD。与其在十个Dao类里重复写Connection和PreparedStatement不如抽一个BaseDao。一个够期末用的版本长这样public class BaseDao { // 通用增删改insert/update/delete都走这里 public static int executeUpdate(String sql, Object... params) { try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i 0; i params.length; i) { ps.setObject(i 1, params[i]); // 从1开始对应SQL里的问号顺序 } return ps.executeUpdate(); } catch (Exception e) { throw new RuntimeException(e); } } // 通用查询配合RowMapper把结果集转成实体对象 public static T ListT queryList(String sql, RowMapperT mapper, Object... params) { ListT list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i 0; i params.length; i) { ps.setObject(i 1, params[i]); } ResultSet rs ps.executeQuery(); while (rs.next()) { list.add(mapper.map(rs)); } } catch (Exception e) { throw new RuntimeException(e); } return list; } }逻辑说明executeUpdate接收一条写好的SQL和不定长参数for循环把参数逐个set进去返回值是受影响的行数后台删除时判断rows 0就能给出成功提示。queryList用泛型和RowMapper接口解耦每一行ResultSet怎么转换成User还是Product由调用方决定。这里用PreparedStatement而不是拼接字符串是为了从根上防SQL注入答辩时如果老师问“怎么防注入”你能答出“参数化查询”就是得分点。实际使用示例查商品列表带排序public ListProduct findAll() { String sql SELECT * FROM product ORDER BY price ASC; return BaseDao.queryList(sql, rs - new Product( rs.getInt(id), rs.getString(pname), rs.getBigDecimal(price), rs.getInt(stock) )); }这里的ORDER BY是Mysql端排序不是查出全表后在Java内存里再排一遍。模糊搜索时把条件拼进SQL参数用%关键词%SELECT * FROM product WHERE pname LIKE ?然后ps.setString(1, % keyword %)。整套后台管理商品列表、用户列表、订单列表的写法就是同一个模板换SQL学会这一节后台部分你一下午能写完。5. JavaWeb项目常见避坑从数据库报错到404的五个翻车点写这一章时我脑子里全是带课设时真实发生过的翻车现场演示前数据库密码连不上、商品页中文变成问号、地址栏多出一个根本没见过的war_exploded后缀。这章把最常见的五类问题按“现象→原因→解决”整理出来验收前一条条对照排查。5.1 Mysql SSL连接错误与驱动版本不匹配现象启动项目或第一次查数据库时控制台报Communications link failure或者SSLHandshakeException有时还伴随NoClassDefFoundError: com/mysql/cj/jdbc/Driver。原因最常见的是三类叠加——连接串里没关SSLMysql 8默认开启SSL协议校验驱动Jar还停在5.1.4x这种老版本和Mysql 8的通信协议对不上驱动Jar放错了位置Maven项目里没加依赖lib项目里没把Jar放进WEB-INF/lib。解决先在db.properties的url末尾补useSSLfalseserverTimezoneAsia/Shanghai然后把驱动换成匹配的版本——Mysql 5.7用mysql-connector-java 5.1.49足够Mysql 8.0请换成8.0.x的驱动二者二选一别混着引入。最后确认Jar位置lib工程必须放在WEB-INF/lib下Maven工程必须在pom.xml的dependencies里放错地方的现象很迷惑——编译不报错运行才报NoClassDefFoundError。这类错按“先补参数、再换版本、最后查位置”的顺序排查十分钟内能定位。5.2 中文乱码问题JSP页面、请求、Tomcat三处统一UTF-8现象页面中文显示成???或æ±å—这种火星文数据库里存进去的中文字段取出来全是问号。原因一句话只要是乱码就是编码不一致。Java源文件可能是GBKJSP的pageEncoding忘了写Tomcat连接器的URIEncoding没设UTF-8三处只要有一处掉链子就乱。解决从上到下统一成UTF-8。IDEA里File → Settings → File Encodings把Global Encoding、Project Encoding、Default encoding for properties files全改成UTF-8这一步治本。每个JSP开头都写好% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %这是页面侧声明。最省事的是加一个全局过滤器把请求和响应的编码统一兜住WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }逻辑说明过滤器拦截所有路径在请求进入Servlet之前强制请求体按UTF-8解码在响应离开之前强制响应按UTF-8编码这样不管Servlet里有没有手动写setCharacterEncoding都不会乱。注意chain.doFilter(req, resp)必须放在最后它才是把请求继续往下放行的动作。Tomcat 8.5以上URIEncoding默认就是UTF-8如果你用的是老Tomcat再去server.xml里改Connector标签的URIEncodingUTF-8。5.3 IDEA部署后404上下文路径与Artifact冲突现象Tomcat正常启动控制台没报错但浏览器访问http://localhost:8080/shop/login.jsp返回404。原因最典型的两个——IDEA里Application context填的是根路径/或者带了一串_war_exploded后缀Servlet的WebServlet注解路径里的字母大小写和前端表单提交的地址不一致/LoginServlet写成/loginservlet这种纠错能让你找一小时。解决打开Run → Edit Configurations找到当前Tomcat配置进Deployment标签底下Application context改成/shop确认后访问地址必须带这个前缀。如果部署名还是xxx_war_exploded在Artifact列表里把带exploded后缀的删掉重新添加或者干脆在context里手动覆盖。最后点Build → Rebuild Project清理一次target目录Maven项目经常因为增量编译没同步导致页面文件没进产物。排查路径不对时先在浏览器直接访问http://localhost:8080/能看到Tomcat首页说明容器正常问题就锁死在项目部署这一环上。5.4 Mysql 8.0的caching_sha2_password认证失败现象启动项目后第一次查库报Access denied for user rootlocalhost或者更明确的Authentication plugin caching_sha2_password cannot be loaded。原因Mysql 8.0把默认认证插件换成了caching_sha2_password老版本JDBC驱动和部分图形工具默认使用mysql_native_password协议两边对不上密码正确也登不进去。解决三个方案按场景选。方案一装Mysql 8时在安装向导里选“Legacy Authentication”一下就兼容老驱动。方案二已经在跑的库就把用户认证插件改回去ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;方案三不想改用户就在连接串最后追加allowPublicKeyRetrievaltrue允许JDBC直接拿公钥完成认证。这三条里最推荐第二个改完立刻见效且能顺带把你手里所有老项目的数据源全救活。改密码后记得同步更新db.properties里的password别把两边的密码改岔了。5.5 Tomcat端口占用与启动失败现象点启动按钮立刻报Address already in use: 8080或者上一秒还正常下一秒同一个Tomcat配置说端口被占用。原因8080被其它进程占用了典型是之前启动的Tomcat实例没关干净或者机器上装了别的Web服务、数据库组件恰好占用8080。Java生态里还有个常见巧合某些组件默认也监听8080两套一起跑必然冲突。解决先查端口占用Windows命令行执行netstat -ano | findstr :8080最后一列是PID拿到后在任务管理器里结束对应进程或者用taskkill /PID 你的PID /F强制结束。不想杀进程就改IDEA里的Tomcat配置HTTP port改成8081JMX port改成1099改完访问地址同步变成http://localhost:8081/shop/index.jsp。注意只改HTTP端口而不改JMX端口IDEA会提示JMX已经占用两个端口最好一起改。这条血泪经验来自一次演示前一晚Torncat没关干净第二天开场就翻车。6. 期末验收前最后一天功能自检清单与三个加分细节交作业不是把代码扔过去就结束老师大概率会让你现场点一遍功能。这一章给一张可以直接照着点的自检表再给三个不花时间但能拉开差距的小动作。6.1 按一条业务链路逐项自检检查项操作方式预期结果注册打开register.jsp提交新账号user表出现新记录无异常报错登录用新账号登录跳转首页且右上角显示昵称商品列表打开商品页商品名、价格、库存正常渲染图片不裂加购同一个商品点两次加购购物车中该商品数量为2下单点结算提交订单库存减1、订单表生成记录、购物车清空后台管理管理员登录改一个商品价格修改后商品页价格同步变化这张表里的每一项都是答辩现场最可能被要求“再做一遍”的操作。注册和下单务必亲手走两遍尤其下单要确认库存真的变了很多同学演示到这一步才发现事务没生效库存死活不减场面非常被动。6.2 三个不花时间但能加分的细节第一个是登录拦截。加一个Filter判断Session里有没有loginUser没有就重定向回登录页。代码就几行但演示时直接在地址栏敲购物车页被弹回登录页比讲十页PPT都有说服力。HttpSession session request.getSession(); User user (User) session.getAttribute(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(request, response);逻辑说明过滤器在请求进入受保护页面前先查Session没有登录用户直接重定向后面的doFilter不执行页面自然进不去。参数说明要给哪些路径加拦截在WebFilter的urlPatterns里控制比如/cart/*、/order/*避开index.jsp等公共页面。第二个是写一个简短的README或项目说明把启动顺序写清楚先导入sql再改db.properties然后用IDEA配置Tomcat最后访问哪个地址。老师不一定看但旁边同学的工程目录什么都没有你有这就是差别。第三个是把控制台日志打得清楚一点在Servlet的关键分支加System.out.println([登录] 成功 username username)这类输出。答辩时一边操作一边指向控制台老师跟着你的讲解走提问难度自然降下来。想再精致点把System.out换成JDK自带的Logger半小时能改完提espect比例很值。过了这么多轮期末我最大的教训不是代码写得不够多而是演示现场数据库临时连不上。后来我养成的习惯是验收前一天把Mysql账号还原成root/123456这种最简单组合IDEA里Tomcat路径和项目上下文全用稳定不变的README第一行写清启动顺序。这些事听起来很土但它们保证了你层层准备的功能能在老师面前一个不缺地按出来已经在课设里救过我太多次。希望帮到你。本文还有配套的精品资源点击获取