简介:基于JSP的网上书店系统毕业设计完整交付包,包含项目源码、毕业论文和演示视频,适合Java Web初学者及需要完成毕业设计的高校学生。系统采用B/S结构与JSP技术,覆盖用户登录、管理员管理、图书管理、销售主页、个人资料修改等模块,并讨论了Session与Cookie安全、身份验证机制及SQL Server数据库保护,完整展示管理信息系统的开发流程。压缩包约23.73MB,内含可直接运行的源代码、详细论文文档和视频演示,视频演示可直观了解系统操作与页面流转。论文从需求分析、数据库概念结构设计和逻辑结构设计,到系统功能模块划分及前端页面开发均有论述,能够帮助读者快速理清从设计到实现的完整思路。目前已有122人浏览学习,可作为网上书店或同类电商系统的设计参考、代码学习和文档撰写模板,毕业设计与论文写作阶段可参考其搭建原型并理解各模块间关系,减少从零开发的试错成本。
1. 基于JSP的网上书店系统:毕业设计到底在做什么,值不值得选
如果你正在找JavaWeb方向的毕业设计,大概率会撞见这个标题:基于JSP的网上书店系统。它不算新东西,但确实是JavaWeb里最典型的练手项目——用JSP写页面、Servlet处理请求、JDBC访问MySQL,把图书展示、用户登录、购物车和订单这一整条电商闭环跑通。这类项目的好处是技术栈单一、代码量可控、论文好写,坏处是如果只会照着抄,答辩时容易一问三不知。这篇笔记就按我实际做过类似项目的流程,把从导入源码到能讲清楚的技术细节拆给你,包括环境版本、数据库设计、核心代码和那些不跑一遍根本发现不了的坑。不管是想快速交差,还是想借这个题目把JavaWeb基本功补起来,都值得往下看。
2. JSP网上书店系统的技术架构与核心原理:从MVC到数据库设计
2.1 为什么这套组合还不过时:JSP+Servlet+JDBC vs Spring Boot
很多同学第一反应是:现在外面写JavaWeb都用Spring Boot,JSP这种老技术还有必要选吗?如果是工作项目,确实没必要;但毕设是另一回事。绝大多数高校的JavaWeb课程还在讲JSP+Servlet,答辩老师对这套流程更熟悉,你讲起来也容易对齐。更关键的是,JSP项目的代码是“透明”的:你写一个login()方法,调用关系一眼能看穿,不像Spring Boot里一堆注解和自动装配,出了错只会看到一个深不见底的堆栈。
从依赖上看,JSP网上书店系统通常只需要四样东西:Servlet API、JSTL标签库、MySQL驱动,外加一个Tomcat运行时。Servlet API负责处理HTTP请求,JSTL负责在JSP里写c:forEach这类标签来循环展示数据,MySQL驱动负责让JDBC能连上数据库。没有Maven的版本冲突,没有Bean的生命周期,连new对象都是手动来。对新手来说,这种“原始”恰恰是好事,因为你能在断点里看到每个对象是怎么创建出来的。
再说运行原理。浏览器访问index.jsp时,Tomcat会把jsp文件翻译成一个Java类,这个类本质上是HttpServlet,然后经过编译、加载、实例化,service方法里根据请求方式分发到doGet或doPost。所以JSP不是一上来就被浏览器直接执行的,它最终还是一个Servlet。理解了这一点,你就明白为什么JSP页面里可以用request、response、session、application这些内置对象——因为它们就是Servlet方法里的参数和成员。
对比Spring Boot,JSP项目少了Controller层的@GetMapping和@RequestParam,改成在web.xml里写servlet-mapping,或者在Servlet类上用@WebServlet注解。如果你在源码里看到web.xml配了servlet路径,别急着删,那是老项目最常用的映射方式。两者没有对错,你只要在论文里写清楚自己用的是哪一种就行。
2.2 数据模型怎么设计:用户、图书、订单三张核心表的关系
网上书店系统的核心表一般不会超过六张,最少的情况是三张:用户表、图书表、订单表。但如果订单和图书是多对多,直接一张订单表就存不了多本书,所以必须拆订单明细表。很多同学的数据库设计问题就出在这里:把“订单表”设计成一行存一本书,同一个订单买三本书就插三条相同order_id的记录,这样查订单总价和改地址都会非常难受。
我习惯先画一个关系简图:用户1—N订单,订单1—N订单明细,图书1—N订单明细。然后每个表按第三范式拆字段。下面是一套常见的建表SQL骨架,你可以直接对照源码调整:
CREATE TABLE user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, phone VARCHAR(20), address VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE book ( book_id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), price DECIMAL(10,2), stock INT DEFAULT 0, cover VARCHAR(255) ); CREATE TABLE orders ( order_id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total_price DECIMAL(10,2), status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES user(user_id) ); CREATE TABLE order_detail ( detail_id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, book_id INT NOT NULL, quantity INT NOT NULL, price DECIMAL(10,2) NOT NULL, CONSTRAINT fk_detail_order FOREIGN KEY (order_id) REFERENCES orders(order_id), CONSTRAINT fk_detail_book FOREIGN KEY (book_id) REFERENCES book(book_id) );字段这里有几个容易忽视的点。第一,价格要选DECIMAL而不是FLOAT,否则计算总价时会出现浮点误差,答辩现场演示到这一步会很尴尬。第二,订单状态的status用TINYINT,0代表待付款,1代表已付款,2代表已发货,3代表已完成,比写字符串更省空间,也方便JSP页面用c:if做状态显示。第三,外键要加,但也不用加太多,订单表关联用户、明细表关联订单和图书就够了,多了反而影响插入效率。
再说购物车表。如果源码里没有购物车表,那它多半是把购物车放在Session里,数据结构是Map<bookId, 购买数量>。这样做的好处是不用为临时数据建表,坏处是用户关闭浏览器购物车就没了。如果源码里有一个单独的cart表,那就要在表里加user_id和book_id两个外键。两种方案各有取舍,论文里挑一种说清楚即可。
2.3 MVC分层与请求流转:一次登录请求到底经过哪些文件
理解JSP项目最快的方式,就是挑一个最简单的操作从上到下追一遍。我们拿登录请求开刀。
假设你在login.jsp里写了一个表单,method="post",action="loginServlet"。用户点登录后,Tomcat会根据web.xml里的配置,把请求交给LoginServlet的doPost方法。doPost里第一步是设置request.setCharacterEncoding("UTF-8"),这一步不能省,省了中文用户名就乱码;第二步getParameter("username")取出表单值;第三步调用UserDao.findByUsernameAndPassword,这个Dao里用JDBC拼一条select语句,把结果集映射成User对象返回;第四步,Servlet判断User是否为null,不为null就把user对象放进session,然后response.sendRedirect("index.jsp"),为null就把错误消息放进request域,再request.getRequestDispatcher("login.jsp").forward(request, response)。
这个流转看似简单,但里面有四个答辩高频问题。第一个问题是doGet和doPost的区分:表单用post就重写doPost,GET请求就重写doGet,如果两个都用到,那就在Servlet里同时重写两个方法并都调用同一个业务方法。第二个问题是sendRedirect和forward的区别,前者是302重定向,request属性会丢,后者是内部转发,request属性还在。第三个问题是JSP里怎么拿数据:重定向后request里的属性会丢,但session里的不会,所以登录成功后的用户信息要放session,而错误提示只能在forward下用request传递。第四个问题是DAO层为什么要返回User对象而不是boolean:因为登录成功后,个人信息展示页面还要展示手机号、地址,一次性查出来省得再查一次。
为了让流程更好讲,建议在Servlet里按步骤打日志,比如logger.info("login success, user={}", username)。答辩时打开控制台,顺着日志讲,比对着代码念有说服力得多。后面第6章我还会专门说怎么用断点和日志把整个链路验证一遍,这里先记住一条核心心法:所有JSP项目问题,归根结底都能追溯到“请求在哪一步断了”。
3. 从零跑通这个毕设项目:环境搭配、导入源码、初始化数据库
3.1 环境版本怎么选:JDK 8 + Tomcat 8.5 + MySQL 5.7 的搭配理由
先解决版本问题。我见过太多人在环境上折腾两三天,最后发现是JDK版本和Tomcat版本不匹配。常见做法是JDK 1.8 + Tomcat 8.5 + MySQL 5.7,这套组合兼容性最高,任何教程里的代码都能跑。如果你用Eclipse,对应装Java EE版;IDEA的话选Community版,再装一个Smart Tomcat插件就能本地跑。
为什么不用JDK 11或17?因为老项目的javax.servlet包名从Jakarta改名后,Tomcat 9和Tomcat 10之间不兼容,而网上大部分JSP源码还是基于javax写的。JDK 8 + Tomcat 8.5能保证javax命名空间没问题,这也是网上书店系统最常见的部署组合。MySQL选5.7主要是避开8.0的时区认证坑,如果你已经装了MySQL 8.0也没关系,后面3.2节会讲怎么配。Tomcat版本不建议低于8.0,否则Servlet 3.0的@WebServlet注解可能不生效。
环境装完后,建议先做一个最小验证:启动Tomcat,浏览器访问http://localhost:8080能出现默认首页;再打开一个命令行窗口,敲mysql -u root -p能进MySQL。这两个动作能通过,再继续导入项目,否则后面出了问题很难判定是环境还是代码。
3.2 导入项目与初始化数据库:SQL脚本和JDBC配置一步步来
拿到zip包后先解压,用IDEA或Eclipse以Web项目方式导入。常见结构是src目录下放Java代码,WebContent/WEB-INF下放web.xml和JSP页面,根目录还有bookshop.sql这类数据库脚本。如果你的IDE没识别成Web项目,可以手动添加Web Facet,再把Tomcat关联到项目上,这一步不做,运行按钮会一直是灰的。
数据库这边,打开MySQL客户端创建一个库,再执行脚本:
CREATE DATABASE bookshop DEFAULT CHARACTER SET utf8mb4; USE bookshop; SOURCE /你的路径/bookshop.sql;执行完用show tables;看一下表是否都建出来了。这里有个血泪经验:数据库默认字符集如果不对,后面页面显示中文全是问号,所以创建库时要显式加上DEFAULT CHARACTER SET utf8mb4。脚本里有中文注释的话,记得把SQL文件本身转成UTF-8无BOM编码,否则第一行注释乱码影响不大,但插入中文数据可能报Incorrect string value。
然后找到项目里的JDBC配置文件。老项目常见是src/db.properties或者src/jdbc.properties,里面写着driver、url、username、password。你需要改成自己的数据库密码:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/bookshop?useUnicode=true&characterEncoding=utf8&useSSL=false jdbc.username=root jdbc.password=你的密码参数说明:useUnicode=true和characterEncoding=utf8是保证数据库读写中文不乱码;useSSL=false是关掉MySQL 5.7默认的SSL警告;如果是MySQL 8.0还要把driver改成com.mysql.cj.jdbc.Driver,并在url后面加serverTimezone=Asia/Shanghai,否则会报时区错误。这些参数看着琐碎,但每一个都是前人踩过坑换来的。
改完后检查一下jar包。老项目的WEB-INF/lib里通常会带mysql-connector-java-5.1.x.jar和jstl.jar。如果lib下没有,你就得自己拷进去:在IDE里右键项目Build Path,Add Library,或者直接把jar扔到WEB-INF/lib目录下。注意Tomcat运行时不会自动加载外部jar,所以别放在项目外的目录还指望它能跑。
3.3 部署到Tomcat并验证主流程:启动参数与访问路径
部署这一步,把项目右键Run on Server,选择之前配好的Tomcat 8.5。如果项目没有自动发布到webapps,IDEA会生成一个exploded目录,Tomcat启动后去访问http://localhost:8080/项目名/。项目名在Eclipse里默认是工作区里的项目名,在IDEA里是Application context里配置的,通常叫/bookshop。
启动日志是第一个排查入口。如果看到Exception starting filter或ClassNotFoundException,多半是lib里的jar冲突,或者Servlet版本不对。启动成功后,先进首页,再走一遍注册、登录、加购、下单流程。这个验证动作至少要做两遍:一遍按正常顺序,一遍故意不登录就访问订单页,看会不会被拦截。
另外要特别注意一点:如果你看到源码里 import javax.servlet.http.HttpServlet,而你的Tomcat版本是10.x,那么运行时会直接报ClassNotFoundException。因为Tomcat 10把包名改成了jakarta.servlet,老代码必须做全局替换才能跑,这个工作比换个Tomcat版本麻烦得多。所以我坚持推荐Tomcat 8.5,不是它新,而是它最省事。
很多JSP网上书店系统的订单页面会在web.xml里配一个简单的过滤器,只放行login.jsp、index.jsp和静态资源,其他路径都必须有session里的user对象。如果你发现直接访问某个jsp页面也能看到内容,说明过滤器配置漏了。这个过滤器属于安全基础,论文里建议单独写一节。下一章我会拆核心功能的代码,把这些流程对应的代码和你讲清楚。
4. 核心功能代码拆解:登录会话、图书分页、购物车与订单事务
4.1 用户登录与Session:一个HttpSession对象管住整个购物链路
先看登录Servlet的典型写法:
@WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); UserDao dao = new UserDao(); User user = dao.findByUsernameAndPassword(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("user", user); response.sendRedirect(request.getContextPath() + "/index.jsp"); } else { request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } }逻辑说明:整个类用@WebServlet注解完成URL映射,省去web.xml配置。doPost里先处理编码,再查库,最后根据结果决定重定向还是转发。注意response.sendRedirect用request.getContextPath()拼路径,这是避免项目部署路径写死的关键。这段代码没有重写doGet,所以浏览器直接访问loginServlet会返回405错误;要支持GET请求,需要再重写doGet方法并调用doPost。
写法要点:session.setAttribute("user", user)存的是对象,JSP里用${sessionScope.user.username}就能取出。登录成功后不要把密码也放在cookie里,session已经够用。退出登录则调用session.invalidate(),它会把购物车数据一起清掉,符合“退出即清空”的预期。如果你要实现“记住我”,单纯靠session做不到,需要额外用cookie存token,这个属于扩展点,可加在论文的展望里。
4.2 图书列表分页查询:DAO层怎么写、JSP怎么渲染
图书列表是首页的核心。数据量少的时候可以一次性全查,但毕设答辩时老师常会问“如果有一万本书怎么展示”,所以分页几乎必做。分页的标准做法是先查总条数,再按limit计算偏移量。下面这个DAO方法返回一个PageBean,里面封装了list、totalCount、currentPage、pageSize:
public PageBean<Book> findPage(int currentPage, int pageSize) { PageBean<Book> pb = new PageBean<>(); String countSql = "SELECT COUNT(*) FROM book"; String pageSql = "SELECT * FROM book LIMIT ?, ?"; int totalCount = queryForInt(countSql); int totalPages = (int) Math.ceil(totalCount * 1.0 / pageSize); List<Book> list = queryForList(pageSql, (currentPage - 1) * pageSize, pageSize); pb.setList(list); pb.setTotalCount(totalCount); pb.setTotalPages(totalPages); pb.setCurrentPage(currentPage); pb.setPageSize(pageSize); return pb; }逻辑说明:LIMIT的第一个参数是偏移量,第二个是条数。第1页偏移0,第2页偏移pageSize,所以公式是(currentPage - 1) * pageSize。分开写两条SQL的原因很简单:COUNT查询和分页查询的返回结构不同,不能用同一条SQL硬套。这里用的是PreparedStatement,参数下标从1开始。
JSP页面拿到PageBean后用JSTL渲染,核心就这一段:
<c:forEach items="${pb.list}" var="book"> <div class="book-card"> <img src="${pageContext.request.contextPath}${book.cover}" /> <p>${book.bookName}</p> <p>¥${book.price}</p> <a href="addCartServlet?bookId=${book.bookId}">加入购物车</a> </div> </c:forEach>这里最大的坑是图片路径。book.cover如果存的是“upload/cover1.jpg”这种相对路径,浏览器会在当前URL目录下找,结果就是404。正确做法是前面加${pageContext.request.contextPath}拼成/项目名/upload/cover1.jpg。很多人遇到“jsp图片如何对坐标定位”的问题、图片显示不出来,90%都是这个原因,后面避坑章节我会再展开。
4.3 购物车加入与订单提交:事务边界放在哪里
购物车如果不落库,可以用Session存一个Map<Integer, Integer>,key是bookId,value是数量。添加购物车的Servlet里,先判断session里有没有cart,没有就new一个HashMap:
HttpSession session = request.getSession(); Map<Integer, Integer> cart = (Map<Integer, Integer>) session.getAttribute("cart"); if (cart == null) { cart = new HashMap<>(); } int bookId = Integer.parseInt(request.getParameter("bookId")); cart.put(bookId, cart.getOrDefault(bookId, 0) + 1); session.setAttribute("cart", cart);逻辑说明:getOrDefault是Java 8的写法,取不到值时默认0,然后加1。用Map的好处是不需要一张购物车表,加减商品都只改value。缺点是重复点击“加入购物车”会连续累加,如果用户刷新页面,购物车数量会翻倍,所以前端可以加个按钮禁用,或者跳转时用sendRedirect来避免重复提交。
真正的重头戏是下单。下单要同时做三件事:往订单表插一条、往明细表插多条、扣减图书库存。任何一个失败,前面成功的都要回滚,否则会出现财务对不上的脏数据。因此事务必须放在这一条链路的外层:
Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 int orderId = orderDao.insertOrder(conn, user.getId(), totalPrice); for (Map.Entry<Integer, Integer> entry : cart.entrySet()) { orderDetailDao.insertDetail(conn, orderId, entry.getKey(), entry.getValue()); bookDao.decreaseStock(conn, entry.getKey(), entry.getValue()); } conn.commit(); // 全部成功才提交 session.removeAttribute("cart"); // 清空购物车 } catch (Exception e) { if (conn != null) conn.rollback(); // 出错回滚 e.printStackTrace(); request.setAttribute("error", "下单失败,库存不足"); } finally { DBUtil.close(conn); }逻辑说明:所有DAO方法都改成接收Connection参数,这是为了保证它们用的是同一个数据库连接,而不是每次调用都新开连接。setAutoCommit(false)之后,必须显式commit才算生效。rollback让前面所有insert和update全部撤销。注意库存不足时,要在decreaseStock里用update book set stock = stock - ? where book_id = ? and stock > ?这样的条件SQL来判断,如果返回0就需要手动抛异常触发回滚。
事务边界是这个项目里最容易讲出深度的部分。答辩时老师问“并发下单库存会不会超卖”,你可以回答:在where条件里加stock > ?,让数据库自己判断库存是否够,比在Java里先查再减更安全。这句话说出来,整个代码的水平就和其他人拉开差距了。
5. 避坑指南:JSP网上书店系统的5个典型翻车点与排查方法
5.1 现象:Tomcat启动后访问项目一直404 / 500
原因分两种。404是路径没配对,500是代码或环境问题。404最常见于项目部署名和访问URL不一致,比如项目名是bookshop,你访问localhost:8080/却看到Tomcat首页,访问/bookshop/才进系统。第二个原因是Servlet的@WebServlet("/loginServlet")和表单的action写成loginServlet.do,路径里多个后缀就匹配不上。500常见于lib缺少jar包,比如ClassNotFoundException: com.mysql.jdbc.Driver,说明驱动没进WEB-INF/lib。解决方法是打开Tomcat的catalina.out日志,按异常栈第一行定位,别被一大段Caused by带偏。
我的习惯是先用浏览器直接访问一个静态JSP,如果静态页能开,而请求Servlet就500,那么问题大概率在DAO或JDBC层。再用一个空Servlet测试URL映射通不通,逐步缩小范围。
5.2 现象:登录后中文用户名变成乱码,或者页面全是问号
这是JSP老项目最常见的病。原因有三层,哪一层不对都会乱码:第一层,表单提交时页面本身要是UTF-8,如果你没有在JSP第一行写pageEncoding="UTF-8",浏览器可能用其他编码提交;第二层,Servlet没有调用request.setCharacterEncoding("UTF-8");第三层,数据库连接URL没加characterEncoding=utf8,或者数据库表本身是latin1。解决时三个地方一起改:所有JSP增加<%@ page contentType="text/html;charset=UTF-8" %>,所有Servlet的doPost第一行先setCharacterEncoding,db.properties连接串补上useUnicode=true&characterEncoding=utf8。
检查技巧是,在Servlet里取到username后先打印一遍,如果打印出来乱码,说明提交或取值环节有问题;如果打印正常而数据库里乱码,说明连接串或表字符集有问题。打印是黑匣子最好的突破口。
5.3 现象:启动时提示Access denied for user 'root'@'localhost'
密码不对是最初级的问题,但经常被忽略:db.properties里写的密码是别人的,你需要改成自己的。第二个原因是MySQL 8.0的认证插件升级成caching_sha2_password,而项目里的老驱动还是5.1.x,两者不兼容,报错是Public Key Retrieval is not allowed。解决方法是换用mysql-connector-java 8.0.x驱动,URL里加allowPublicKeyRetrieval=true。第三种情况是Tomcat能启动但第一次查库就报CommunicationsException,通常是MySQL服务没启动,或者URL里的端口改过,检查3306是否被占用。
如果你不确定驱动,最简单的方式是写一个Java主方法直接main里测试Connection,绕过Tomcat,能把问题定位到驱动层面还是项目层面。
5.4 现象:页面能开,但图书封面、CSS、JS全都不显示
这是JSP图片路径问题的集中体现。网上书店的图书信息展示页面经常要显示封面图,而封面图一般存在项目根目录的upload文件夹下。如果JSP里写的是,那么当当前URL是/bookshop/bookList.jsp时,浏览器会解析成/bookshop/upload/cover1.jpg,没问题;但如果你通过/bookshop/indexServlet跳转过来的,页面URL变成/bookshop/indexServlet,相对路径就会拼到/bookshop/indexServlet/upload/cover1.jpg,结果404。
解决方法是所有图片路径都写绝对路径:前面加${pageContext.request.contextPath},即${pageContext.request.contextPath}/upload/cover1.jpg。至于“jsp图片如何对坐标定位”,那属于CSS问题,一般用width/height或者background-position,注意不要用table布局硬调像素,响应式会崩。还有一个隐藏坑:Tomcat默认不允许直接访问WEB-INF目录下的文件,所以图片不要放WEB-INF/upload,要放在WebContent/upload下。
5.5 现象:下单成功但订单状态不变,或者反复刷新生成重复订单
先说重复订单:用户在orderConfirm.jsp提交后,如果按F5刷新,浏览器会重新提交上一次POST请求,导致同一个购物车内容生成两笔订单。解决方式是在下单成功后sendRedirect到订单完成页,而不是forward。这个做法叫Post/Redirect/Get模式,论文里值得提。
订单状态不变通常是写库失败但没给用户提示。我见过一个源码在业务方法里catch异常然后continue,导致部分明细插入成功,订单主表却回滚,最后状态还是0。正确做法是要么全部成功,要么全部回滚。如果你发现代码里没有conn.setAutoCommit(false),那这个项目十有八九没有做事务,你需要自己补上。这个坑特别隐蔽,我建议下单流程走一遍后去数据库看一眼orders和order_detail表的数据,别只看页面出现“下单成功”就放心。
5.6 现象:IDEA/Eclipse里能跑,但导出war部署到独立Tomcat就404
这是很多毕设项目的最后一关。原因通常是IDE运行时把Tomcat的conf/context.xml配了数据源,而导出的war包没有这个配置。另外,项目里如果有绝对路径如“/Users/xxx/...”那换一台电脑必挂。处理方法是把db.properties里的配置全部改成相对Web应用的写法,或者在数据源初始化时用classpath资源路径。
如果war包部署后JSP页面能开,但Servlet请求404,检查web.xml的servlet-mapping里有没有把jsp扩展名拦截,或者看Tomcat manager里的Application Context路径是不是/。建议直接用ROOT.war方式部署,访问localhost:8080/就能进,少一级路径也少一个坑。
6. 答辩与扩展:从「能跑」到「能讲清楚」的三个验证技巧
6.1 用日志和断点把核心链路串起来
答辩前,我建议你挑三个流程:登录、加购、下单,分别打上日志或者用断点走一遍。具体做法是在Servlet入口打一个断点,再在DAO的查询方法打一个断点,F8单步追,看request参数是否完整、SQL参数是否正确、返回结果是否为空。这一步走完,你对这个项目的熟悉程度会立刻超越班上一半照着抄的人。如果项目没接日志框架,可以用System.out.println临时打,但记得答辩前删掉,或者保留成logger,不然老师看到控制台满屏System.out会皱眉。
6.2 画一张请求时序图作为答辩开场
不需要用复杂工具,就用ProcessOn或手画一张登录时序图:浏览器→LoginServlet→UserDao→MySQL→返回User→写入Session→跳转index.jsp。这张图放在PPT第二页,讲三分钟,老师能快速看到你懂不懂这个系统,比贴满屏代码强。
6.3 三个低成本加分扩展:模糊搜索、库存扣减、个人信息展示页
第一个扩展:首页加一个按书名模糊搜索的输入框,DAO里一处select where book_name like ?就够。第二个扩展:修改库存的SQL用update book set stock = stock - ? where book_id = ? and stock >= ?,并在业务层判断返回值。第三个扩展:做一个jsp个人信息展示页面,显示用户基本信息、历史订单列表,这就用到了登录时存的user对象和历史订单查询。这三个改动都不大,但答辩时你可以说“我考虑了并发扣库存、实现了个人信息聚合展示”,这就是亮点。
最后说一个我自己的教训:不要到答辩前一晚才第一次导出war,也不要只在IDE里跑通就觉得自己会了。我见过同学在演示时换个浏览器就白屏,最后发现是浏览器缓存了旧版CSS。正确做法是答辩前一周,用干净的Tomcat,从头到尾不接触IDE,纯命令行启动,把流程走两遍。这事看着麻烦,但能帮你把环境里的玄学问题提前消掉。希望帮到你。
本文还有配套的精品资源,点击获取