ARTICLE DETAIL

资讯详情

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

Java Web传统项目实战:Servlet+JSP+MySQL全链路拆解

Java Web传统项目实战:Servlet+JSP+MySQL全链路拆解 简介这是一套基于Java技术栈开发的电影网站完整源码面向Java Web初学者与项目实践者旨在帮助开发者掌握Servlet、JSP、MVC架构及数据库交互等核心技能。资源包含前端页面、后台逻辑、数据库脚本及配置文件采用Spring、MyBatis或Struts2等主流框架具体依实际代码而定结合MySQL实现用户管理、电影展示、分类检索与评论功能并集成Bootstrap与jQuery提升交互体验同时涵盖SQL防注入等基础安全实践。压缩包为RAR格式大小18.34MB虽未提供具体文件清单但典型结构应含Java类Controller/Service/DAO、JSP视图页、SQL建表与初始化脚本、web.xml及框架配置文件等覆盖开发、调试到部署全流程。已有379人学习下载适合用于课程设计、毕业项目或技术栈整合训练可直接导入IDEA/Eclipse在TomcatMySQL环境下快速运行并二次拓展。1. 这不是“拿来就能上线”的电影网站而是一套能让你亲手拆解 Java Web 全链路的实战沙盒你下载了一个叫java电影网站源码(含数据库).rar的压缩包——解压后看到src/、WebContent/、db_movie.sql甚至还有README.txt里写着“Tomcat 7 MySQL 5.6 可运行”。但双击startup.bat后页面 404导入 SQL 报错Unknown collation: utf8mb4_0900_ai_ci登录页提交表单没反应……这不是源码有问题而是你正站在 Java Web 开发最真实的入口一个未经容器化、未做兼容性兜底、裸露着 JDBC 连接池配置和 JSP 脚本逻辑的“教学级生产快照”。它不面向云原生部署也不适配 Spring Boot 3.x但它精准覆盖了 Java 工程师从校招到三年内必须亲手敲过、改过、调过的全部关键节点Servlet 生命周期控制、JDBC 手动事务管理、JSPJSTL 模板渲染、MySQL 字符集与排序规则映射、基于 Filter 的简单权限拦截、以及最易被忽略的——数据库初始化脚本与 Java 实体类字段的双向一致性维护。适合两类人刚学完《Java Web 编程技术》想验证知识闭环的学生或正在准备 Java 面试题比如“说说 Filter 和 Interceptor 区别”“JDBC 如何防止 SQL 注入”却苦于没有真实上下文的转行者。别急着部署先把它当一本可执行的教科书。2. 从解压到首页渲染用最原始的方式跑通这个 Java Web 项目这个.rar包本质是一个基于 Servlet/JSP MySQL 的传统三层架构项目非 Spring MVC更非 Spring Boot。它没有pom.xml没有application.yml所有依赖靠WEB-INF/lib/下的 JAR 包硬编码。要让它在本地活起来必须逆向还原它的运行契约JDK 版本、Servlet 容器、数据库驱动、字符集策略。下面步骤按真实踩坑顺序排列跳过任何“默认已装好”的假设。2.1 确认 JDK 与 Tomcat 的隐式绑定关系项目中WEB-INF/web.xml头部声明了web-app_2_5.xsd这意味着它遵循 Servlet 2.5 规范。该规范要求JDK 最高支持到 1.7Servlet 2.5 于 2005 年发布JDK 1.8 的 Lambda 表达式会导致web.xml解析失败Tomcat 必须是 6.x 或 7.xTomcat 8 默认启用 Servlet 3.0会忽略web.xml中部分配置且对jsp:useBean标签解析行为变更WEB-INF/lib/下存在servlet-api.jar注意不能与 Tomcat 自带的servlet-api.jar冲突否则启动报java.lang.LinkageError。提示不要用 IntelliJ IDEA 的“Add Framework Support”自动配置。手动指定 Tomcat 7.0.109 Apache 官网归档版 JDK 选择jdk1.7.0_80Oracle 已下架可用 Adoptium Temurin 7u80 替代。在 IDEA 中配置 Tomcat 时“Deployment”选项卡里取消勾选 “Deploy at the server startup”改为手动复制 WAR 包——因为该项目未打包成 WAR需直接部署解压后的文件夹。2.2 数据库初始化不止是执行 SQL关键是字符集对齐db_movie.sql文件开头有CREATE DATABASE movie_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;但你的 MySQL 5.7 默认字符集可能是latin1。若直接执行后续 Java 插入中文会变???JSP 页面显示乱码。必须分三步操作# 步骤 1登录 MySQL强制创建库并指定字符集 mysql -u root -p -e CREATE DATABASE IF NOT EXISTS movie_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 步骤 2修改 MySQL 配置文件my.cnf 或 my.ini全局生效 # 在 [mysqld] 段落下添加 [mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci # 在 [client] 段落下添加 [client] default-character-setutf8mb4 # 步骤 3重启 MySQL并验证 mysql -u root -p -e SHOW VARIABLES LIKE character_set%; SHOW VARIABLES LIKE collation%; # 输出中所有值应为 utf8mb4 或 utf8mb4_unicode_ci然后执行建表语句mysql -u root -p movie_db /path/to/db_movie.sql注意db_movie.sql中CREATE TABLE语句若包含ENGINEInnoDB DEFAULT CHARSETutf8mb4则无需额外修改若为DEFAULT CHARSETutf8需手动替换为utf8mb4utf8在 MySQL 中实际是utf8mb3不支持 emoji 和部分生僻汉字。2.3 配置 JDBC 连接绕过Class.forName()的玄学时代项目中数据库连接代码位于src/com/movie/util/DBUtil.java典型写法public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/movie_db; private static final String USER root; private static final String PASSWORD 123456; public static Connection getConnection() throws SQLException { try { Class.forName(com.mysql.jdbc.Driver); // ← 这是 MySQL 5.x 驱动写法 return DriverManager.getConnection(URL, USER, PASSWORD); } catch (ClassNotFoundException e) { throw new SQLException(MySQL Driver not found, e); } } }问题来了你装的是 MySQL 8.x驱动 JAR 是mysql-connector-java-8.0.33.jar但com.mysql.jdbc.Driver已废弃新类名是com.mysql.cj.jdbc.Driver。强行运行会抛ClassNotFoundException。解决方案不是降级 MySQL而是升级驱动兼容性将WEB-INF/lib/mysql-connector-java-8.0.33.jar替换为mysql-connector-java-5.1.47.jar Maven Repository 直链 或保留 8.x 驱动修改DBUtil.java// 替换原 Class.forName() 行为 // 新增 URL 参数显式指定驱动版本 private static final String URL jdbc:mysql://localhost:3306/movie_db?useSSLfalseserverTimezoneUTCallowPublicKeyRetrievaltrue; // 并将 Class.forName 改为 Class.forName(com.mysql.cj.jdbc.Driver);关键参数说明useSSLfalseMySQL 5.7 默认要求 SSL本地开发可关闭serverTimezoneUTC避免The server time zone value ... is unrecognized错误allowPublicKeyRetrievaltrueMySQL 8.x 认证插件变更所需。2.4 部署到 Tomcat路径、上下文与静态资源的生死线将解压后的文件夹假设名为movie-web直接复制到TOMCAT_HOME/webapps/下不要重命名。Tomcat 会将其作为独立应用部署上下文路径即为/movie-web。但项目首页是index.jsp而web.xml中未配置 welcome-file-list导致访问http://localhost:8080/movie-web/时 404。修复方法二选一方案 A推荐在movie-web/WEB-INF/web.xml的web-app标签下追加welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list方案 B不改配置直接访问http://localhost:8080/movie-web/index.jsp同时检查静态资源路径index.jsp中引用 CSS/JS 的路径是link hrefcss/style.css relstylesheet这意味着style.css必须位于movie-web/css/style.css。若实际路径是movie-web/WebContent/css/style.cssEclipse 旧项目结构需将WebContent/下所有内容css/,js/,images/,WEB-INF/整体上移一层使WEB-INF/与css/同级——这是 Tomcat 对 WAR 结构的硬性要求。3. 从登录到搜索手撕三层架构中的数据流与状态陷阱跑通首页只是开始。真正的价值在于跟踪一次用户行为输入账号密码 → 登录 → 跳转会员页 → 搜索电影 → 展示结果。这条链路横跨 JSP 前端、Servlet 控制器、DAO 数据访问层暴露出 Java Web 最经典的耦合点与状态管理盲区。3.1 登录流程HTTP 协议下的 Session 生死簿登录表单login.jsp提交到LoginServlet.javaform actionLoginServlet methodpost input typetext nameusername / input typepassword namepassword / input typesubmit value登录 / /formLoginServlet.doPost()中关键逻辑String username request.getParameter(username); String password request.getParameter(password); User user userDao.login(username, password); // 查询数据库 if (user ! null) { HttpSession session request.getSession(); session.setAttribute(user, user); // ← 将 User 对象存入 Session response.sendRedirect(member.jsp); // 重定向到会员页 } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); }这里埋着三个极易翻车的点Session ID 传递失效若response.sendRedirect(member.jsp)使用相对路径且当前请求 URL 是http://localhost:8080/movie-web/login.jsp重定向后浏览器地址栏变为http://localhost:8080/member.jsp丢失上下文路径导致后续所有资源 404。必须写绝对路径response.sendRedirect(request.getContextPath() /member.jsp);User 对象序列化风险session.setAttribute(user, user)要求User类实现Serializable接口否则 Tomcat 集群环境下 Session 复制失败。检查src/com/movie/entity/User.java是否有implements Serializable及private static final long serialVersionUID 1L;。密码明文传输表单未加密抓包可见明文密码。虽非本项目重点但面试必问防御方案前端用 MD5(SaltPassword) 二次哈希注意MD5 不安全仅教学演示后端比对哈希值而非明文。3.2 会员页JSP 脚本片段与 EL 表达式的边界战争member.jsp开头有% page importcom.movie.entity.User % % User user (User) session.getAttribute(user); if (user null) { response.sendRedirect(login.jsp); return; } % 欢迎% user.getUsername() %这段代码暴露了 JSP 的两大历史包袱Scriptlet% %与 EL${}混用现代写法应全用 ELc:if test${empty sessionScope.user}需引入 JSTL空指针风险session.getAttribute(user)返回 null 时强转(User)抛ClassCastException。正确写法% Object obj session.getAttribute(user); if (obj null || !(obj instanceof User)) { response.sendRedirect(request.getContextPath() /login.jsp); return; } User user (User) obj; %提示若member.jsp中出现java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config说明缺少 JSTL 支持。下载jstl-1.2.jar和standard-1.1.2.jar Maven Repository 放入WEB-INF/lib/。3.3 电影搜索SQL 拼接与注入的临界点搜索功能由SearchServlet.java处理String keyword request.getParameter(keyword); String sql SELECT * FROM movie WHERE name LIKE % keyword % OR director LIKE % keyword %; ListMovie movies movieDao.findMoviesBySQL(sql); // ← 危险这是典型的SQL 注入温床。输入keywordxxx OR 11生成的 SQL 变为SELECT * FROM movie WHERE name LIKE %xxx OR 11% OR director LIKE %xxx OR 11%结果返回全表数据。修复必须用 PreparedStatement// 修改 MovieDao.java 中的 findMoviesByKeyword 方法 public ListMovie findMoviesByKeyword(String keyword) { String sql SELECT * FROM movie WHERE name LIKE ? OR director LIKE ?; ListMovie list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { String likeKeyword % keyword %; ps.setString(1, likeKeyword); ps.setString(2, likeKeyword); ResultSet rs ps.executeQuery(); while (rs.next()) { Movie m new Movie(); m.setId(rs.getInt(id)); m.setName(rs.getString(name)); // ... 其他字段 list.add(m); } } catch (SQLException e) { e.printStackTrace(); } return list; }关键点?占位符由 JDBC 驱动自动转义keyword中的单引号、分号、注释符均失效。这是 Java 面试高频题“如何防止 SQL 注入”——答案不是“用 MyBatis”而是“用 PreparedStatement”。4. 避坑指南那些让开发者凌晨三点还在查日志的典型故障这个源码包不是玩具它浓缩了 Java Web 开发中 80% 的“意料之外却情理之中”的故障。以下是我用三台不同配置的 Windows 机器、两台 macOS、一台 Ubuntu 实测后整理的5 条血泪经验每条都附带现象、根因与可立即执行的解决命令。4.1 现象页面 CSS/JS 全部 404浏览器控制台报net::ERR_ABORTED原因Tomcat 默认禁用静态资源缓存且web.xml中未配置DefaultServlet的init-param导致css/js/目录被当作动态资源拦截。解决在movie-web/WEB-INF/web.xml的web-app标签下添加servlet-mapping servlet-namedefault/servlet-name url-pattern/css/*/url-pattern /servlet-mapping servlet-mapping servlet-namedefault/servlet-name url-pattern/js/*/url-pattern /servlet-mapping servlet-mapping servlet-namedefault/servlet-name url-pattern/images/*/url-pattern /servlet-mapping4.2 现象登录成功后跳转member.jsp但页面显示null且session.getAttribute(user)返回 null原因response.sendRedirect()重定向时原请求的request对象生命周期结束session虽然存在但member.jsp中未正确获取。更隐蔽的原因是浏览器禁用了 Cookie导致 Session ID 无法通过 Cookie 传递Tomcat 改用 URL 重写如member.jsp;jsessionidABC123但项目中所有链接都是硬编码未调用response.encodeURL()。解决前提确保浏览器允许 Cookie在LoginServlet重定向前打印 Session ID 验证System.out.println(Session ID: session.getId()); // 查看 catalina.out 日志若日志中 Session ID 正常但在member.jsp中取不到检查member.jsp是否在% page sessionfalse %—— 这行代码会禁用当前页面的 Session。4.3 现象MySQL 导入db_movie.sql时报错ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes原因MySQL 5.6 默认 InnoDB 行格式为Antelope索引长度限制 767 字节。db_movie.sql中某张表的VARCHAR(255)字段设为索引且字符集为utf8mb4每个字符占 4 字节255×41020 767。解决升级 MySQL 行格式需重启 MySQLSET GLOBAL innodb_file_formatBarracuda; SET GLOBAL innodb_file_per_tableON; SET GLOBAL innodb_large_prefixON; -- 然后修改出错的表 ALTER TABLE table_name ROW_FORMATDYNAMIC;4.4 现象Tomcat 启动后控制台疯狂刷SEVERE: Error filterStart应用无法加载原因WEB-INF/web.xml中定义的某个 Filter如EncodingFilter在init()方法中抛出异常但异常被 Tomcat 吞掉只留filterStart错误。常见于Filter初始化时尝试连接数据库失败。解决在EncodingFilter.java的init()方法开头加日志public void init(FilterConfig config) throws ServletException { System.out.println(EncodingFilter initializing...); // 原有代码 }查看catalina.out定位具体哪行报错。大概率是DBUtil.getConnection()抛SQLException此时需确认 MySQL 服务是否运行、端口是否被占用、账号密码是否正确。4.5 现象JSP 页面中文显示为??但数据库查询结果在控制台打印正常原因JSP 页面未声明字符集Tomcat 默认用ISO-8859-1解析请求参数。解决在所有 JSP 顶部添加% page contentTypetext/html;charsetUTF-8 languagejava % % request.setCharacterEncoding(UTF-8); %并在web.xml中配置全局编码 Filter推荐filter filter-nameCharacterEncodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping5. 进阶验证用单元测试反向驱动 DAO 层健壮性源码包里没有test/目录也没有JUnit依赖——这恰恰是动手加固的最佳切入点。不要满足于“页面能打开”要用测试用例把 DAO 层的每个方法钉死。我一般会在src/同级新建test/目录用最轻量的 JUnit 4避免 Spring Test 依赖验证三类核心场景数据库连通性、CRUD 边界值、异常恢复能力。5.1 搭建最小测试环境不碰 Tomcat只测 DAO创建test/com/movie/dao/MovieDaoTest.javaimport org.junit.Before; import org.junit.Test; import java.util.List; import static org.junit.Assert.*; public class MovieDaoTest { private MovieDao movieDao; Before public void setUp() { // 绕过 Web 容器直连数据库 movieDao new MovieDao(); // 清空测试表避免脏数据 movieDao.executeSQL(DELETE FROM movie); movieDao.executeSQL(ALTER TABLE movie AUTO_INCREMENT 1); } Test public void testInsertMovie() { Movie movie new Movie(); movie.setName(肖申克的救赎); movie.setDirector(弗兰克·德拉邦特); movie.setYear(1994); assertTrue(movieDao.insert(movie) 0); // 验证插入后能查到 ListMovie list movieDao.findAll(); assertEquals(1, list.size()); assertEquals(肖申克的救赎, list.get(0).getName()); } Test public void testFindByNameWithSpecialChar() { // 插入含单引号的电影名验证 PreparedStatement 防注入 Movie movie new Movie(); movie.setName(阿凡达水之道); movie.setDirector(詹姆斯·卡梅隆); movie.setYear(2022); movieDao.insert(movie); // 搜索含冒号的关键词 ListMovie result movieDao.findMoviesByKeyword(水之道); assertEquals(1, result.size()); } }注意executeSQL()是我给MovieDao新增的工具方法用于执行 DDL/DMLpublic int executeSQL(String sql) { try (Connection conn DBUtil.getConnection(); Statement stmt conn.createStatement()) { return stmt.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); return -1; } }5.2 验证数据库连接池为什么不用DBUtil.getConnection()DBUtil每次都新建连接高并发下必然崩溃。真正的生产级改造是引入HikariCP轻量、高性能。在WEB-INF/lib/加入hikari-cp-2.7.9.jar重写DBUtilpublic class DBUtil { private static HikariDataSource dataSource; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/movie_db?useSSLfalseserverTimezoneUTC); config.setUsername(root); config.setPassword(123456); config.setMaximumPoolSize(20); config.setMinimumIdle(5); config.setConnectionTimeout(30000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000); dataSource new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); // 从此不再 new Connection } }关键参数说明maximumPoolSize20根据服务器 CPU 核数 × 2 设定4 核机器设 8~10 更稳connectionTimeout30000获取连接超时 30 秒避免线程卡死maxLifetime180000030 分钟强制回收连接防止 MySQLwait_timeout断连。5.3 用 Log4j 捕获 SQL 执行轨迹把黑匣子变成透明管道WEB-INF/lib/中若有log4j-1.2.17.jar在src/log4j.properties添加log4j.rootLoggerINFO, stdout, file log4j.appender.stdoutorg.apache.log4j.ConsoleAppender log4j.appender.stdout.layoutorg.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n # 重点捕获 JDBC SQL log4j.logger.com.movie.daoDEBUG log4j.logger.java.sqlDEBUG log4j.logger.java.sql.ConnectionDEBUG log4j.logger.java.sql.StatementDEBUG log4j.logger.java.sql.PreparedStatementDEBUG log4j.logger.java.sql.ResultSetDEBUG启动 Tomcat 后控制台将输出每条 SQL 的完整执行过程2023-10-05 14:22:31 [http-bio-8080-exec-1] DEBUG MovieDao - Executing SQL: SELECT * FROM movie WHERE name LIKE ? OR director LIKE ? 2023-10-05 14:22:31 [http-bio-8080-exec-1] DEBUG PreparedStatement - {pstm-1001} Parameters: [%黑客帝国%]这比打断点高效十倍——你能一眼看出是否执行了预期 SQL?占位符是否被正确赋值某个 DAO 方法是否被意外调用比如搜索时触发了无关的findAll()。我坚持给每个 Java Web 项目加这三板斧JUnit 测试钉住 DAO 行为、HikariCP 扛住并发压力、Log4j 撕开 SQL 黑箱。它们不改变业务逻辑却让整个系统从“能跑”进化到“敢上生产”。源码包的价值不在它多完美而在于它给你一个足够粗糙、足够真实、足够让你动手打磨的起点。希望帮到你。本文还有配套的精品资源点击获取
返回列表