ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

JavaEE商城系统实战:Servlet+JSP毕业设计全链路解析

JavaEE商城系统实战:Servlet+JSP毕业设计全链路解析 简介这是一套面向计算机专业本科生的JavaEE毕业设计实战项目完整实现了一个功能完备的网上购物商城系统涵盖用户注册登录、商品浏览、购物车管理、订单生成与支付模拟等核心电商模块适用于课程设计、毕设开题及JavaWeb技术栈综合实践。资源包共1749个文件包含111个Java后端业务逻辑与Servlet代码、156个XML配置文件含Spring与MyBatis配置、10个SQL建表与初始化脚本、600个JS前端交互脚本以及Vue、TS、HTML、CSS等前后端协同开发所需资源整体压缩包大小为78.07MB。已有698人学习下载项目已通过导师评审并获高分源码结构清晰、注释完整、数据库可一键导入下载解压后即可部署运行无需额外修改显著降低毕设实施门槛与调试成本。1. 这不是“又一个商城Demo”而是一套能跑通毕业答辩、经得起老师现场提问的JavaEE实战系统你搜到这个压缩包时大概率正被导师催着交开题报告或者卡在“系统怎么连数据库”“JSP页面总报404”“Tomcat启动后访问不了首页”这些具体问题里。标题里的“JavaEE”三个字不是指某个框架而是指一套完整的技术栈组合Servlet处理请求、JSP渲染页面、JDBC操作MySQL、JSTL简化标签、Filter做登录拦截、Listener监听会话——它代表的是2010年代高校教学体系里最扎实、最贴近企业早期架构的Web开发范式。我带过六届毕业设计每年都有学生拿着Spring Boot项目去答辩结果被问“你的事务是怎么传播的”“AOP切面在哪个类加载时机生效”就卡壳而用这套JavaEE老派架构的同学反而因为每个环节都亲手写过Filter链、手写过DAO层SQL拼接、调试过web.xml的URL映射顺序答辩时能指着代码说清楚“为什么这里要用request.setAttribute而不是session.setAttribute”。它不炫技但每行代码背后都有明确的工程意图比如商品列表页的分页不是靠MyBatis插件自动完成而是用PageBean封装offset和limit参数再在SQL里手动拼接LIMIT ? , ? ——这种写法在企业里早被淘汰但在毕业设计场景下它让你真正理解分页的本质是数据库查询的物理偏移而不是框架黑盒里的魔法。压缩包里的.sql文件不是随便导出的空表结构而是包含测试数据的完整初始化脚本用户表有admin/test两个预置账号商品表有12条带图片路径的真实商品订单表预留了三种状态待付款/已发货/已完成的流转逻辑。这意味着你解压后不用改一行代码就能看到登录页→首页→商品详情→加入购物车→提交订单的全链路这比那些“启动成功但点任何按钮都报500”的源码强十倍。2. 系统架构拆解为什么坚持用ServletJSP而非Spring Boot2.1 技术选型背后的教学逻辑与工程现实高校课程体系里JavaEE通常作为《Java Web程序设计》或《软件工程实践》的承载体它的核心价值不是追求技术先进性而是建立对Web请求生命周期的肌肉记忆。当你用HttpServlet重写doGet()方法时你必须手动解析request.getParameter(username)这强迫你理解GET请求参数如何通过URL编码传递当你在web.xml里配置servlet-mapping时你得搞清容器如何根据URL路径匹配到具体的Servlet类——这种“低效”的手动配置恰恰是理解MVC模式中Controller角色本质的必经之路。反观Spring Boot的RestController一个注解就屏蔽了所有底层细节学生能快速做出界面但当老师问“如果我要在所有请求前统一记录IP地址该在哪里加逻辑”很多人会懵是写Interceptor还是Filter还是AOP而在这套JavaEE系统里答案直接写在LoginFilter.java里chain.doFilter(request, response)之前调用request.getRemoteAddr()逻辑清晰可见。数据库层采用原生JDBC而非Hibernate同样出于教学考量。你看ProductDAO.java里的public ListProduct findAll(int offset, int limit)方法里面是标准的PreparedStatement ps conn.prepareStatement(SELECT * FROM product LIMIT ?, ?); ps.setInt(1, offset); ps.setInt(2, limit);——这种写法暴露了SQL注入防护的关键预编译语句如何将参数与SQL结构分离。而Hibernate的HQL或Criteria API虽然安全但学生容易误以为“只要用了框架就不会有SQL注入”直到某次答辩被要求手写一条带参数的UPDATE语句才意识到问题。2.2 模块化设计从MVC到真实业务边界的映射整个系统严格遵循经典MVC分层但每一层都嵌入了毕业设计特有的验证点。Model层不只是实体类User.java里有Override public String toString()方法这是为调试时打印日志准备的OrderItem.java里private double totalPrice;字段的getter方法做了四舍五入处理return Math.round(totalPrice * 100) / 100.0;避免浮点数精度问题在订单金额上暴露——这种细节常被学生忽略却在答辩时成为加分项。View层的JSP页面刻意保留了原始HTML结构比如index.jsp里商品列表用c:forEach items${productList} varp遍历但每个img src${p.imagePath}的路径是硬编码在数据库里的相对路径如/images/shoe1.jpg这要求你在部署时必须把images文件夹放在Tomcat的webapps目录下否则图片404。这个“麻烦”恰恰是考察你是否理解Web应用的静态资源加载机制。Controller层的Servlet则承担了业务逻辑的胶水作用CartServlet.java处理添加购物车请求时先调用CartService.addProduct()更新内存中的购物车对象再用response.sendRedirect(cart.jsp)跳转——这里没有用Ajax局部刷新因为毕业设计更看重流程完整性而非用户体验优化。特别值得注意的是OrderServlet.java里的事务控制它用conn.setAutoCommit(false)开启事务执行插入订单主表、插入订单明细表、更新商品库存三步操作任一失败则conn.rollback()。这段代码虽不如Spring的Transactional优雅但你能清晰看到ACID特性在代码中的具象化表达答辩时老师追问“如果第三步更新库存失败前两步数据怎么回滚”你可以指着rollback()那行代码回答。2.3 数据库设计从ER图到可运行脚本的落地细节shopping_mall.sql文件不是简单的CREATE TABLE集合而是包含完整约束和测试数据的生产级脚本。以用户表为例CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL UNIQUE, password varchar(100) NOT NULL COMMENT BCRYPT加密后的密码, email varchar(100) DEFAULT NULL, phone varchar(20) DEFAULT NULL, role enum(USER,ADMIN) DEFAULT USER, status tinyint(1) DEFAULT 1 COMMENT 1-启用,0-禁用, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意password字段的COMMENT注明了BCRYPT加密这意味着RegisterServlet.java里必然有BCrypt.hashpw(password, BCrypt.gensalt())调用role字段用ENUM而非INT既保证数据一致性又让UserDAO.findByUsername()返回的对象可以直接用user.getRole().equals(ADMIN)做权限判断。商品表的category_id外键关联分类表而分类表本身有自关联结构parent_id字段支持多级分类如“手机”→“iPhone”→“iPhone 15”。最值得深挖的是订单表设计CREATE TABLE order_info ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(50) NOT NULL UNIQUE COMMENT 订单号格式ORD202405010001, user_id int(11) NOT NULL, total_amount decimal(10,2) NOT NULL, status tinyint(1) DEFAULT 0 COMMENT 0-待付款,1-已付款,2-已发货,3-已完成,4-已取消, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里order_no的生成逻辑在OrderService.generateOrderNo()里实现ORD new SimpleDateFormat(yyyyMMdd).format(new Date()) String.format(%04d, counter)确保订单号具备时间可读性和唯一性两个索引idx_user_id和idx_order_no则是为高频查询用户查自己订单、后台按订单号查单做的性能优化。这些设计细节正是答辩时老师追问“为什么订单号不用UUID”“为什么status用tinyint不用varchar”的底气来源。3. 开发环境配置实操VSCodeTomcatMySQL的零坑搭建3.1 VSCode配置JavaEE环境绕过IDEA的“舒适陷阱”很多学生习惯用IDEA但VSCode配置JavaEE反而更能暴露底层依赖关系。第一步安装Extension Pack for Java插件包重点确认其中Language Support for Java(TM) by Red Hat已启用——这是提供JSP语法高亮和Servlet类自动补全的核心。第二步配置Java Runtime在VSCode设置里搜索java.home指向你本地的JDK 8必须是8因为JavaEE 7规范要求JDK 8兼容性JDK 11会导致javax.servlet.*包找不到。第三步关键配置Tomcat服务器。下载Apache Tomcat 8.5.x不要用9或10它们默认启用HTTP/2且Servlet API版本不匹配解压后在VSCode中安装Tomcat for Java插件点击侧边栏Tomcat图标选择Add Tomcat Server浏览到Tomcat解压目录的conf文件夹。此时VSCode会自动生成server.xml配置但你需要手动修改Connector port8080 protocolHTTP/1.1/节点添加URIEncodingUTF-8属性否则中文参数如商品搜索关键词会乱码。最后一步将项目文件夹拖入VSCode工作区右键项目根目录选择Configure Workspace在.vscode/settings.json里添加{ java.configuration.updateBuildConfiguration: interactive, tomcat.server.runtime: /path/to/your/tomcat }这样配置后右键web.xml选择Run on ServerVSCode会自动部署到Tomcat并打开浏览器。相比IDEA的一键部署VSCode的每一步都需要你明确指定路径和参数这正是理解“Web应用如何被容器加载”的最佳训练。3.2 MySQL数据库初始化从.sql文件到可连接实例压缩包里的shopping_mall.sql需要手动导入而非依赖工具一键执行。首先确保MySQL服务已启动Windows下用services.msc检查MySQL80服务状态macOS用brew services start mysql。然后打开命令行进入MySQL安装目录的bin文件夹执行mysql -u root -p输入密码后进入MySQL命令行创建数据库并指定字符集CREATE DATABASE shopping_mall CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE shopping_mall; SOURCE /path/to/shopping_mall.sql;注意SOURCE命令的路径必须是绝对路径且.sql文件里不能有CREATE DATABASE语句否则会报错所以原始脚本里应删除首行的CREATE DATABASE只保留建表和INSERT语句。导入完成后验证数据SELECT COUNT(*) FROM user; -- 应返回2admin和test SELECT * FROM product WHERE id1; -- 检查第一条商品数据如果出现ERROR 1366 (HY000): Incorrect string value错误说明.sql文件保存编码不是UTF-8需用Notepad另存为UTF-8无BOM格式。数据库连接配置在src/db/DBUtil.java里private static final String URL jdbc:mysql://localhost:3306/shopping_mall?useUnicodetruecharacterEncodingUTF-8serverTimezoneGMT%2B8;这里serverTimezoneGMT%2B8是关键避免JDBC驱动因时区问题报错。实测发现如果MySQL服务器时区设为SYSTEM而JVM默认时区是Asia/Shanghai不加此参数会导致java.sql.SQLException: The server time zone value ... is unrecognized。3.3 项目部署与调试定位404/500错误的黄金三步法部署后最常见的问题是首页404或登录页500。我的调试流程是第一步检查Tomcat日志。打开tomcat/logs/catalina.out搜索SEVERE关键字。如果看到java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet说明JDK版本不对用了JDK 11如果看到java.lang.NoClassDefFoundError: com/mysql/jdbc/Driver说明mysql-connector-java-5.1.47.jar没放进WEB-INF/lib目录。第二步验证web.xml配置。打开web/WEB-INF/web.xml确认servlet-mapping的url-pattern与浏览器访问路径一致。例如url-pattern/login/url-pattern对应http://localhost:8080/login而url-pattern/login.jsp/url-pattern才是直接访问JSP页面。很多学生把Servlet映射写成/login.jsp结果请求/login时404。第三步断点调试Servlet。在VSCode中打开LoginServlet.java在doPost()方法第一行打断点启动Debug模式CtrlShiftD在浏览器输入http://localhost:8080/login观察变量值。如果request.getParameter(username)为null检查login.jsp里的form表单input nameusername的name属性是否拼写正确如果userDAO.findByUsername()返回null用MySQL命令行直接执行SELECT * FROM user WHERE usernametest;验证数据库连接是否正常。这三步覆盖了90%的部署问题比盲目重启Tomcat高效得多。4. 核心功能实现详解从登录验证到订单生成的代码级剖析4.1 登录模块Filter拦截与Session管理的双重保险登录功能看似简单但涉及安全边界设计。LoginServlet.java接收表单提交后先校验验证码request.getSession().getAttribute(verifyCode)与request.getParameter(verifyCode)比对再调用UserDAO.findByUsername()查询用户用BCrypt.checkpw(password, user.getPassword())验证密码。这里的关键是密码比较必须用BCrypt提供的checkpw()而非user.getPassword().equals(inputPassword)——后者会暴露密码哈希值被暴力破解的风险。验证通过后不是简单session.setAttribute(user, user)而是创建LoginContext对象封装用户信息和登录时间LoginContext context new LoginContext(); context.setUser(user); context.setLoginTime(new Date()); context.setIp(request.getRemoteAddr()); session.setAttribute(loginContext, context);这样设计的好处是后续Filter可以检查context.getIp()实现同一账号多地登录限制。LoginFilter.java的doFilter()方法里先排除静态资源路径if (request.getRequestURI().endsWith(.jpg) || request.getRequestURI().endsWith(.css) || request.getRequestURI().endsWith(/login.jsp) || request.getRequestURI().endsWith(/LoginServlet)) { chain.doFilter(request, response); return; }然后检查session.getAttribute(loginContext)是否存在不存在则重定向到登录页。注意这里用response.sendRedirect()而非request.getRequestDispatcher().forward()因为前者是客户端跳转URL变化后者是服务器端转发URL不变对于登录拦截必须用前者否则用户直接在地址栏输入/cart.jsp仍能访问。实测发现有些学生把Filter配置在url-pattern/*/url-pattern下却忘了排除/login.jsp导致登录页无限重定向。4.2 购物车模块内存存储与持久化同步的权衡购物车采用Session内存存储数据库异步同步方案。Cart.java是一个POJO包含MapInteger, CartItemkey为商品IDvalue为数量CartItem类里有getTotalPrice()方法计算product.getPrice() * quantity。添加商品时CartServlet.java的doPost()方法Cart cart (Cart) session.getAttribute(cart); if (cart null) { cart new Cart(); session.setAttribute(cart, cart); } int productId Integer.parseInt(request.getParameter(productId)); cart.addItem(productId, 1); // addItem内部处理重复商品数量累加这里session.setAttribute(cart, cart)是关键确保购物车对象绑定到当前用户会话。但内存购物车有个致命缺陷用户关闭浏览器后数据丢失。因此系统在订单提交时强制同步OrderServlet.java里调用OrderService.createOrder(cart, userId)该方法先遍历cart.getItems()生成订单明细再清空session.removeAttribute(cart)。更严谨的做法是在sessionDestroyed()监听器里触发购物车备份但毕业设计层面订单提交时的同步已足够。调试时常见错误是cart.addItem()方法里忘记判空// 错误写法 cartItems.put(productId, new CartItem(product, quantity)); // 正确写法 CartItem item cartItems.get(productId); if (item ! null) { item.setQuantity(item.getQuantity() quantity); } else { cartItems.put(productId, new CartItem(product, quantity)); }否则每次添加都会覆盖原有商品数量。4.3 订单模块分布式事务的简化实现与状态机设计订单生成是系统最复杂的业务逻辑涉及多表写入和状态流转。OrderService.createOrder()方法里先生成订单号ORD202405010001格式再开启数据库事务Connection conn DBUtil.getConnection(); conn.setAutoCommit(false); try { // 插入订单主表 PreparedStatement ps1 conn.prepareStatement(INSERT INTO order_info (...) VALUES (...)); ps1.setLong(1, orderId); ps1.setInt(2, userId); ps1.setBigDecimal(3, totalAmount); ps1.executeUpdate(); // 插入订单明细表 PreparedStatement ps2 conn.prepareStatement(INSERT INTO order_item (...) VALUES (...)); for (CartItem item : cart.getItems().values()) { ps2.setLong(1, orderId); ps2.setInt(2, item.getProduct().getId()); ps2.setInt(3, item.getQuantity()); ps2.setBigDecimal(4, item.getTotalPrice()); ps2.executeUpdate(); } // 更新商品库存 PreparedStatement ps3 conn.prepareStatement(UPDATE product SET stock stock - ? WHERE id ?); for (CartItem item : cart.getItems().values()) { ps3.setInt(1, item.getQuantity()); ps3.setInt(2, item.getProduct().getId()); ps3.executeUpdate(); } conn.commit(); } catch (SQLException e) { conn.rollback(); throw new RuntimeException(订单创建失败, e); } finally { conn.close(); }这里conn.rollback()是兜底保障但实际测试中发现如果库存不足UPDATE product SET stock stock - 5 WHERE id 100执行后ROW_COUNT()为0事务不会自动回滚需要手动检查ps3.getUpdateCount()是否等于预期值。因此在更新库存后添加if (ps3.getUpdateCount() 0) { throw new RuntimeException(商品库存不足); }订单状态机设计在OrderStatus.java枚举类里public enum OrderStatus { WAIT_PAYMENT(0, 待付款), PAID(1, 已付款), SHIPPED(2, 已发货), COMPLETED(3, 已完成), CANCELLED(4, 已取消); private final int code; private final String desc; // 构造方法和getter }状态变更不是简单UPDATE order_info SET status 2而是通过OrderService.updateStatus(Long orderId, OrderStatus fromStatus, OrderStatus toStatus)方法先查询当前状态是否为fromStatus再更新为toStatus防止状态非法跳转如从“待付款”直接到“已完成”。这种设计在答辩时能清晰展示你对业务规则的理解深度。5. 毕业设计专项优化论文写作、答辩演示与代码改造技巧5.1 论文撰写避坑指南从“技术堆砌”到“问题驱动”很多学生的论文第一章写“JavaEE技术概述”第二章写“MySQL数据库原理”第三章贴代码——这注定被答辩组质疑“你到底解决了什么问题”。正确的写法是以问题为导向重构章节第一章“需求分析”不要罗列“用户可以注册登录”而是描述真实痛点。例如“调研发现校园二手交易平台存在商品信息更新滞后问题卖家发布商品后平均2.3小时才被买家看到本系统通过引入Redis缓存商品列表将首页加载时间从3.2秒降至0.8秒”。第二章“系统设计”重点画两张图——不是UML图而是数据流向图和异常处理流程图。数据流向图标注每个环节的数据形态如“用户提交的JSON数据 → Servlet解析为String → DAO层转换为Product对象 → MySQL存储为BLOB”异常处理流程图展示“支付失败时订单状态如何回滚库存如何恢复用户界面如何提示”。第三章“实现细节”挑三个最能体现你能力的点深挖。比如“验证码防刷机制”不是写“用了Kaptcha”而是说明“在LoginFilter里增加IP请求频次统计同一IP每分钟最多5次登录尝试超限后返回429状态码并记录到log4j日志供管理员审计”。这种细节让老师相信你真的动手做过。5.2 答辩演示话术把“我写了”变成“我解决了”答辩时老师最反感“这个功能是我写的”这种表述。换成场景化话术展示登录页时说“这里实现了双因子验证除了密码还要求输入短信验证码验证码有效期2分钟超时自动失效——这是为了解决同学反馈的‘室友盗用账号’问题。”展示订单页时说“订单状态机设计允许管理员后台强制变更状态比如买家投诉未发货客服可直接将状态从‘已付款’改为‘已取消’系统自动触发库存回滚和退款通知——这比让用户自己申请售后更高效。”展示数据库时说“商品表的category_id字段建立了B树索引实测在10万条商品数据下按分类查询响应时间稳定在15ms内满足毕设性能指标要求。”每句话都要绑定一个具体问题、一个解决方案、一个可验证结果。避免使用“可能”“大概”“应该”等模糊词汇用“实测”“日志显示”“监控截图”等确定性表述。5.3 代码改造速查表让系统从“能跑”升级为“亮眼”压缩包里的源码是基线但答辩加分项在于你做了哪些增值改造。以下是零成本高回报的改造清单改造点实现方式答辩话术登录日志审计在LoginServlet.java里添加LogUtil.info(用户[{}]于[{}]从[{}]登录成功, username, new Date(), request.getRemoteAddr())日志输出到logs/login.log“增加了安全审计功能所有登录行为实时记录IP和时间便于追溯异常访问”商品搜索优化修改ProductDAO.searchByName()方法SQL从WHERE name LIKE %?%改为WHERE MATCH(name) AGAINST(? IN NATURAL LANGUAGE MODE)并为name字段添加FULLTEXT索引“将模糊搜索升级为全文检索关键词匹配准确率提升40%测试数据集下响应时间减少60%”响应式页面用Bootstrap 4重写index.jsp添加meta nameviewport contentwidthdevice-width, initial-scale1和栅格系统“适配移动端访问测试覆盖iPhone SE到iPad Pro所有尺寸触控操作区域放大至44px以上”接口文档用Swagger2为OrderServlet生成API文档在web.xml里配置servlet-classcom.github.kongchen.swagger.docgen.SpringSwaggerConfig/servlet-class“提供标准化接口文档方便后续扩展微信小程序端所有接口支持CORS跨域”这些改造都不需要重写核心逻辑但能让答辩老师眼前一亮。记住毕业设计不是比谁代码量大而是比谁解决问题的思路更贴近真实工程场景。6. 常见问题与排查技巧实录那些让我熬夜三天的坑6.1 JSP页面中文乱码字符集传递链的断裂点现象登录页输入中文用户名后台request.getParameter(username)得到乱码如李国。这不是单一环节问题而是字符集传递链的断裂。排查顺序JSP页面声明检查login.jsp顶部是否有% page contentTypetext/html;charsetUTF-8 %缺了这行浏览器会用ISO-8859-1解析页面。表单编码确认form标签里有accept-charsetUTF-8属性否则浏览器用默认编码提交。Tomcat配置conf/server.xml里Connector节点必须有URIEncodingUTF-8否则URL路径参数如/product?id1会乱码。Request解码在LoginServlet.java开头添加request.setCharacterEncoding(UTF-8)这是最关键的一步——因为POST请求体的编码需要手动指定否则Servlet容器用默认的ISO-8859-1解码。数据库连接DBUtil.java里的JDBC URL必须包含characterEncodingUTF-8否则存入数据库的就是乱码。这五个环节缺一不可我曾因漏掉第4步调试了8小时最终在request.getParameter()返回值旁边加了System.out.println(new String(request.getParameter(username).getBytes(ISO-8859-1), UTF-8))才定位到问题。6.2 Tomcat启动后端口被占用进程级冲突的精准定位现象VSCode点击“Start Server”后报错Address already in use: JVM_Bind port。不要盲目重启电脑用命令行精准定位Windowsnetstat -ano | findstr :8080找到PID后tasklist | findstr PID查进程名如果是java.exe用taskkill /f /pid PID结束如果是Skype.exe需在Skype设置里关闭“使用80/443端口”。macOS/Linuxlsof -i :8080输出类似java 12345 user 123u IPv6 0x... TCP *:http-alt (LISTEN)然后kill -9 12345。更隐蔽的情况是另一个Tomcat实例在后台运行如通过终端启动未关闭ps aux | grep tomcat可查到。实测发现VSCode的Tomcat插件有时会残留进程建议每次调试后手动在VSCode的“Terminal”里执行pkill -f tomcat。6.3 数据库连接池配置失效DBCP与Tomcat JNDI的冲突现象系统运行一段时间后报错Cannot get a connection, pool error Timeout waiting for idle object。这是因为默认的DBUtil.getConnection()每次新建连接未使用连接池。修复方案有两种方案一推荐用Tomcat内置JNDI配置。在conf/context.xml里添加Resource namejdbc/shopping authContainer typejavax.sql.DataSource maxTotal20 maxIdle10 minIdle5 initialSize5 maxWaitMillis10000 validationQuerySELECT 1 testOnBorrowtrue removeAbandonedOnBorrowtrue removeAbandonedTimeout60 usernameroot password123456 driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/shopping_mall?useUnicodetrueamp;characterEncodingUTF-8/然后在web.xml里配置资源引用resource-ref descriptionDB Connection/description res-ref-namejdbc/shopping/res-ref-name res-typejavax.sql.DataSource/res-type res-authContainer/res-auth /resource-ref最后在DBUtil.java里用JNDI查找Context ctx new InitialContext(); DataSource ds (DataSource) ctx.lookup(java:comp/env/jdbc/shopping); return ds.getConnection();方案二用DBCP2连接池。下载commons-dbcp2-2.9.0.jar和commons-pool2-2.11.1.jar放入WEB-INF/lib在DBUtil.java里初始化静态连接池private static BasicDataSource dataSource new BasicDataSource(); static { dataSource.setUrl(jdbc:mysql://localhost:3306/shopping_mall?...); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(5); dataSource.setMaxTotal(20); dataSource.setTestOnBorrow(true); dataSource.setValidationQuery(SELECT 1); }两种方案都能解决连接泄漏但JNDI方案更符合JavaEE规范答辩时能体现你对容器管理的理解。6.4 JSP EL表达式不解析隐式对象与作用域的混淆现象index.jsp里写${productList}页面直接显示${productList}字符串而非商品列表。原因通常是JSP版本过低检查web.xml的web-app声明必须是version3.0或更高否则EL表达式默认禁用。page指令关闭EL确认% page isELIgnoredfalse %如果为true则EL被禁用。作用域错误ProductServlet.java里用request.setAttribute(productList, list)但JSP里${productList}只能访问pageScope需改为${requestScope.productList}或确保% page isELIgnoredfalse %且request.setAttribute()正确调用。Tomcat版本不匹配Tomcat 7默认支持EL但若用Tomcat 6需手动开启。最快速验证法在JSP里写${11}如果显示2说明EL正常问题在数据绑定如果显示${11}说明EL被禁用。提示所有排查技巧都来自真实答辩现场。去年有学生因web.xml里web-app版本写成2.5导致EL不解析答辩时当场修改为3.0并重启Tomcat老师当场给了最高分——因为这证明他掌握了XML Schema版本与功能特性的对应关系。我在实际指导中发现学生最大的误区是把毕业设计当成“代码搬运工”而真正的价值在于当系统报错时你能像老司机一样听声音就知道是离合器打滑还是变速箱油不足。这套JavaEE商城系统就是你的第一台“实训车”——它不追求最新技术但每个零件都裸露在外让你看清动力如何从油门传递到轮胎。答辩时老师不会问“Spring Cloud怎么集成”但一定会问“你的Filter为什么放在web.xml而不是注解配置”因为前者考察你是否理解容器生命周期后者只是框架API调用。压缩包里的每一行代码都是为你铺就的思维阶梯从request.getParameter()开始到conn.rollback()结束中间是你亲手构建的Web世界。最后分享一个小技巧把src目录下的所有Java文件用VSCode批量格式化ShiftAltF再用git diff对比格式化前后的差异你会突然明白为什么老师强调“代码风格一致性”——那不是审美问题而是团队协作的契约。本文还有配套的精品资源点击获取
返回列表