JSP+Servlet+MySQL户外旅游网站课程设计全流程实战与踩坑记录
2026/9/7 14:41:59 网站建设 项目流程

这段时间正好在做一套JSP蚂蚁户外旅游网站(g303g),属于非常典型Java Web课程设计项目:程序、源码、数据库脚本、调试部署说明、开发环境整套都齐了。这类项目我接触过不少,很多同学拿到的其实就是一个压缩包,里面有代码、SQL文件和一份部署文档,但真要从零跑起来、跑通、改明白,还是有不少坑。这篇就把这个项目的技术拆解、实操过程和踩坑记录完整写一遍,给正在做课程设计或者想搞懂JSP项目全链路的朋友做个参考。

先说清楚这套东西的价值在哪里。JSP蚂蚁户外旅游网站不是一个花里胡哨的商业项目,而是一个刚好的教学综合体:它有用户注册登录、景点与线路展示、在线预订、订单管理、后台管理、留言功能,基本把Java Web开发的核心知识点全串起来了——JSP页面渲染、Servlet请求处理、JavaBean封装、JDBC数据库操作、Session会话管理、Filter过滤器,一样都不少。对初学者来说,把这套项目从头到尾跑通一遍,Java Web的知识点基本就通了七成;对要做课程设计的人来说,它又是一个随时能改、能扩展、能写进报告里的完整选题。

1. 项目整体设计与思路拆解

1.1 技术选型:为什么这套组合至今仍然是课程设计的稳妥方案

JSP蚂蚁户外旅游网站用的技术组合是:JSP + Servlet + JavaBean + MySQL + Tomcat,最多再加一个JSTL标签库和EL表达式。不少人会问,现在企业里都SpringBoot了,为什么课程设计还在用这套老组合?这里我的观点很明确:学习阶段用这套组合,恰恰是性价比最高的选择。

原因不复杂。SpringBoot这类框架做了大量“自动配置”,你写一个@RequestMapping注解,请求就能被处理,但底层Servlet容器是怎么把请求路由过来的、请求参数是怎么封装的、响应又是怎么写回浏览器的,这些全被框架藏得干干净净。而JSP + Servlet则是一套“毛坯房”式技术栈,你写一个HttpServlet子类,重写doGetdoPost方法,使用request.getParameter()拿参数,使用request.getRequestDispatcher().forward()做页面跳转——每一步的逻辑链条都摆在明面上。这就好比学开车先学手动挡,虽然累一点,但你能真正理解发动机和变速箱是怎么配合的,以后开自动挡才会心里有数。

把话说得再直白一些:做课题设计的核心目标是掌握Java Web的底层运转机制,而不是搭一个开箱即用的脚手架。用JSP这套组合,涉及HTTP请求生命周期、Servlet实例化、Session会话管理、JDBC数据库连接这些底层概念,强制你亲手去处理。这套项目做完,再去看SpringMVC的DispatcherServlet、MyBatis的SqlSession,你会觉得“原来如此”,因为底层逻辑你早就见过原貌了。

1.2 功能模块划分:用户端与管理员端的边界

这套蚂蚁户外旅游网站在功能规划上走了教科书式的两段式设计:用户端和管理员端。用户端是游客和注册用户看得到的部分,管理员端是网站运营者用来维护数据的后台。这种前后台分离的架构非常经典,几乎所有内容型网站的基因都是这样。

用户端的功能设计得非常接地气,完全贴合“户外旅游”这个题材:

  • 用户注册与登录:注册时校验用户名是否重复、密码长度、两次输入是否一致,登录后把用户信息放进Session,用于后续识别“当前是谁”。
  • 景点展示:首页展示热门景点图片和简介,点击进入景点详情页,能看到景点的地理位置、开放时间、门票价格等详细数据。
  • 旅游线路列表:按照线路主题、价格区间分类展示,支持按关键字模糊搜索。
  • 线路预订与订单管理:用户选择线路后填写出行人数、出发日期,提交生成订单,在我的订单里可以查看订单状态、取消未处理的预订。
  • 个人中心:展示当前登录用户的信息,支持修改个人资料,同时列出自己的历史订单和留言记录。
  • 留言评价:用户可以对某个景点或线路发表留言,管理员审核后在前台展示。

管理员端则是典型的CRUD操作集合:

  • 管理员登录:使用独立的管理员表,不走用户端登录入口,管理员登录后Session里存放的也是独立标识。
  • 用户管理:查看注册用户列表、禁用或启用账号。
  • 景点管理:对景点信息做增删改查,上传景点图片。
  • 线路管理:发布、修改、下架旅游线路,设置线路价格、天数、出发城市。
  • 订单管理:查看所有用户提交的订单,标记已确认或已完成。
  • 公告管理:发布网站公告,公告显示在用户端首页。

两个端之间通过权限控制隔离,普通用户访问管理员页面会直接跳回登录页。功能清单用一张表归纳会更直观一些:

功能模块用户端管理员端涉及的核心表
登录注册注册、登录、退出管理员登录user / admin
内容展示景点、线路、公告浏览景点/线路/公告维护scenic / route / notice
预订业务提交预订、查看订单订单状态管理orders
用户管理个人信息维护用户列表、状态管理user
留言互动提交留言留言审核、回复comment

1.3 三层架构与文件组织:分层的价值在于以后改起来不头疼

看这套项目的源码,最能体现“设计感”的一点就是分层清晰。标准的JSP项目通常采用JSP + Servlet + DAO三件套来完成一次完整的请求处理链路。我拿“景点列表页”来举例,这个链路是:浏览器请求list.jsp,页面通过Servlet获取数据,Servlet调用DAO层的findAll()方法查询数据库,把结果封装成List集合放进request作用域,再转发回JSP页面,由JSP结合JSTL标签循环渲染表格或卡片。

这里最关键的分层逻辑是:JSP只负责显示,不写SQL、不写业务判断;Servlet只负责接收请求、调用业务方法、决定跳转到哪个页面;DAO只负责数据库交互,不关心页面长什么样。三层各管一摊,职责边界非常清楚。实际项目里很多同学喜欢在JSP里直接写<% ... %>脚本片段拼接HTML,这种写法在小项目里看似省事,但一旦页面需要调整布局,你就会发现Java代码和HTML标签纠缠在一起,改起来极其痛苦。这个项目采用JSTL + EL表达式来替代页面里的Java脚本,JSP页面里几乎看不到<% %>,这就非常规范,排版和后期维护体验好非常多。

源码的包结构设计也是典型的教学型分层:实体类放在entitymodel包里,DAO类放在dao包,Servlet控制器分散在servletcontroller包里,工具类如数据库连接工具、字符串处理工具放在util包。看着清爽,学起来也容易照猫画虎。

2. 核心细节解析与实操要点

2.1 数据库设计的核心:七张表之间的关联关系

这个项目的数据库设计可以说是整个课题设计的灵魂。一套旅游网站的数据库脚本,通常包含七张核心表:用户表(user)、管理员表(admin)、景点表(scenic)、线路表(route)、订单表(orders)、留言表(comment)、公告表(notice)。我在拿到SQL脚本后第一件事就是画表关系图,把每张表的主外键理清楚。

用户表和订单表是一对多关系,一个用户可以下多个订单;线路表和订单表也是一对多关系,一条线路可以被多个用户预订;用户表和留言表是一对多关系,用户可以对不同景点发表留言;景点表和线路表可以做成一对多,一个景点可以出现在多条线路中,也可以简化成互不关联的两张独立表,看课题要求而定。

订单表设计时有一个非常值得注意的细节:它除了记录用户ID和线路ID外,还会冗余存储一份出行人数、出发日期、订单金额、联系人姓名、联系电话、订单状态等字段。有人说冗余字段违反了数据库范式,但我强烈建议订单表保留这些信息。原因很简单,订单是业务快照,用户在提交订单时填写了出行人数和日期,如果这些数据通过线路表去关联查询,一旦管理员后面改了线路价格,订单历史记录就“变味”了。把下单那一刻的关键信息冗余进订单表,从业务角度才是最稳妥的做法。

用户表通常包含id、username、password、nickname、phone、email、avatar、status、create_time。其中密码字段这里要专门说一下:项目源码里的密码存储用的不是明文,而是经过MD5加密后的字符串。很多人会问,MD5不是已经被证明不安全了吗?这里要解释清楚,课程设计用MD5本身没问题,因为教学重点在于让初学者理解“密码不能明文落库”这个原则,而不是追求极致安全。真正上线项目要用BCrypt这类加盐算法,那是后话,但“密码不能明文存储”的意识必须从第一个项目就开始建立。我在实操时给这个项目加了加盐处理的工具类,在注册时把salt + password拼起来做MD5,登录时用同样的规则重新计算比对,效果很好。

景点表和线路表的结构就比较直观了,景点表包含景点名称、图片URL、简介、详细描述、所在城市、门票价格、开放时间、创建时间,线路表则包含线路名称、线路图片、出发城市、目的地、行程天数、价格、线路简介、是否上架、发布时间。公告表简单一些,就标题、内容、发布时间三个核心字段。注意日期字段统一用datetime类型而不用varchar,不要给自己挖排序和比较的坑。

2.2 个人信息展示页:Session、EL表达式与JSTL的三方协作

JSP里做“当前登录用户的个人信息展示”这个功能,在热词里被点名的频率非常高。我看到“jsp个人信息展示页面”这个检索词时,就知道很多人在这里卡住了。其实这个功能非常基础,核心就三步:登录成功后把用户对象放入Session,个人信息页从Session里取出,用EL表达式输出。我贴一下关键代码片段,这是一个标准的登录Servlet里的写法:

// 登录成功后 User user = dao.findByUsernameAndPassword(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("loginUser", user); response.sendRedirect("index.jsp"); } else { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); }

个人信息展示页里,通过${sessionScope.loginUser.nickname}这样的EL表达式直接输出当前登录用户的昵称。这里sessionScope是EL表达式的隐含对象,专门用来从Session作用域取数据。页面里还可以用JSTL的<c:if>标签做条件判断,比如判断用户是否登录、判断用户在Session里是否存在,再决定显示“登录/注册”按钮还是显示“欢迎xxx”下拉菜单:

<c:if test="${not empty sessionScope.loginUser}"> <span>欢迎回来,${sessionScope.loginUser.nickname}</span> </c:if> <c:if test="${empty sessionScope.loginUser}"> <a href="${pageContext.request.contextPath}/login.jsp">登录</a> </c:if>

这里要特别提醒一个新手最容易踩的坑:EL表达式取不到值。最常见的原因有三个,第一是Session里的key写错了,Servlet存的时候叫loginUser,页面上取的时候写成了loginUser少个字母,这属于低级错误但真的会发生;第二是pageContext.request.contextPath没有加,导致项目路径变化后静态资源或跳转地址变成404;第三是JSP页面头部缺少isELIgnored="false"指令,不过新版的Tomcat默认支持EL,老版本配置才需要关注。每一条我都实际处理过,建议大家遇到EL不显示值的时候,按照这三个方向排查。

2.3 视频播放与数据导出:两个高频加分的扩展功能

热词里出现了“jsp实现mp4视频播放”和“jsp实现数据导出为excel”,这两个功能虽然不是这个项目的核心功能,但作为课程设计的加分项非常常见。很多同学拿到一个旅游网站项目后,想加一个“景点视频展示”或者“订单数据导出”来做亮点,这里我把实现思路一起讲透。

先说JSP页面上播放MP4视频。最简单可靠的方式就是用HTML5的<video>标签,JSP页面本质上是HTML模板,原生支持:

<video width="640" height="360" controls> <source src="${pageContext.request.contextPath}/upload/videos/xxx.mp4" type="video/mp4"> </video>

这里最核心的不是<video>标签本身,而是视频文件的存放路径与访问方式。如果把视频直接放在项目的webapp/upload/videos目录下,通过Tomcat直接访问静态资源就行;但如果是管理员后台通过文件上传功能上传的视频,文件通常会被写入服务器硬盘的某个物理目录,此时需要配置Tomcat的虚拟路径映射,才能让浏览器能通过URL访问到不在项目目录里的文件。这个映射在IDEA里配置Tomcat时可以设置Deployment下的External Source,也可以在server.xml里加<Context>配置。很多同学视频加载不出来,一看控制台报404,基本就是路径映射的问题。

再说数据导出Excel。JSP生态里做Excel导出最常用的库是Apache POI。实现思路也不复杂,在Servlet里查询出订单列表,用POI创建HSSFWorkbookSheetRowCell,循环把数据写入单元格,最后通过response的输出流把文件写回浏览器。关键代码模板长这样:

// 设置响应头 response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setHeader("Content-Disposition", "attachment;filename=orders.xls"); // 创建Excel文档 HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet("订单列表"); // 创建表头行、写入数据行... // 写入输出流 workbook.write(response.getOutputStream()); workbook.close();

里面最容易被忽视的细节有两个。第一是Content-Disposition里的filename如果要支持中文文件名,需要对文件名做URL编码处理,否则下载下来的文件会是一串乱码;第二是POI操作完workbook后一定要关闭连接,否则文件占用、内存泄漏的问题会在高并发导出时暴露出来。这个扩展功能很值得做,因为它在课程设计答辩时能直观展示你的数据操作能力和“需求转化能力”,比干巴巴地讲页面切换有趣得多。

2.4 请求流转链路:从表单到数据库再到页面的全路径

理解一个JSP项目,最核心的就是一条请求链路。我拿“线路预订”这个最核心的业务场景来完整走一遍流程,把这个看懂,基本就掌握了JSP开发的运转模式。

用户在线路详情页填写出发人数和出行日期,点击提交预订按钮,浏览器以POST方式请求bookServlet

  1. 表单数据被Tomcat封装到request对象里,Servlet通过request.getParameter("routeId")request.getParameter("num")request.getParameter("date")取出参数;
  2. Servlet从Session中取出当前登录用户,判断用户是否已登录,未登录跳回登录页并提示“请先登录”;
  3. Servlet调用订单DAO的insert()方法,把订单信息写入数据库;
  4. 插入成功后,通过response.sendRedirect("orderList.jsp")重定向到订单列表页面,也就是用户下单后能看到自己订单的页面;
  5. 订单列表页面被JSP渲染时,通过订单DAO的findByUserId(userId)方法查出订单集合,放进request作用域,然后用JSTL的<c:forEach>循环渲染表格。

这条链路里有个非常经典的设计考点——为什么用sendRedirect重定向而不是forward转发?我的回答是:如果使用转发,用户在订单列表页刷新浏览器时,表单会再次被提交,同一个订单会被创建两次,这就是所谓的“表单重复提交问题”。重定向的本质是浏览器重新发起一次GET请求,请求地址是订单列表页面,原来的POST请求已经结束,即使刷新也只是刷新列表页,不会重复创建订单。这个问题在答辩时被问到的概率极高,你把这个逻辑讲清楚,答辩老师就知道你是真的理解了这个项目的运行机制,而不是背代码。

3. 实操过程与核心环节实现

3.1 开发环境从零搭建:JDK、IDEA、Tomcat、Maven、MySQL

拿到g303g这个项目的压缩包后,第一步不是打开代码,而是确认开发环境。很多时候代码报错、项目跑不起来,问题恰恰出在环境版本不匹配上。这套项目我建议按以下的版本组合去搭建:

  • JDK:8。这个版本是Java Web课程的绝对主力,稳定、资料多、Tomcat兼容性最好。如果你装了更高版本的JDK,比如JDK 17,运行老项目可能会遇到java.lang.NoClassDefFoundError之类的报错,因为老项目依赖的某些库和模块系统有冲突。
  • IDE:IDEA 2022(社区版或旗舰版均可)。社区版免费,支持JSP项目的开发调试,对本项目完全够用。旗舰版在数据库工具有图形化界面,但配合Navicat也够用。
  • Tomcat:8.5。老项目的默认选择。如果你用Tomcat 10,注意命名空间变化——javax.servlet变成了jakarta.servlet,老代码直接编译不过,这确实是个让人头疼的问题,所以稳妥起见不要用Tomcat 10来跑老项目。
  • Maven:3.6或3.8。做项目依赖管理。
  • MySQL:5.7或8.0。这个项目同时兼容两种版本,但在连接配置上有驱动版本和URL参数的区别,下文单独讲。
  • 数据库可视化工具:Navicat或DataGrip。

JDK环境配置这里顺带提一句:安装完JDK后,一定要在系统变量里配置JAVA_HOME指向JDK安装目录,在Path变量中添加%JAVA_HOME%\bin。配置完成后在命令行输入java -version验证,能输出版本号说明配好了。很多同学卡在这一步,是因为只安装了JDK没有配环境变量,或者配置了但没重新打开命令行窗口,环境变量不生效。IDEA 2022版本其实可以自动识别系统里的JDK路径,但它识别的是标准的JDK目录,不规范的安装位置有可能识别不到,所以自己配一遍最放心。

3.2 创建Maven Web项目与配置Tomcat运行

环境就绪后,在IDEA里创建一个新的Maven项目,选择maven-archetype-webapp骨架。这个骨架会自动生成标准的Web项目目录结构:src/main/java放Java源码,src/main/resources放配置文件,src/main/webapp放JSP页面和静态资源,pom.xml是Maven的依赖管理文件。很多同学拿到别人的源码包后不知道从哪下手,其实就是把这个目录结构和Maven的标准结构对应起来,把源码复制进对应的目录路径即可。

pom.xml核心依赖需要手动添加JSP和Servlet相关的库。注意Servlet API和JSP API在打包时不能打进war包,因为Tomcat自身已经提供这些类库,如果强行加入会发生类冲突。标准配置是加provided作用域:

<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>

如果用到Druid连接池,再加一个com.alibaba:druid:1.2.8。Maven依赖配置好之后,需要等待IDEA自动下载这些jar包,首次下载比较慢,建议配置阿里云镜像源加速。具体做法是在Maven的settings.xml里添加镜像配置,这个网上教程很多,实操中能省下大量等待时间。

Tomcat运行配置这一步也很关键。在IDEA里选择Run -> Edit Configurations -> + -> Tomcat Server -> Local,配置Tomcat主目录,然后在Deployment页签添加Artifact。这里有一个细节:部署描述符里的Application context默认是项目名,比如/g303g_war_exploded,访问地址就是http://localhost:8080/g303g_war_exploded/。很多人访问返回404,大概率就是这个上下文路径不对。建议把它改成简洁的名字,比如/g303g,这样访问路径更清晰,也不会因为目录名过长而手滑输错。

3.3 数据库导入与连接池配置

数据库导入是最容易出问题的一步。我拿到SQL脚本后,会用Navicat打开MySQL连接,右键选择“新建数据库”,命名为g303g_travel,字符集选择utf8mb4,排序规则选utf8mb4_general_ci,然后在这个数据库上右键“运行SQL文件”,选择项目提供的g303g.sql脚本完成导入。导入成功后检查一下表是否完整,正常会出现7张表左右,如果有表缺失,多半是SQL脚本执行过程中报错退出,需要看错误信息定位是哪一条SQL出了问题。

数据库连接配置在项目里一般有两种位置:一种是写在src/main/resources/db.properties配置文件中,另一种是直接写在JDBC工具类里。我更推荐前一种,因为它把变化的部分(数据库地址、账号、密码)和代码逻辑分离,部署到新环境时只需要改配置不需要动代码,这对课程设计和实际项目都是好习惯。一个典型的db.properties配置长这样:

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/g303g_travel?useUnicode=true&characterEncoding=utf8&useSSL=false jdbc.username=root jdbc.password=123456

如果数据库用的是MySQL 8.0,驱动类名要换成com.mysql.cj.jdbc.Driver,而且连接URL里最好加上serverTimezone=Asia/Shanghai参数,否则会报时区错误。这个区别非常典型:用8.0驱动时,原先的com.mysql.jdbc.Driver虽然能通过编译,但运行时会抛出ClassNotFoundException。JDBC工具类里通过Class.forName()加载数据库驱动,然后用DriverManager.getConnection()获取连接。连接操作相对繁琐,通常会把获取连接和关闭连接这两个操作封装到一个DBUtil工具类里,各DAO直接调用即可。

3.4 核心代码实现解读:注册、预订、后台管理

源码里最值得钻研的三个功能是用户注册、线路预订和管理员登录拦截。用户注册功能的重点在用户名查重和密码安全存储。注册Servlet收到用户名后,先调用UserDAO的findByUsername()方法判断用户名是否已存在,存在则返回注册页并提示用户更换用户名;不存在则把用户提交的密码做MD5加密后和用户信息一起封装成User对象,调用插入方法写入数据库。注册成功后,一般有两种处理策略:直接跳转到登录页让用户登录,或者直接把用户信息存入Session实现“注册即登录”。这个项目采用的策略是注册成功后自动跳转到登录页,更符合“先注册后登录”的标准流程。

线路预订功能涉及两张表的操作:插入订单表记录,同时更新线路表的“已报名人数”或“库存”字段。这个过程中必须使用事务,否则可能出现订单插入成功但线路人数没更新,数据库处于不一致状态。谈及JDBC事务,默认情况下Connection是自动提交模式,每执行一条SQL就立即提交;要控制事务,先把conn.setAutoCommit(false)关掉自动提交,然后按顺序执行多条SQL,全部成功就commit()提交,任何一步失败就rollback()回滚。为保证所有操作使用同一个Connection对象,不能在每个DAO方法内部单独获取连接,而是要在Service层获取连接后传入DAO层,或者使用ThreadLocal方案把连接绑定到当前线程。课程设计阶段用最简单的“Service获取连接 + 传入DAO”方案即可。

管理员登录拦截功能的核心是一个Filter过滤器。通过实现javax.servlet.Filter接口,在doFilter方法里判断当前Session里是否存在管理员标识,如果没有就重定向到管理员登录页,如果有就调用filterChain.doFilter()放行。在web.xml里配置过滤器作用于/admin/*路径下的所有请求,这样只要访问后台路径,都必须先经过登录校验,普通游客无法直接通过URL访问后台页面。这个功能在答辩时也是很亮眼的点,因为它在讲“安全控制”这个主题时有天然的说服力。

3.5 调试部署全流程:从IDEA调试到war包发布

开发过程中最常用的就是IDEA的Debug模式。在Servlet代码某一行设置断点,以Debug模式启动Tomcat,浏览器发起请求后,程序会停在断点位置,此时可以查看request参数、Session内容、数据库查询结果的每一个值。排查“为什么这里查不到数据”“为什么条件判断不成立”这类问题,Debug模式是最快的工具,比到处加System.out.println()靠谱得多。

项目开发完成之后,需要部署到独立的Tomcat环境里跑起来,像交付给老师演示或者部署到服务器这种场景,一般用war包部署。在IDEA里执行Build -> Build Artifacts -> 选择g303g:war -> Build,IDEA就会在target目录下生成g303g.war文件。把这个war包复制到Tomcat安装目录的webapps文件夹下,启动Tomcat,它会自动解压war包并部署应用,访问地址是http://localhost:8080/g303g/。很多人在这一步会遇到一个情况:war包解压成功,但页面样式错乱,图片全部加载不出来。这是因为JSP页面里引用的CSS、JS、图片路径用了绝对路径,但没有加上项目上下文。解决办法是统一在页面头部加<base>标签,或者在所有路径前拼上${pageContext.request.contextPath}

4. 常见问题与排查技巧实录

4.1 JSP改了不生效:三处缓存都要清

热词里“jsp改了不生效”这个搜索词出现的频率非常高,这不是个别现象,几乎每个做JSP项目的人都会遇到。现象就是改了JSP页面代码,重新刷新浏览器,页面还是老样子。我总结下来有三个层次的原因:

第一,IDEA的Tomcat运行配置里没有开启Update resources热部署。在Deployment页签的On frame deactivation下拉菜单里选Update resources,这样IDE窗口失去焦点时,Tomcat会自动把修改过的静态资源和JSP文件同步到部署目录,再配合浏览器强制刷新(Ctrl+F5)往往就能生效。第二,浏览器本身的缓存。JSP页面虽然是动态渲染的,但很多浏览器对imagecssjs这类静态资源会做缓存,地址没变就直接用缓存。开发阶段可以开启浏览器开发者工具里的Disable cache选项。第三,target目录下的class文件没有被重新编译。这种情况最隐蔽,Java代码编译产物没有更新,你改的Servlet代码不生效,怎么刷新都没用。对策是按ctrl+shift+F9重新编译当前模块,或者干脆在Maven面板里执行clean这个生命周期指令清掉target目录再重新构建。

我给出的检查顺序是:先看Tomcat控制台有没有重新部署日志,再看浏览器Network面板里请求的响应码,最后看target目录里class文件的修改时间。这套排查流程走下来,基本能定位到是哪一层缓存出了问题。

4.2 数据库连不上的三类原因:驱动、URL、字符集

数据库连接报错是JSP项目的重灾区。最常见的报错信息是java.lang.ClassNotFoundException: com.mysql.jdbc.Driver,这说明驱动类没有被加载到类路径里。排查方向是Maven依赖是否已经引入MySQL驱动,如果pom.xml里没有这个依赖,或者依赖加了但IDEA没有刷新(右下角会提示导入),驱动就不会出现在External Libraries里。

第二个高频问题是java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES),这属于账号密码或者权限问题。db.properties里的数据库密码和本地MySQL实际密码不一致,是生产环境里的头号嫌疑犯。还有些人用的是MySQL 8.0默认的caching_sha2_password认证插件,老驱动不兼容的情况下也需要换驱动版本或者改认证插件。

第三个容易忽略的问题是连接URL里的字符集参数。如果URL没有加characterEncoding=utf8,程序往数据库写入中文时会出现乱码,或者查询时中文条件匹配不上。数据库连接URL其实是一个很挑剔的东西,每个参数都有它存在的意义,useUnicode=truecharacterEncoding=utf8两个参数配合使用,才能保证中文正常读写。

我整理了一个问题定位速查表,实操时对照着查能节省不少时间:

报错现象可能原因排查与解决方向
ClassNotFoundException驱动未引入或版本不匹配检查pom.xml依赖,刷新Maven项目
Access denied用户名或密码错误核对db.properties和MySQL实际密码
Unknown database数据库名不存在确认建库名称和URL中名称一致
Connection refusedMySQL未启动或端口不对检查MySQL服务状态,核对端口3306
Communications link failure连接超时或网络中断检查MySQL服务是否存活,重启试试
Server returns invalid timezone时区偏移错误URL加serverTimezone=Asia/Shanghai

4.3 Tomcat启动报错:端口占用与重复部署

Tomcat默认端口是8080,如果本机已经有一个Tomcat实例在运行,IDEA再次启动就会出现端口占用报错:Port 8080 was already in use。解决办法有两个方向,一个是关掉占用端口的进程,命令行执行netstat -ano | findstr 8080查看占用端口的进程PID,再到任务管理器中结束该进程;另一个是修改项目的Tomcat配置,把HTTP端口改成8081或自定义端口。实操中很多人遇到的情况是,之前启动的Tomcat进程并没有完全退出,确认任务管理器里没有残留的java.exeTomcat进程再重启最稳。

还有一种特殊情况:部署时选择了war包,同时也选择了war exploded,造成同一应用部署两次,IDEA控制台会报Cannot resolve artifact或者应用路径冲突。解决方式是检查Run Configuration里的Deployment页签,只保留一个Artifact,不要在同一个配置里同时添加war和war exploded两种部署方式。

4.4 中文乱码:一套编码原则打天下

涉及到中文乱码的问题,看着五花八门,原因其实高度统一:数据从浏览器到数据库,中间经过的所有环节编码不统一。我习惯把乱码分成“页面显示乱码”“请求参数乱码”“数据库存取乱码”三种场景来处理。

页面显示乱码的解决办法是保证JSP文件本身是UTF-8编码,同时页面头部设置<%@ page contentType="text/html;charset=UTF-8" %>指令。IDEA里如果文件编码不统一,可以在Settings -> Editor -> File Encodings里把项目编码全部设为UTF-8,这一步要在一开始建项目时就检查到位。

请求参数乱码分GET和POST两种。POST请求乱码的处理方式是在Servlet开头加request.setCharacterEncoding("UTF-8"),或者更规范的做法是写一个全局编码过滤器,在web.xml里配置,让所有请求在进入Servlet前都完成编码设置。GET请求乱码则是另一个机制,Tomcat 8及以上版本默认用UTF-8解码URI,一般没问题;如果是老版本Tomcat,需要在server.xml的Connector节点上配置URIEncoding="UTF-8"

数据库存取乱码上面已经提到,连接URL加上characterEncoding=utf8,同时数据库本身的字符集设置为utf8mb4。只要“页面编码、请求编码、连接编码、数据库编码”这四处全是UTF-8,中文乱码就能彻底根治,一个套路可通用于所有JSP项目。

4.5 部署到外部Tomcat后404与静态资源加载失败

项目在IDEA里跑得好好的,一部署到外部Tomcat就出现问题,这种现象也很常见。404异常通常是因为访问路径没带项目上下文。IDEA里Tomcat配置的Application context如果是/g303g,访问路径就是http://localhost:8080/g303g/,如果你直接访问http://localhost:8080/,Tomcat返回的是它默认的首页,你当然看不到项目页面。一定要知道自己的项目上下文是什么,并确保URL和它一致。

静态资源加载失败则是路径写法问题。最稳妥的解决方案,是在JSP页面顶部加一个base标签改写基础路径:

<% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <base href="<%=basePath%>">

加了base标签之后,页面里所有相对路径都会以项目根路径为基准进行解析,不管项目部署在哪个上下文目录下都能正确加载,这个方法同样适用于war包部署到服务器后的场景。部署到独立Tomcat后,我强烈建议直接访问JSP页面时留意控制台有没有FileNotFound之类的异常,经常是路径问题在控制台输出里已经给出提示了,只是很多人不习惯看Tomcat的catalina日志。

5. 避坑心得与扩展建议

跑到这,这套JSP蚂蚁户外旅游网站在我手上已经算是彻底跑通了。从最初拿到压缩包、搭建环境、导入数据库、配置Tomcat,到后来读懂每个Servlet、改页面、加功能、打war包,整个过程走了两三天,踩过的坑在上文都写清楚了。回头看这个项目,我最真实的感受是:它的难度并不在代码本身,而在于把整个链路串起来的能力——环境要对、数据库要对、路径要对、部署要对,任何一个环节掉链子,页面就是出不来。但也正因为这样,它才是一个好的练习项目,你在解决这些问题的过程中获得的经验,远比代码本身值钱。

这里补充几个平时少有人提但很实用的个人习惯。第一,每次对项目做了较大改动,在IDEA里重启Tomcat之前,先执行一次Maven的clean,把target目录里的旧class清干净,能避开八成“改了不生效”的坑。第二,拿到别人的源码后不要急着跑,先用文本编辑器打开SQL脚本看里面的建表语句,确认表名前缀、核心字段是否与源码里的DAO方法对得上,很多项目跑不起来就是表和实体类对不上,提前发现能省很多事。第三,部署到外部Tomcat时,启动后多看一眼logs/catalina.out日志文件,Tomcat几乎所有异常都会如实记录在那里,比在浏览器里猜原因高效得多。

后续如果时间和精力允许,可以考虑给这个项目做几个低成本但收益高的扩展。比如接入百度地图API,在景点详情页显示景点地理位置,这个对“户外旅游”主题非常加分;或者把数据库连接方式从原生JDBC改成MyBatis,加深对ORM框架的掌握;前端也可以引入Bootstrap框架把页面做精致一点,视觉体验提升之后整个项目的完成度观感完全不一样。不管选哪个方向,都能在这个课设项目的基础上再上一个台阶。做项目就像带团爬一座山,先把氧气瓶检查好、路线图看好、队友照顾好——环境弄对、依赖配好、路径理清,爬到山顶只是时间问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询