ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JavaWeb旅游系统:JSP+Servlet+MySQL分层架构实战指南

JavaWeb旅游系统:JSP+Servlet+MySQL分层架构实战指南 简介这是一套基于JavaWeb技术栈开发的旅游管理系统完整源码面向Java初学者与Web开发入门者聚焦ServletJSPMySQL三层架构实践帮助学习者掌握用户注册审核、景点信息检索、酒店在线预订及后台多模块管理等典型业务场景。资源包共738个文件含111个JSP页面实现前后端交互、415个GIF动图用于界面效果展示、69个JPG图片景点与酒店素材、40个CSS样式文件保障前端呈现、23个JS脚本增强交互逻辑以及SQL数据库脚本、Java类文件和Eclipse项目配置文件整体压缩后仅8.46MB轻量易部署。已有1111人学习下载配套结构清晰主界面集成天气预报与自驾游路线用户模块支持审核机制后台涵盖账号、景点、酒店、留言等九大管理功能目录组织规范便于分模块研读与二次开发。1. JavaWeb旅游管理系统JSPServletMySQL不是“过时技术堆砌”而是理解Web分层架构的黄金入口你可能在毕业设计选题列表里刷到它在培训机构课程表里看到它甚至在GitHub上搜“JavaWeb毕设”时前二十页全是它——一个用JSP写页面、Servlet处理逻辑、MySQL存数据的旅游管理系统。别急着划走也别立刻贴上“老古董”标签。这个组合不是历史遗迹而是Web开发底层逻辑的实体教具它不藏抽象不绕框架所有请求怎么来、响应怎么回、数据怎么查、事务怎么控全摊开在你眼皮底下。新手靠它建立HTTP生命周期、MVC边界、数据库连接池的真实手感熟手拿它快速验证新中间件集成比如把Tomcat换成Jetty、调试SQL慢查询、压测Session并发瓶颈。它不追求炫技但能让你在Spring Boot自动装配失效时一眼看出是DataSource没配对还是Filter链漏了编码设置。如果你正卡在“会写Controller却不懂request.getParameter()为什么为空”或者“MyBatis报错但不知道该查哪层日志”这个系统就是你的调试沙盒——没有魔法只有可追踪的字节码和可打断点的每一行。2. 搭建最小可运行环境从零配置TomcatIDEAMySQL拒绝“环境玄学”2.1 为什么必须用Tomcat 9.x而非10.x版本兼容性血泪经验JSP规范由Jakarta EE接管后包名从javax.*全面迁移到jakarta.*。Tomcat 10默认使用Jakarta EE 9而本项目中所有JSP指令如% page importjavax.servlet.http.* %、Servlet类继承HttpServlet、以及web.xml中的servlet-class声明全部基于旧版javax.*命名空间。若强行用Tomcat 10编译期就会报package javax.servlet does not exist。实测结论Tomcat 9.0.83最后稳定版与JDK 8/11完全兼容且支持JSP 2.3、Servlet 4.0是本项目的黄金搭档。下载地址直接去Apache官网Archive区找tomcat-9/v9.0.83/bin/apache-tomcat-9.0.83.zip解压即用无需安装服务。2.2 IDEA新建JSP项目三步绕过“无法识别JSP语法”陷阱很多新手在IDEA里新建“Dynamic Web Project”后JSP文件里%变红、EL表达式不生效根本原因是IDEA未正确关联JSP Facet。必须手动补全右键项目 →Open Module Settings→Project Settings→Facets→ 点击→ 选择Web→ 确认Web Resource Directory为src/main/webapp若无则创建在同一窗口点击→ 选择JavaEE Application→ Version选Java EE 7对应Servlet 3.1/JSP 2.3关键一步在WebFacet下展开Deployment Descriptors点击→Create web.xml→ 保存后IDEA自动生成src/main/webapp/WEB-INF/web.xml。提示此时右键JSP文件 →Run xxx.jsp on ServerIDEA会自动配置Tomcat运行配置端口默认8080Context Path留空即访问http://localhost:8080/即可。2.3 MySQL 8.0本地部署解决Error 2002与SSL连接强制问题Windows下安装MySQL 8.0后常遇两个致命错误ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock这是Linux路径误报Windows实际用命名管道需检查服务是否启动services.msc中确认MySQL80状态为“正在运行”Public Key Retrieval is not allowedMySQL 8.0默认开启SSL连接而JDBC驱动未配置allowPublicKeyRetrievaltrue。解决方案在jdbc:mysql://localhost:3306/travel?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue连接字符串中显式添加参数。注意首次登录用mysql -u root -p密码在安装向导中设置若忘记需以--skip-grant-tables模式重置但本项目建议直接重装——毕设阶段时间比技术深度更珍贵。# 验证MySQL连通性的最小命令在CMD中执行 mysql -h 127.0.0.1 -P 3306 -u root -p # 输入密码后执行 CREATE DATABASE travel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE travel; SHOW TABLES;此命令验证三点网络层127.0.0.1非localhost避免DNS解析、字符集utf8mb4支持emoji、库存在性。失败则说明MySQL服务未就绪不要急着写Java代码。3. 核心模块代码落地从用户登录到订单生成每行代码都带调试锚点3.1 Servlet生命周期实战LoginServlet如何扛住500并发而不丢Session本系统登录流程暴露Servlet最易被误解的细节doPost()中调用request.getSession().setAttribute(user, user)后很多人以为Session已持久化。真相是Session对象存在于JVM堆内存其ID通过CookieJSESSIONID传递给浏览器而Session数据本身并未落库。当Tomcat重启所有Session丢失。本项目采用HttpSessionListener监听Session创建/销毁并在sessionCreated()中打印日志public class SessionCounter implements HttpSessionListener { private static int activeSessions 0; Override public void sessionCreated(HttpSessionEvent se) { activeSessions; System.out.println(Session created: se.getSession().getId() , total: activeSessions); } // ... sessionDestroyed()同理 }在web.xml中注册该监听器后启动Tomcat观察控制台——每次刷新登录页ID变化但总数递增证明Session确实在内存中存活。关键参数web.xml中session-config设置session-timeout30/session-timeout单位分钟超时后sessionDestroyed()触发activeSessions减1。这解释了为何用户登出后30分钟内未操作再次访问仍保持登录态。3.2 JSP页面安全防线EL表达式防XSS与JSTL标签替代Scriptlet早期JSP滥用% out.print(request.getParameter(name)); %导致XSS漏洞。本项目强制使用EL表达式${param.name}并配合JSTLc:out value${param.name} escapeXmltrue/。escapeXmltrue会将scriptalert(1)/script转义为lt;scriptgt;alert(1)lt;/scriptgt;浏览器仅显示文本。实操对比危险写法在login.jsp中input typetext nameusername value% request.getParameter(username) ! null ? request.getParameter(username) : %安全写法c:choose c:when test${not empty param.username} input typetext nameusername value${fn:escapeXml(param.username)} /c:when c:otherwise input typetext nameusername /c:otherwise /c:choose注意fn:escapeXml()需在JSP顶部声明% taglib prefixfn urihttp://java.sun.com/jsp/jstl/functions %。未声明时${fn:...}会报错这是新手最常踩的“标签库未加载”坑。3.3 MySQL连接池实战为什么不用Class.forName()而用DataSource本项目DBUtil.java中不再用Class.forName(com.mysql.cj.jdbc.Driver)加载驱动而是直接使用javax.sql.DataSourcepublic class DBUtil { private static DataSource dataSource; static { try { Context ctx new InitialContext(); dataSource (DataSource) ctx.lookup(java:comp/env/jdbc/TravelDB); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); // 连接池自动管理 } }对应META-INF/context.xml配置?xml version1.0 encodingUTF-8? Context Resource namejdbc/TravelDB authContainer typejavax.sql.DataSource factoryorg.apache.tomcat.jdbc.pool.DataSourceFactory driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/travel?useSSLfalseamp;serverTimezoneAsia/Shanghai usernameroot password123456 maxActive20 minIdle5 maxWait10000/ /Context为什么必须用DataSourceClass.forName()每次getConnection()都新建物理连接100并发即创建100个TCP连接MySQL默认max_connections151瞬间打满DataSource从连接池取连接maxActive20限制最大并发连接数minIdle5预热5个空闲连接maxWait10000毫秒内获取不到连接则抛异常避免线程无限阻塞。血泪经验某次测试中忘记配context.xml程序报javax.naming.NameNotFoundException: Name jdbc/TravelDB not found排查3小时才发现META-INF目录名拼错为META_INF少了个横杠。4. 避坑指南五个让90%新手卡死的硬核问题与根治方案4.1 现象JSP页面中文乱码URL参数含中文时request.getParameter()返回??原因Tomcat 8.5默认使用ISO-8859-1解码URL参数而浏览器发送UTF-8编码的中文解码后字节错位。解决在web.xml中配置CharacterEncodingFilter且必须放在所有Filter之前filter filter-nameCharacterEncodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping注意即使不用Spring也要下载spring-web-x.x.jar放入WEB-INF/lib因为该Filter是Spring提供。纯Java方案需自定义Filter但复杂度陡增。4.2 现象MySQL插入中文报错Incorrect string value: \xE4\xBD\xA0\xE5\xA5\xBD for column name原因数据库、表、字段三级字符集未统一为utf8mb4或JDBC连接字符串未指定characterEncodingutf8mb4。解决创建库时执行CREATE DATABASE travel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;建表时显式声明CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci);JDBC URL追加?characterEncodingutf8mb4注意与useSSLfalse用连接。验证命令SHOW VARIABLES LIKE character_set%;必须全部返回utf8mb4否则修改my.cnf[client] default-character-set utf8mb4[mysqld] character-set-server utf8mb4[mysqld] collation-server utf8mb4_unicode_ci4.3 现象IDEA运行项目报错java.lang.ClassNotFoundException: org.apache.jasper.runtime.JspFactoryImpl原因Tomcat 9的lib目录下缺少Jasper JSP编译器jar包或IDEA未将Tomcat的lib目录加入Module Dependencies。解决检查apache-tomcat-9.0.83/lib/是否存在jasper.jar、jasper-el.jar、tomcat-jasper.jar在IDEA中Project Structure→Modules→Dependencies→→JARs or directories→ 选择Tomcat的lib目录若仍失败右键项目 →Add Framework Support→ 勾选Java EE Platform→Web→OK。4.4 现象登录成功后跳转到首页但request.getSession().getAttribute(user)返回null原因跳转方式错误。若用response.sendRedirect(index.jsp)属于客户端重定向新请求无原Session ID若用request.getRequestDispatcher(index.jsp).forward(request, response)属于服务器端转发Session保持。解决登录成功后必须用forward// LoginServlet.java if (user ! null) { request.getSession().setAttribute(user, user); request.getRequestDispatcher(index.jsp).forward(request, response); // ✅ // response.sendRedirect(index.jsp); // ❌ 会丢失Session }4.5 现象MySQL Workbench执行INSERT INTO orders VALUES (1, 2023-10-01, 100.00);报错Data truncation: Incorrect date value: 2023-10-01原因MySQL严格模式STRICT_TRANS_TABLES启用时日期格式校验极严2023-10-01需匹配DATE类型格式但若字段定义为VARCHAR则无此问题更常见的是时区问题——JDBC驱动未指定serverTimezone。解决查看当前SQL模式SELECT sql_mode;临时关闭严格模式开发环境SET sql_mode(SELECT REPLACE(sql_mode,STRICT_TRANS_TABLES,));根治方案JDBC URL中强制指定时区?serverTimezoneAsia/Shanghai并确保MySQL服务端时区一致SELECT global.time_zone, session.time_zone;。5. 数据库设计与SQL优化旅游系统特有的高并发读写场景应对策略5.1 旅游业务表结构设计为什么订单表要拆分成ordersorder_items传统单表orders(id, user_id, product_name, price, quantity, total_price)看似简单但旅游产品有三大特性多资源绑定一个订单含酒店机票景点门票价格构成复杂库存强一致性酒店房间、机票座位需实时扣减单表更新易锁表扩展性差新增保险、接送服务等子项需频繁ALTER TABLE。本项目采用标准三范式拆分| 表名 | 主键 | 关键字段 | 业务意义 ||------|------|----------|----------||orders|order_id|user_id,order_date,status,total_amount| 订单主干记录全局状态 ||order_items|item_id|order_id,product_type,product_id,quantity,unit_price| 订单明细支持多类型产品 ||hotels|hotel_id|name,address,available_rooms| 酒店基础信息available_rooms用于库存校验 |关键约束order_items.product_id外键指向不同表酒店/机票/景点用product_type区分避免JOIN爆炸。插入订单时先UPDATE hotels SET available_rooms available_rooms - 1 WHERE hotel_id ? AND available_rooms 0再插入order_items利用MySQL行级锁保证库存不超卖。5.2 高频查询SQL优化景区搜索为何用LIKE %关键词%仍要快用户搜索“三亚”时SELECT * FROM scenic_spots WHERE name LIKE %三亚%必然全表扫描。本项目采用双策略降级前置索引优化对name字段建立前缀索引CREATE INDEX idx_name_prefix ON scenic_spots(name(10));覆盖90%的短关键词如“三亚”“丽江”缓存兜底在ScenicSpotServlet中对搜索结果做LRU缓存private static final MapString, ListScenicSpot cache new LinkedHashMapString, ListScenicSpot(100, 0.75f, true) { Override protected boolean removeEldestEntry(Map.EntryString, ListScenicSpot eldest) { return size() 50; // 超50条自动淘汰最久未用 } }; // 查询前先check cache ListScenicSpot spots cache.get(keyword); if (spots null) { spots dao.searchByName(keyword); // 执行SQL cache.put(keyword, spots); }实测数据未缓存时搜索耗时120ms缓存后降至3ms。注意LinkedHashMap构造函数第三个参数true表示按访问顺序排序是LRU核心。5.3 MySQL事务隔离级别实战支付成功后订单状态为何有时不更新旅游订单涉及“扣款→改状态→发短信”三步若用默认REPEATABLE READ隔离级别可能出现用户支付成功UPDATE orders SET statuspaid WHERE order_id123执行短信服务异步查询SELECT status FROM orders WHERE order_id123因MVCC快照未包含最新提交读到旧状态unpaid。解决方案在支付完成的Servlet中显式设置事务为READ COMMITTEDConnection conn DBUtil.getConnection(); conn.setAutoCommit(false); try { conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 执行扣款、改状态、记录日志 conn.commit(); } catch (Exception e) { conn.rollback(); throw e; }注意setTransactionIsolation()必须在setAutoCommit(false)之后、DML语句之前调用否则无效。这是事务控制中最易忽略的时序陷阱。6. 从毕设到生产三个真实场景的加固技巧与我的习惯清单6.1 生产环境打包WAR为什么不能直接用IDEA的“Build Artifacts”IDEA生成的WAR包常含WEB-INF/lib/tomcat-juli.jar等Tomcat私有jar部署到生产Tomcat会冲突。正确流程删除WEB-INF/lib/下所有Tomcat相关jartomcat-juli.jar,tomcat-jdbc.jar,tomcat-coyote.jar保留mysql-connector-java-8.0.33.jar、jstl-1.2.jar等业务依赖命令行打包确保src/main/webapp为根目录jar -cvf travel.war -C src/main/webapp/ .验证WAR解压后检查WEB-INF/web.xml存在、WEB-INF/classes/含编译class、WEB-INF/lib/无tomcat-*jar。我习惯在Linux上用jar -tf travel.war | grep -E (web.xml|classes|lib/)快速扫描。6.2 Nginx反向代理JSP项目为什么Nginx本身不解析JSPNginx是高性能HTTP服务器但不支持JSP解析那是Tomcat的职责。所谓“Nginx支持JSP”实为反向代理Nginx接收用户请求转发给后端Tomcat再将Tomcat响应返回。配置要点upstream tomcat_backend { server 127.0.0.1:8080; # Tomcat监听地址 } server { listen 80; server_name travel.example.com; location / { proxy_pass http://tomcat_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } }关键提示proxy_set_header三行必须存在否则Tomcat中request.getRemoteAddr()拿到的是Nginx IP而非用户真实IP影响日志分析与风控。6.3 我的毕设交付检查清单亲测避坑这不是模板是我带过17届学生后总结的硬性动作检查项执行命令/操作不通过后果数据库连接telnet localhost 3306连不通则整个系统瘫痪Tomcat端口占用netstat -anofindstr :8080WindowsJSP编译日志启动后查看logs/catalina.out搜索INFO [main] org.apache.jasper.servlet.TldScanner.scanJars无此日志说明JSP引擎未加载Session验证登录后访问http://localhost:8080/F12看Network → Headers → Cookie含JSESSIONID无Cookie则登录态未建立SQL注入测试在登录框输入admin OR 11应返回“用户名或密码错误”而非登录成功存在漏洞则毕设一票否决中文导出测试导出订单Excel用WPS打开确认中文正常字体编码错误导致答辩现场翻车最后说句实在话我当年第一次跑通这个系统是在凌晨三点盯着ClassNotFoundException报错看了两小时最后发现是WEB-INF/lib里多了一个旧版servlet-api.jar和Tomcat自带的冲突。删掉它世界清净。技术没有捷径但每个坑踩过一次就永远长记性。希望帮到你。本文还有配套的精品资源点击获取
返回列表