☰
JSP+Servlet+MySQL九宫格日志网站毕业设计实战
2026/10/19 6:02:00 网站建设 项目流程

简介:本资源是一套完整的JavaWeb毕业设计项目源码及配套材料,面向计算机专业本科生、Java初学者与课程设计实践者,解决毕业设计选题难、系统开发流程不清晰、前后端功能模块整合经验不足等实际问题。压缩包为ZIP格式,大小25.18MB,包含JSP动态页面、Servlet控制逻辑、MySQL建表脚本、数据库备份文件、系统设计论文(含需求分析、架构设计、ER图与测试用例)、答辩PPT及项目部署说明文档,覆盖从编码实现到成果展示的全链路交付内容。目前已有58人学习下载,适合用于课程设计参考、毕设快速启动或JSP+MySQL+B/S架构的实战复现。读者可直接导入Tomcat运行系统,完整体验管理员后台(含用户、日记、美食、景点、新闻、论坛、收藏、留言等12类管理模块)与前台门户(首页、信息分类展示、个人中心、后台跳转等)双端交互逻辑,同时获得规范的软件工程文档范本与可调试源码结构。

1. 九宫格日志网站:不是炫技的UI,而是毕业设计里最扛压的JSP全栈练兵场

你有没有试过——在答辩前48小时,导师突然说:“把后台权限控制再加一层角色隔离,明天要演示给企业评委看”?这个九宫格日志网站就是那种能让你临危不乱的项目:它用最朴素的JSP+Servlet+MySQL组合,把B/S架构的完整闭环(用户登录→前台九宫格导航→内容CRUD→后台多角色管理→日志与收藏联动)全打穿了。它不是为高并发设计的,但胜在结构清晰、模块解耦明确、每条SQL都可查、每个JSP跳转路径都可追溯。适合两类人:一是Java Web零基础但需要快速交出一份“有后台、有数据库、有前后台分离感”的毕业设计的同学;二是想亲手拆解一个真实可用的JSP老项目、补全Servlet生命周期理解、搞懂Filter如何统一拦截登录态的开发者。它不讲Spring Boot自动装配,但教你手写一个BaseServlet怎么通过反射调用子类方法;它不用MyBatis,但让你看清ResultSet怎么一行行映射成JavaBean;它没上Redis缓存,却把<c:forEach>和<c:if>在JSP层的条件渲染逻辑写得明明白白。这不是过时的技术栈,而是一套经得起现场调试、改得了字段、加得进新模块的“可演进式”教学级工程。


2. 项目结构与技术选型:为什么是JSP而不是Spring Boot?——一个务实的选择

这个项目没有用任何现代框架,但它绝不是“凑合”。它的技术选型背后,是一整套面向教学落地的权衡逻辑:JSP负责视图层的直观性,Servlet承担控制器职责的可控性,原生JDBC直连MySQL保证数据库操作的透明性,而整个B/S结构则让部署成本降到最低——Tomcat 8.5 + MySQL 5.7,两台虚拟机就能跑通全部功能。这种“裸写”方式,反而让初学者能真正看清HTTP请求从浏览器发出,到web.xml匹配URL-pattern,再到doGet/doPost执行,最后RequestDispatcher.forward()跳转JSP的完整链路。下面我们就从目录结构开始,一层层剥开这个项目的骨架。

2.1 标准Web应用目录结构:/WEB-INF是你的安全边界

项目采用标准Java Web目录布局,关键路径如下:

/WebRoot ├── index.jsp ← 前台首页,九宫格入口页 ├── login.jsp ← 统一登录页(含管理员/普通用户双入口) ├── logout.jsp ← 登出逻辑页 ├── /css/ ← 全局CSS(含九宫格grid布局核心样式) ├── /js/ ← 基础JS(表单校验、分页跳转、收藏状态切换) ├── /images/ ← 图标与占位图(九宫格每个模块对应独立icon) ├── /WEB-INF/ │ ├── web.xml ← 核心配置:servlet映射、filter定义、welcome-file-list │ ├── /lib/ ← 仅3个jar:mysql-connector-java-5.1.47.jar、jstl-1.2.jar、standard-1.1.2.jar │ └── /classes/ ← 编译后的.class文件(按包名分层存放) ├── /admin/ ← 后台管理页面集合(所有.jsp都在此) │ ├── admin_index.jsp ← 后台首页,含12个功能模块快捷入口 │ ├── user_list.jsp ← 用户管理列表页(带搜索+分页) │ └── ... ← 其他管理页命名规则统一:{模块名}_list/edit/add.jsp └── /user/ ← 普通用户专属页面(如个人中心、我的收藏) ├── user_center.jsp └── my_collection.jsp

提示:所有业务JSP页面(除index.jsp和login.jsp外)均放在/WEB-INF/下,这是硬性安全措施——外部无法直接通过URL访问这些页面,必须经由Servlet转发,避免绕过权限校验。

2.2web.xml核心配置:Filter统一拦截 + Servlet精准路由

web.xml是整个项目的“交通指挥中心”,其关键配置如下:

<!-- 1. 全局字符编码Filter --> <filter> <filter-name>EncodingFilter</filter-name> <filter-class>filter.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 2. 登录状态Filter(重点!) --> <filter> <filter-name>LoginFilter</filter-name> <filter-class>filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/admin/*</url-pattern> <!-- 后台所有路径 --> <url-pattern>/user/*</url-pattern> <!-- 用户中心所有路径 --> </filter-mapping> <!-- 3. Servlet映射(示例:日记管理) --> <servlet> <servlet-name>DiaryServlet</servlet-name> <servlet-class>servlet.DiaryServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DiaryServlet</servlet-name> <url-pattern>/diary</url-pattern> <!-- 所有日记操作走此路径 --> </servlet-mapping>

这段配置决定了三个关键行为:

  • 字符编码统一处理:避免中文参数乱码,EncodingFilter在doFilter()中强制设置request.setCharacterEncoding("UTF-8")和response.setContentType("text/html;charset=UTF-8");
  • 登录态强校验:LoginFilter会检查session.getAttribute("user")是否存在且不为null,若未登录则重定向至login.jsp,并保留原始请求URL(存入session供登录后跳转);
  • URL路由集中化:所有业务逻辑由Servlet接管,JSP只负责展示,彻底分离关注点——比如访问/diary?action=list,由DiaryServlet解析action参数,调用diaryService.list(),再将结果存入request.setAttribute("diaryList", list),最后forward到/admin/diary_list.jsp。

2.3 数据库设计:12张表如何支撑九宫格模块?

MySQL数据库共12张表,严格对应12个功能模块,命名规范统一为tb_{模块名}(如tb_diary、tb_food、tb_scenic)。核心设计原则是:每个模块一张主表 + 一张关联表(如收藏、评论)。以“我的收藏”为例:

-- 主模块表:日记信息(tb_diary) CREATE TABLE tb_diary ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, content TEXT, author_id INT NOT NULL, -- 关联tb_user.id create_time DATETIME DEFAULT NOW(), status TINYINT DEFAULT 1 -- 1=发布, 0=草稿 ); -- 收藏关联表:tb_collection(通用设计,支持跨模块收藏) CREATE TABLE tb_collection ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, -- 收藏者 target_id INT NOT NULL, -- 被收藏的目标ID(可能是日记id/美食id/景点id) target_type VARCHAR(20) NOT NULL, -- 目标类型:'diary'/'food'/'scenic' create_time DATETIME DEFAULT NOW(), UNIQUE KEY uk_user_target (user_id, target_id, target_type) );

这种设计带来两个实际好处:

  1. 扩展性强:新增“电影推荐”模块时,只需建tb_movie表,并在tb_collection.target_type中增加'movie'值,无需修改收藏逻辑;
  2. 查询高效:用户查看“我的收藏”时,SQL可写为:
SELECT c.id, c.target_type, d.title AS diary_title, f.name AS food_name FROM tb_collection c LEFT JOIN tb_diary d ON c.target_type='diary' AND c.target_id=d.id LEFT JOIN tb_food f ON c.target_type='food' AND c.target_id=f.id WHERE c.user_id = ?

虽用LEFT JOIN,但因target_type字段有索引,实际性能可接受。

2.4 JSP九宫格实现:CSS Grid + 动态URL拼接

前台首页index.jsp的九宫格并非图片堆砌,而是语义化HTML+CSS Grid布局,每个格子动态生成:

<div class="grid-container"> <c:forEach items="${moduleList}" var="module" varStatus="status"> <div class="grid-item"> <a href="${pageContext.request.contextPath}/${module.url}"> <img src="${pageContext.request.contextPath}/images/${module.icon}.png" alt="${module.name}"> <p>${module.name}</p> </a> </div> </c:forEach> </div>

其中moduleList由IndexServlet从数据库读取(或硬编码在Servlet中),结构为:

List<Map<String, String>> moduleList = new ArrayList<>(); Map<String, String> diary = new HashMap<>(); diary.put("name", "日记信息"); diary.put("url", "diary?action=list"); // 指向DiaryServlet diary.put("icon", "diary"); moduleList.add(diary); // ... 其他11个模块同理 request.setAttribute("moduleList", moduleList);

注意:<c:forEach>标签需在JSP顶部声明<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,且web.xml中<web-app>根节点必须声明version="3.0"以上,否则JSTL不生效。

2.5 后台管理首页:admin_index.jsp的模块入口逻辑

/admin/admin_index.jsp是管理员视角的“总控台”,其核心是动态生成12个功能模块卡片。每个卡片包含:

  • 模块名称(如“用户管理”)
  • 操作按钮(列表/添加/统计)
  • 实时数据摘要(如“用户总数:236”、“今日新增日记:12”)

数据摘要部分通过AJAX异步加载,避免首屏阻塞:

// admin_index.jsp 中的JS片段 function loadSummary() { $.get("${pageContext.request.contextPath}/summary", function(data) { $("#userCount").text(data.userCount); $("#diaryToday").text(data.diaryToday); }); } $(document).ready(function() { loadSummary(); setInterval(loadSummary, 60000); // 每分钟刷新一次 })

对应的SummaryServlet中,通过多个COUNT(*)查询汇总数据,并封装为JSON返回(使用response.getWriter().write(jsonString)),不依赖任何JSON库——纯手写字符串拼接,简单可靠。


3. 核心模块代码逐行解析:从登录到日记发布的完整链路

现在我们聚焦最核心的业务闭环:用户登录 → 进入日记列表 → 发布新日记。这条链路覆盖了Filter拦截、Servlet分发、JDBC操作、JSP渲染四大环节,是理解整个项目的关键切口。

3.1 登录流程:LoginFilter如何守住第一道门?

filter.LoginFilter的doFilter()方法是权限控制的起点:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; // 1. 放行静态资源和登录相关页面 String uri = req.getRequestURI(); if (uri.endsWith("/login.jsp") || uri.endsWith("/LoginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/images/")) { chain.doFilter(request, response); return; } // 2. 检查Session中是否有登录用户 User user = (User) req.getSession().getAttribute("user"); if (user == null) { // 3. 未登录:保存原始请求URL,重定向到登录页 String originalUrl = req.getRequestURL().toString(); if (req.getQueryString() != null) { originalUrl += "?" + req.getQueryString(); } req.getSession().setAttribute("originalUrl", originalUrl); resp.sendRedirect(req.getContextPath() + "/login.jsp"); return; } // 4. 已登录:放行,继续执行后续Filter或Servlet chain.doFilter(request, response); }

关键点说明:

  • 放行策略:/login.jsp和/LoginServlet必须放行,否则形成死循环;静态资源(CSS/JS/IMG)放行是为了页面正常渲染;
  • 原始URL保存:originalUrl存入session,供LoginServlet验证成功后跳转回原页面(如用户点击“写日记”按钮时未登录,登录后应自动跳转到/diary?action=add);
  • Session失效处理:若用户长时间无操作导致session过期,再次访问受保护路径时,getAttribute("user")返回null,同样触发重定向,体验连贯。

3.2 登录验证:LoginServlet如何完成身份核验?

LoginServlet的doPost()方法处理表单提交:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); String userType = request.getParameter("userType"); // 'admin' or 'user' UserService userService = new UserService(); User user = null; if ("admin".equals(userType)) { user = userService.loginAdmin(username, password); } else { user = userService.loginUser(username, password); } if (user != null) { // 登录成功:存入session,跳转目标页 request.getSession().setAttribute("user", user); String originalUrl = (String) request.getSession().getAttribute("originalUrl"); if (originalUrl != null && !originalUrl.isEmpty()) { response.sendRedirect(originalUrl); request.getSession().removeAttribute("originalUrl"); // 清理 } else { // 默认跳转:管理员到后台首页,普通用户到前台首页 String redirectUrl = "admin".equals(userType) ? request.getContextPath() + "/admin/admin_index.jsp" : request.getContextPath() + "/index.jsp"; response.sendRedirect(redirectUrl); } } else { // 登录失败:返回错误信息 request.setAttribute("errorMsg", "用户名或密码错误!"); request.getRequestDispatcher("/login.jsp").forward(request, response); } }

参数说明与踩坑点:

  • userType来自登录页的radio按钮,区分管理员与普通用户,调用不同DAO方法;
  • userService.loginAdmin()内部执行SQL:SELECT * FROM tb_admin WHERE username=? AND password=MD5(?)(密码明文MD5存储,毕业设计常见做法,非生产环境建议加盐);
  • 重要细节:request.getRequestDispatcher(...).forward()用于服务端跳转,保持request作用域;而response.sendRedirect(...)是客户端重定向,会丢失request属性——此处失败时用forward,可将errorMsg传回login.jsp显示;成功时用sendRedirect,避免F5刷新重复提交。

3.3 日记列表:DiaryServlet如何分页查询?

DiaryServlet是典型的CRUD Servlet,action=list分支负责分页展示:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); if ("list".equals(action)) { // 1. 获取分页参数 int currentPage = 1; int pageSize = 10; try { currentPage = Integer.parseInt(request.getParameter("page")); } catch (NumberFormatException e) { /* 忽略,用默认值 */ } // 2. 查询总记录数(用于计算总页数) DiaryService diaryService = new DiaryService(); int totalCount = diaryService.getCount(); // SELECT COUNT(*) FROM tb_diary WHERE status=1 // 3. 查询当前页数据 List<Diary> diaryList = diaryService.listByPage(currentPage, pageSize); // 4. 计算分页信息 int totalPage = (int) Math.ceil((double) totalCount / pageSize); request.setAttribute("diaryList", diaryList); request.setAttribute("currentPage", currentPage); request.setAttribute("totalPage", totalPage); request.setAttribute("totalCount", totalCount); // 5. 转发到JSP request.getRequestDispatcher("/admin/diary_list.jsp").forward(request, response); } }

分页SQL实现(DiaryDao.java):

public List<Diary> findByPage(int start, int size) { String sql = "SELECT d.*, u.username FROM tb_diary d " + "LEFT JOIN tb_user u ON d.author_id = u.id " + "WHERE d.status = 1 ORDER BY d.create_time DESC LIMIT ?, ?"; return queryRunner.query(sql, new BeanListHandler<>(Diary.class), start, size); }
  • start = (currentPage - 1) * pageSize,MySQL的LIMIT偏移量从0开始;
  • LEFT JOIN关联作者用户名,避免在JSP中循环查用户表(N+1问题);
  • ORDER BY d.create_time DESC保证最新日记在前。

3.4 发布日记:表单提交与事务处理

前台发布日记的流程在/user/diary_add.jsp中:

<form action="${pageContext.request.contextPath}/diary" method="post"> <input type="hidden" name="action" value="add"/> <label>标题:<input type="text" name="title" required/></label> <label>内容:<textarea name="content" required></textarea></label> <button type="submit">发布</button> </form>

DiaryServlet.doPost()处理提交:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); if ("add".equals(action)) { // 1. 封装参数 String title = request.getParameter("title"); String content = request.getParameter("content"); User user = (User) request.getSession().getAttribute("user"); // 2. 构建日记对象 Diary diary = new Diary(); diary.setTitle(title); diary.setContent(content); diary.setAuthorId(user.getId()); diary.setStatus(1); // 发布状态 // 3. 事务处理(关键!) QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource()); Connection conn = null; try { conn = qr.getDataSource().getConnection(); conn.setAutoCommit(false); // 开启事务 // 插入日记 String insertSql = "INSERT INTO tb_diary (title, content, author_id, status, create_time) VALUES (?, ?, ?, ?, NOW())"; Object[] params = {title, content, user.getId(), 1}; qr.update(conn, insertSql, params); // 记录操作日志(可选,增强审计) String logSql = "INSERT INTO tb_operation_log (user_id, operation, target_id, create_time) VALUES (?, ?, LAST_INSERT_ID(), NOW())"; qr.update(conn, logSql, user.getId(), "ADD_DIARY"); conn.commit(); // 提交事务 request.setAttribute("msg", "日记发布成功!"); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) {} } request.setAttribute("msg", "发布失败:" + e.getMessage()); e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (SQLException e) {} } } request.getRequestDispatcher("/user/diary_add.jsp").forward(request, response); } }

事务要点说明:

  • 使用QueryRunner的update(Connection, ...)重载方法,显式传入Connection,确保多条SQL在同一个事务中;
  • LAST_INSERT_ID()获取刚插入日记的自增ID,用于写入操作日志,避免二次查询;
  • try-catch-finally确保连接正确关闭,即使发生异常也不泄露数据库连接。

4. 避坑指南:那些让我在凌晨三点对着Tomcat日志抓狂的5个问题

这个项目看似简单,但在实际部署和调试过程中,有5个高频问题几乎每个使用者都会撞上。我把它们按“现象→原因→解决”列出来,全是血泪经验,照着做能省下至少6小时排查时间。

4.1 现象:登录后跳转到空白页,地址栏显示/admin/admin_index.jsp,但页面什么也不显示

原因:admin_index.jsp中引用了<c:forEach>等JSTL标签,但web.xml的<web-app>版本声明低于3.0,或/WEB-INF/lib/下缺少jstl-1.2.jar与standard-1.1.2.jar。JSP引擎无法解析JSTL标签,直接忽略整段代码,导致九宫格区域为空。
解决:

  1. 检查web.xml第一行是否为<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">;
  2. 确认/WEB-INF/lib/下存在两个jar(大小约:jstl-1.2.jar 490KB,standard-1.1.2.jar 390KB);
  3. 在admin_index.jsp顶部添加<%@ page isELIgnored="false" %>,强制启用EL表达式。

4.2 现象:添加日记时中文乱码,数据库里存的是问号(????)

原因:三个地方未统一UTF-8:① Tomcat的server.xml中Connector未配置URIEncoding="UTF-8";② MySQL连接URL未加useUnicode=true&characterEncoding=UTF-8;③EncodingFilter未对POST请求设置request.setCharacterEncoding("UTF-8")。
解决:

  1. 修改Tomcat/conf/server.xml,在<Connector port="8080" ... />中添加URIEncoding="UTF-8";
  2. 检查JdbcUtils.java中的连接URL:jdbc:mysql://localhost:3306/logdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8;
  3. 确保EncodingFilter.doFilter()中request.setCharacterEncoding("UTF-8")在chain.doFilter()之前执行(位置不能错)。

4.3 现象:后台管理页点击“用户管理”报404,URL是/admin/user_list.jsp

原因:web.xml中未配置<welcome-file-list>,或user_list.jsp不在/WEB-INF/下,导致Tomcat无法定位该文件。根据安全规范,所有管理页必须放在/WEB-INF/内,但web.xml中<welcome-file-list>只列出index.jsp,未覆盖/admin/路径。
解决:

  • 将user_list.jsp等所有管理页移入/WEB-INF/admin/目录;
  • 在web.xml中添加<welcome-file-list>,但不要添加user_list.jsp(它不是欢迎页);
  • 正确做法:所有管理页必须由Servlet转发访问,例如/admin/user由UserServlet处理,再forward到/WEB-INF/admin/user_list.jsp。检查web.xml中UserServlet的<url-pattern>是否为/admin/user,而非/admin/user_list.jsp。

4.4 现象:收藏功能点击无反应,浏览器控制台报$ is not defined

原因:/js/目录下的jQuery库未被正确引入,或<script>标签顺序错误(如先引入自定义JS,后引入jQuery)。my_collection.jsp中使用了$.get(),但jQuery未加载。
解决:

  1. 确认/WebRoot/js/jquery-3.6.0.min.js文件存在(大小约87KB);
  2. 在my_collection.jsp顶部<head>中,按顺序引入:
<script src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script> <script src="${pageContext.request.contextPath}/js/collection.js"></script>
  1. 检查浏览器开发者工具Network标签,确认jquery-3.6.0.min.js返回状态码为200。

4.5 现象:管理员登录后,点击“系统管理”里的“清理日志”,页面报错java.lang.ClassNotFoundException: servlet.ClearLogServlet

原因:ClearLogServlet.java源文件存在,但未编译成.class文件,或编译后的.class未放入/WEB-INF/classes/servlet/目录。Tomcat启动时扫描/WEB-INF/classes/,找不到该类即抛此异常。
解决:

  1. 在IDE中右键项目 →Build Project(Eclipse)或Build→Build Project(IntelliJ),确保ClearLogServlet.class生成;
  2. 检查输出目录(如/WebRoot/WEB-INF/classes/servlet/ClearLogServlet.class)是否存在;
  3. 若手动复制,注意包路径:servlet.ClearLogServlet对应/WEB-INF/classes/servlet/ClearLogServlet.class,不能错放成/WEB-INF/classes/ClearLogServlet.class。

5. 二次开发实战:如何在30分钟内为九宫格新增“电影推荐”模块?

毕业设计答辩常被问:“如果要加一个新模块,比如‘电影推荐’,你怎么快速实现?”这个问题的本质,是考察你对MVC分层和模块复用的理解。下面我带你实操一遍,从数据库建表到前台展示,全程不碰已有代码,只增不改。

5.1 第一步:数据库层面——建表与初始化

新建电影主表tb_movie和电影分类表tb_movie_category(为后续扩展留余地):

-- 电影主表 CREATE TABLE tb_movie ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, director VARCHAR(50), actors TEXT, score DECIMAL(2,1), -- 评分,如8.5 category_id INT, -- 关联分类 description TEXT, cover_url VARCHAR(200), -- 封面图URL create_time DATETIME DEFAULT NOW() ); -- 电影分类表 CREATE TABLE tb_movie_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL UNIQUE ); -- 初始化分类 INSERT INTO tb_movie_category (name) VALUES ('动作'), ('喜剧'), ('爱情'), ('科幻');

注意:cover_url字段存相对路径(如/images/movie/interstellar.jpg),方便前端直接<img src="${movie.coverUrl}">。

5.2 第二步:Java层——复用现有模式,只写新类

遵循项目原有命名规范,新建4个类(全部放在src/对应包下):

类型包名类名说明
EntityentityMovie.java字段与tb_movie列一一对应,含id,title,score等
DAOdaoMovieDao.java继承BaseDao<Movie>,实现listByPage(),findById()等
ServiceserviceMovieService.java调用MovieDao,封装业务逻辑(如按分类查询)
ServletservletMovieServlet.java处理/movie?action=list/add/edit,转发到对应JSP

关键复用点:

  • MovieDao直接复制DiaryDao.java,仅替换SQL中的表名和字段;
  • MovieService结构与DiaryService.java完全一致,方法签名相同(List<Movie> listByPage(int, int));
  • MovieServlet的doGet/doPost逻辑与DiaryServlet雷同,仅将Diary替换为Movie,diaryService替换为movieService。

5.3 第三步:JSP层——最小化改动,复用九宫格模板

  1. 前台首页添加电影入口:在index.jsp的moduleList中追加一项:
Map<String, String> movie = new HashMap<>(); movie.put("name", "电影推荐"); movie.put("url", "movie?action=list"); movie.put("icon", "movie"); moduleList.add(movie);
  1. 后台管理页添加入口:在/admin/admin_index.jsp中,找到九宫格区域,插入新卡片HTML(复制其他卡片结构,仅改文字和链接):
<div class="grid-item"> <a href="${pageContext.request.contextPath}/admin/movie_list.jsp"> <img src="${pageContext.request.contextPath}/images/movie.png" alt="电影推荐"> <p>电影推荐</p> </a> </div>
  1. 创建管理页面:新建/admin/movie_list.jsp,复用diary_list.jsp结构,仅替换变量名:
<c:forEach items="${movieList}" var="movie"> <tr> <td>${movie.title}</td> <td>${movie.director}</td> <td>${movie.score}</td> <td> <a href="${pageContext.request.contextPath}/movie?action=edit&id=${movie.id}">编辑</a> | <a href="${pageContext.request.contextPath}/movie?action=delete&id=${movie.id}" onclick="return confirm('确定删除?')">删除</a> </td> </tr> </c:forEach>

5.4 第四步:配置与部署——3个文件搞定路由

  1. web.xml中注册Servlet:
<servlet> <servlet-name>MovieServlet</servlet-name> <servlet-class>servlet.MovieServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MovieServlet</servlet-name> <url-pattern>/movie</url-pattern> </servlet-mapping>
  1. web.xml中补充Filter映射(若需权限控制):
<filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/movie</url-pattern> <!-- 让MovieServlet也受登录保护 --> </filter-mapping>
  1. 重启Tomcat,访问/movie?action=list即可看到空列表,后台/admin/movie_list.jsp可增删改。

5.5 验证与调试:如何确认新增模块已生效?

  • 数据库验证:执行SELECT COUNT(*) FROM tb_movie;,确认表存在且可写入;
  • Servlet验证:直接浏览器访问http://localhost:8080/yourapp/movie?action=list,应返回JSON或跳转到movie_list.jsp(取决于Servlet逻辑);
  • JSP验证:打开/admin/admin_index.jsp,检查“电影推荐”卡片是否显示,点击是否跳转到movie_list.jsp;
  • 日志验证:查看Tomcat日志logs/catalina.out,搜索MovieServlet,确认无ClassNotFoundException或SQLException。

从那以后我每次接到“加个新模块”的需求,都强制走一遍这个流程:先建表→再写Entity/DAO/Service/Serlvet→最后配web.xml和JSP。哪怕只有10分钟,我也先把MovieServlet的doGet()骨架写出来,打印一句System.out.println("MovieServlet invoked");,然后启动Tomcat curl一下,看到控制台输出,心里就踏实了——因为我知道,骨架立住了,血肉只是时间问题。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询