ARTICLE DETAIL

资讯详情

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

JSP+Servlet动漫论坛实战:从零部署到避坑指南

JSP+Servlet动漫论坛实战:从零部署到避坑指南 简介本资源是一套完整的基于JSP技术开发的动漫论坛毕业设计项目面向计算机专业本科生及Java Web初学者解决课程设计、毕设选题与实战能力提升需求。资源包含论文、源码、数据库脚本、说明文档及配套视频覆盖从需求分析、系统设计到部署运行的全流程。压缩包共15个文件含5张功能界面截图如管理员登录、分区管理、回复帖子等、3段实操视频后端/前端功能介绍及项目部署、2份Word论文与任务书、1份PPT答辩稿、1个SQL建库脚本、1个源码ZIP包及1个使用说明TXT总大小162.6MB。已有927人学习下载读者可直接复现完整MVC架构的JSPMySQL论坛系统获取可运行源码、规范论文模板、模块化功能演示及部署指导特别适合理解用户权限分级、帖子CRUD、前后端交互等核心Web开发实践。1. 为什么今天还要用 JSP 做动漫论坛——一个被低估的轻量级 Web 方案落地实录你可能刚在招聘网站上看到“熟悉 JSP/Servlet 技术栈”仍出现在中小型企业后台开发岗 JD 里也可能在 GitHub 搜索 “anime forum” 时发现近三个月仍有 7 个新开源项目把index.jsp放在根目录更可能——你正被导师或甲方压着交一份「基于 JSP 的动漫论坛」毕业设计或内部系统而你手头只有 Tomcat 9、MySQL 5.7 和一份模糊的“MVC 分层要求”。这不是怀旧是现实JSP 并未死亡它只是退守到教育闭环、内网管理后台、轻量 CMS 快速原型等具体场景中以极低的环境依赖和清晰的请求链路成为新手理解 Web 请求生命周期最不抽象的入口。本方案不是教你怎么写 Struts2 或 Spring MVC而是用最原始的 JSP Servlet JDBC 组合跑通一个真实可交互的动漫论坛——支持用户注册登录、发帖回帖、图片上传非 Base64、帖子分类、分页展示且所有代码可直接解压.zip后在本地 Tomcat 一键部署。它不追求高并发但必须能让你看清每个 HTTP 请求从 URL 到数据库再到页面渲染的完整路径。适合 Java Web 入门者、课程设计学生、需要快速交付内部社区原型的运维/测试工程师。提示本文所有操作均基于 JDK 8u291 Tomcat 9.0.83 MySQL 5.7.42 环境验证不兼容 JDK 17 或 Tomcat 10因 Servlet API 版本变更。若你已装了 MySQL 8.0请务必关闭caching_sha2_password认证插件否则 JDBC 连接会报Unknown initial character set index 255错误——这是本方案第一个也是最痛的坑我们会在第 4 章专门拆解。2. 用最简 JSPServlet 架构跑通论坛核心流程从 URL 到数据库的 5 步链路JSP 动漫论坛不是“用 JSP 写页面”而是用 JSP 做视图View用 Servlet 做控制器Controller用 JavaBean 做模型Model——这就是经典 MVC 在 J2EE 原生生态里的最小可行实现。它不依赖任何框架却强制你亲手串联 HTTP 协议、会话管理、SQL 执行、HTML 渲染四个关键环节。下面这 5 步是你解压xxx.zip后必须亲手走通的主干链路每一步都对应一个真实文件、一个可调试断点、一个可观察的浏览器行为。2.1 创建数据库与初始化表结构用 SQL 脚本一次性建好 5 张核心表解压后的sql/目录下应有anime_forum.sql文件。它不是简单建表而是按论坛业务逻辑预设了外键约束、索引和默认值。执行前请确认 MySQL 用户有CREATE DATABASE权限并在命令行中切换到 MySQL 客户端-- 在 MySQL 命令行中执行注意路径要替换成你本地的实际路径 source /path/to/anime_forum.sql;该脚本会创建anime_forum数据库并生成以下 5 张表users存储用户名、密码MD5 加密、邮箱、注册时间、头像路径VARCHAR存相对路径如upload/avatar_123.jpgcategories分类表含cid主键、cname如“新番讨论”“经典回顾”“资源分享”posts主贴表含pid、title、contentTEXT、uid外键关联 users、cid外键关联 categories、post_time、view_countreplies回帖表含rid、pid外键、uid发帖人、content、reply_timeuploads附件表可选记录图片上传信息含upload_id、original_name、save_path、uploader_uid注意anime_forum.sql中users.password字段定义为VARCHAR(32)这是为 MD5 加密后 32 位十六进制字符串预留空间。若你改用 BCrypt需将字段改为VARCHAR(60)并修改注册逻辑——但本方案坚持用 MD5仅限教学场景因其加密逻辑可直接写在 Servlet 里无需额外引入 jar 包。2.2 配置 JDBC 连接池用context.xml替代硬编码让数据库连接可复用Tomcat 自带的 JNDI 数据源是 JSP 项目最稳妥的连接管理方式。不要在每个 Servlet 里写DriverManager.getConnection()——那会吃光线程并导致连接泄漏。打开META-INF/context.xml若不存在则新建填入你的 MySQL 实例信息?xml version1.0 encodingUTF-8? Context Resource namejdbc/AnimeForumDB authContainer typejavax.sql.DataSource maxTotal20 maxIdle10 minIdle5 initialSize5 maxWaitMillis10000 usernameroot passwordyour_mysql_password driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/anime_forum?useSSLfalseamp;serverTimezoneGMT%2B8amp;characterEncodingutf8/ /Context关键参数说明maxTotal20最大连接数对单机开发足够生产环境需根据并发量调高url中useSSLfalse是必须项MySQL 5.7 默认开启 SSL但本地开发常无证书serverTimezoneGMT%2B8解决时区错乱导致post_time存成 1970 年characterEncodingutf8防止中文存成乱码driverClassName必须用com.mysql.jdbc.DriverMySQL 5.x若你误用com.mysql.cj.jdbc.DriverMySQL 8.x 驱动启动时会报ClassNotFoundException部署后在任意 Servlet 中通过 JNDI 查找数据源// 在 LoginServlet.java 中 Context initCtx new InitialContext(); Context envCtx (Context) initCtx.lookup(java:comp/env); DataSource ds (DataSource) envCtx.lookup(jdbc/AnimeForumDB); Connection conn ds.getConnection(); // 此处获取的是连接池中的连接用完必须 close()2.3 实现用户登录流程从表单提交到 Session 绑定的完整 Servlet 链login.jsp是入口但它只负责呈现 HTML 表单。真正的校验逻辑在LoginServlet.java中。这个 Servlet 不是“处理完就结束”而是完成三件事查库比对密码、设置用户 Session、重定向到首页。以下是核心代码段删减异常处理保留主干// LoginServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username).trim(); String password request.getParameter(password); // 1. MD5 加密密码前端不传明文此处为演示简化 String md5Pass DigestUtils.md5Hex(password); // 使用 commons-codec-1.15.jar // 2. 查询用户使用 PreparedStatement 防 SQL 注入 String sql SELECT uid, username, email FROM users WHERE username? AND password?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, md5Pass); ResultSet rs ps.executeQuery(); if (rs.next()) { // 3. 登录成功将用户基本信息存入 Session HttpSession session request.getSession(true); session.setAttribute(uid, rs.getInt(uid)); session.setAttribute(username, rs.getString(username)); session.setAttribute(email, rs.getString(email)); session.setMaxInactiveInterval(1800); // Session 30 分钟无操作失效 response.sendRedirect(index.jsp); // 重定向避免刷新重复提交 } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }逻辑说明response.sendRedirect(index.jsp)是关键。它让浏览器发起新 GET 请求从而刷新地址栏为index.jsp且后续所有请求都携带该 Session ID Cookie。若用request.getRequestDispatcher().forward()URL 仍显示/login用户刷新会重新 POST造成重复登录尝试——这是新手最常翻车的点。2.4 发帖功能JSP 表单 Servlet 处理 数据库插入的三角闭环post.jsp页面包含一个form actionPostServlet methodpost其中action指向PostServlet需在web.xml中配置映射。PostServlet接收表单后需做三件事校验标题长度、获取当前登录用户 UID、插入posts表。注意不能直接从 request 获取uid必须从 Session 取因为用户身份由登录态保证// PostServlet.java HttpSession session request.getSession(false); if (session null || session.getAttribute(uid) null) { response.sendRedirect(login.jsp); return; } int uid (int) session.getAttribute(uid); String title request.getParameter(title).trim(); String content request.getParameter(content).trim(); int cid Integer.parseInt(request.getParameter(cid)); // 分类 ID // 插入 posts 表 String sql INSERT INTO posts (title, content, uid, cid, post_time) VALUES (?, ?, ?, ?, NOW()); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setString(1, title); ps.setString(2, content); ps.setInt(3, uid); ps.setInt(4, cid); ps.executeUpdate(); // 获取自增主键 pid用于跳转详情页 ResultSet generatedKeys ps.getGeneratedKeys(); if (generatedKeys.next()) { int pid generatedKeys.getInt(1); response.sendRedirect(post_detail.jsp?pid pid); }参数说明ps.executeUpdate()后调用getGeneratedKeys()是为了拿到数据库生成的pid这是跳转到帖子详情页的必要参数。若你漏掉Statement.RETURN_GENERATED_KEYS参数generatedKeys将为空导致跳转失败——这是本方案第二个高频踩坑点。2.5 帖子列表分页用 LIMIT OFFSET 实现真分页而非前端假分页index.jsp显示最新 10 条帖子但需支持翻页。分页逻辑不在 JSP 里写 SQL而在ListPostsServlet.java中计算LIMIT和OFFSET// ListPostsServlet.java int page 1; String pageParam request.getParameter(page); if (pageParam ! null !pageParam.isEmpty()) { page Integer.parseInt(pageParam); } int pageSize 10; int offset (page - 1) * pageSize; String sql SELECT p.pid, p.title, p.post_time, u.username, c.cname FROM posts p JOIN users u ON p.uid u.uid JOIN categories c ON p.cid c.cid ORDER BY p.post_time DESC LIMIT ? OFFSET ?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, pageSize); ps.setInt(2, offset); ResultSet rs ps.executeQuery(); // 将结果集封装为 ListPostVO存入 request ListPostVO posts new ArrayList(); while (rs.next()) { PostVO vo new PostVO(); vo.setPid(rs.getInt(pid)); vo.setTitle(rs.getString(title)); vo.setPostTime(rs.getTimestamp(post_time)); vo.setUsername(rs.getString(username)); vo.setCname(rs.getString(cname)); posts.add(vo); } request.setAttribute(posts, posts); // 计算总页数需另查一次 COUNT String countSql SELECT COUNT(*) FROM posts; PreparedStatement countPs conn.prepareStatement(countSql); ResultSet countRs countPs.executeQuery(); countRs.next(); int total countRs.getInt(1); int totalPages (int) Math.ceil((double) total / pageSize); request.setAttribute(totalPages, totalPages); request.setAttribute(currentPage, page); request.getRequestDispatcher(index.jsp).forward(request, response);关键细节LIMIT ? OFFSET ?是 MySQL 5.7 原生分页语法比WHERE id ?更可靠。Math.ceil((double) total / pageSize)保证最后一页不足pageSize条时也能正确显示页码。若你用整数除法total / pageSize当total15, pageSize10时结果为 1漏掉第 2 页——这是第三个隐蔽坑。3. 图片上传与展示JSP 页面如何安全地处理用户头像与帖子配图动漫论坛离不开图片用户头像、帖子封面、回复中的截图。JSP 本身不处理文件上传必须依赖 Servlet API 3.0 的Part接口。本方案不使用 Apache Commons FileUpload已过时而是用原生request.getPart()既轻量又符合教学目标。3.1 配置web.xml启用文件上传支持在WEB-INF/web.xml中添加multipart-config标签Tomcat 7.0.41 支持servlet servlet-nameUploadAvatarServlet/servlet-name servlet-classcom.animeforum.servlet.UploadAvatarServlet/servlet-class multipart-config max-file-size2097152/max-file-size !-- 2MB -- max-request-size4194304/max-request-size !-- 4MB 总请求大小 -- file-size-threshold0/file-size-threshold !-- 内存阈值0 表示全写磁盘 -- /multipart-config /servlet注意multipart-config必须放在servlet标签下且max-file-size单位是字节。若你设为2097152020MB而用户上传 25MB 文件Servlet 会直接返回 400 错误不会进入doPost方法——这是上传失败却无日志的根源。3.2 编写UploadAvatarServlet保存图片到upload/目录并更新数据库UploadAvatarServlet.java的核心逻辑是校验文件类型、生成唯一文件名、保存到upload/avatar/目录、更新users.avatar字段protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session request.getSession(false); if (session null || session.getAttribute(uid) null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } int uid (int) session.getAttribute(uid); Part filePart request.getPart(avatar); // 对应 input typefile nameavatar String fileName Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // 1. 校验文件类型仅允许 JPG/PNG String contentType filePart.getContentType(); if (!image/jpeg.equals(contentType) !image/png.equals(contentType)) { request.setAttribute(error, 仅支持 JPG 或 PNG 格式); request.getRequestDispatcher(profile.jsp).forward(request, response); return; } // 2. 生成唯一文件名uid 时间戳 随机数 String ext fileName.substring(fileName.lastIndexOf(.)); String newFileName avatar_ uid _ System.currentTimeMillis() new Random().nextInt(1000) ext; // 3. 保存到 upload/avatar/ 目录绝对路径由 getServletContext().getRealPath() 获取 String uploadPath request.getServletContext().getRealPath() upload/avatar/; File uploadDir new File(uploadPath); if (!uploadDir.exists()) uploadDir.mkdirs(); filePart.write(uploadPath newFileName); // 4. 更新 users 表 String sql UPDATE users SET avatar? WHERE uid?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, upload/avatar/ newFileName); // 存相对路径前端用 img src${user.avatar} ps.setInt(2, uid); ps.executeUpdate(); session.setAttribute(avatar, upload/avatar/ newFileName); // 同步更新 Session response.sendRedirect(profile.jsp?success1); }逻辑说明request.getServletContext().getRealPath()返回 Web 应用根目录的绝对路径如D:\tomcat\webapps\anime_forum\拼接upload/avatar/后形成物理存储路径。filePart.write()会自动创建父目录无需手动mkdirs()——但若你漏掉uploadDir.mkdirs()当upload/目录不存在时write()会抛IOException。这是第四个常见问题。3.3 在 JSP 中安全展示图片用 EL 表达式 路径校验防 XSSprofile.jsp中展示头像不能直接写img src%user.getAvatar()%而要用 JSTL 和 EL并加路径白名单校验% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % c:set varavatarPath value${user.avatar} / !-- 白名单校验只允许 upload/avatar/ 开头的路径 -- c:if test${fn:startsWith(avatarPath, upload/avatar/) fn:endsWith(avatarPath, .jpg) || fn:endsWith(avatarPath, .png)} img src${avatarPath} alt头像 width120 height120 / /c:if c:if test${empty avatarPath || !fn:startsWith(avatarPath, upload/avatar/)} img srcimages/default_avatar.png alt默认头像 width120 height120 / /c:if注意fn:是 JSTL Functions 标签库需在 JSP 顶部声明% taglib prefixfn urihttp://java.sun.com/jsp/jstl/functions %。若你忘记引入${fn:startsWith()}会直接输出字符串文本而非执行函数——页面显示一堆true/false这是第五个“看起来没报错但功能失效”的玄学坑。4. 避坑JSP 动漫论坛开发中 5 个血泪经验总结JSP 项目看似简单但因技术栈老旧、文档分散、错误提示模糊新手极易陷入“页面空白”“跳转失败”“中文乱码”等黑匣子状态。以下是我在带 12 届学生做课程设计时统计出的最高频 5 个问题每个都附带现象、根因和可立即执行的解决步骤。4.1 现象Tomcat 启动后访问http://localhost:8080/anime_forum/显示 404但index.jsp明明在webapp/目录下原因Web 应用未正确部署或web.xml中缺少welcome-file-list配置。Tomcat 默认查找index.html若没有则返回 404不会自动匹配index.jsp。解决检查webapp/WEB-INF/web.xml是否存在且内容包含welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list若使用 Tomcat Manager 部署确认应用状态为running非stopped若用 IDE如 IntelliJ部署检查Deployment选项卡中Application context是否为/anime_forum非/或空。清理 Tomcatwork/Catalina/localhost/下的缓存文件夹重启 Tomcat。4.2 现象登录成功后跳转到index.jsp但页面顶部显示null且session.getAttribute(username)为空原因HttpSession未正确创建或跨请求丢失。常见于response.sendRedirect()后未在目标页面index.jsp中显式获取 Session或浏览器禁用了 Cookie。解决在index.jsp开头添加% HttpSession session request.getSession(false); %并检查session是否为null在LoginServlet中response.sendRedirect()前确认request.getSession(true)已执行且未被session.invalidate()干扰浏览器地址栏输入http://localhost:8080/anime_forum/index.jsp直接访问若正常显示则证明 Session 机制有效问题出在重定向链路上。4.3 现象MySQL 中存入的中文显示为??但数据库字符集已设为utf8mb4原因JDBC 连接 URL 缺少characterEncodingutf8参数或 Tomcatserver.xml中Connector未配置URIEncodingUTF-8。解决检查context.xml中url属性是否包含amp;characterEncodingutf8注意要写成amp;打开conf/server.xml找到Connector port8080 ... /添加属性URIEncodingUTF-8重启 Tomcat并在index.jsp中打印request.getCharacterEncoding()确认输出为UTF-8。4.4 现象上传图片后upload/avatar/目录下文件名含中文如头像.jpg但页面img srcupload/avatar/头像.jpg无法加载原因Tomcat 默认对 URL 中的非 ASCII 字符进行 ISO-8859-1 解码而getSubmittedFileName()返回的原始文件名是 UTF-8 编码导致路径不匹配。解决在UploadAvatarServlet.java中对fileName进行 URL 解码String fileName URLDecoder.decode(filePart.getSubmittedFileName(), UTF-8);或更彻底在server.xml的Connector中添加useBodyEncodingForURItrue强制用请求体编码解析 URI。4.5 现象点击分页链接?page2后页面空白控制台无报错但数据库查询日志显示LIMIT 10 OFFSET 10执行成功原因ListPostsServlet中request.getRequestDispatcher(index.jsp).forward(request, response)后index.jsp未正确接收request.getAttribute(posts)因 JSP 中未用c:forEach而用% out.print(request.getAttribute(posts)) %导致空指针。解决确认index.jsp顶部有% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %遍历逻辑必须用c:forEach items${posts} varpost而非脚本片段% for(...) { %...% } %在index.jsp开头添加c:if test${empty posts}暂无帖子/c:if验证posts是否为空集合。5. 从论文到可运行系统源码包里 3 类文件的用途与修改优先级指南你下载的基于JSP动漫论坛的设计与实现论文源码文件说明文档.zip不是单一资源而是三层交付物论文理论、源码实践、说明文档操作。它们服务于不同目标修改时优先级也不同。我带过 37 个学生做此课题总结出一套“先保运行、再调功能、最后补论文”的实操顺序。5.1 源码文件解压后立刻验证的 4 个核心目录目录路径文件数量必须验证项修改优先级说明src/约 15 个.java文件编译是否通过javac *.javaLoginServlet是否能查库★★★★★Java 源码是系统心脏若编译失败其他全废。重点检查web.xml中 servlet-class 路径是否与实际包名一致如com.animeforum.servlet.LoginServlet。webapp/约 20 个.jsp.js.cssindex.jsp能否正常渲染login.jsp表单action是否指向正确 Servlet★★★★☆JSP 是用户界面错误最直观。若页面空白先看浏览器开发者工具 Network 标签页确认index.jsp返回状态码是 200 还是 500。sql/1 个.sql文件source anime_forum.sql是否成功SELECT COUNT(*) FROM users是否返回 0★★★★☆数据库是数据基石。若建表失败检查 MySQL 版本是否 ≥5.5以及sql_mode是否包含STRICT_TRANS_TABLES会拒绝空字符串插入。lib/约 5 个.jar文件commons-codec-1.15.jarMD5、mysql-connector-java-5.1.49.jarJDBC是否在WEB-INF/lib/下★★★☆☆Jar 包缺失会导致ClassNotFoundException。若你用 MySQL 8.x需替换为mysql-connector-java-8.0.28.jar并改driverClassName。提示不要一上来就改论文先确保源码能在你电脑上跑起来。我见过太多学生花 3 天调论文格式结果第 4 天发现PostServlet根本没连上数据库——源码不动论文就是空中楼阁。5.2 说明文档不是说明书而是你的“部署后悔药”说明文档.docx或README.md通常包含三类信息环境要求明确写出 JDK/Tomcat/MySQL 版本号如“JDK 1.8.0_291”这是你安装软件的唯一依据。若你装了 JDK 17必须卸载重装 JDK 8。部署步骤详细到“将anime_forum.war放入tomcat/webapps/目录启动startup.bat”而非“部署到服务器”。照做即可别跳步。常见问题列出MySQL 连接失败、图片上传失败等解决方案。这是作者踩过的坑比百度快 10 倍。血泪经验说明文档里有一句话叫“数据库密码默认为root”但你实际安装 MySQL 时设的是123456。此时不要怀疑文档立刻打开context.xml把passwordroot改成password123456——文档写的不是“必须用 root”而是“示例密码”。5.3 论文文件从“抄”到“改”的 3 个安全动作论文.docx是交付物不是学习材料。它的价值在于结构模板目录层级摘要、需求分析、系统设计、实现、测试、总结不可删减这是答辩硬性要求图表占位ER 图、系统架构图、界面截图已嵌入你只需替换为自己的数据库表截图、index.jsp实际效果文字复用描述“MVC 模式优势”“JSP 与 Servlet 协作流程”的段落可直接复制但必须在“系统实现”章节加入你修改过的代码片段如PostServlet.java的insert部分。关键技巧用 Word “比较”功能审阅 → 比较 → 比较文档把你改过的PostServlet.java与原文对比截图放入论文“代码实现”小节。这比大段粘贴代码更专业且证明你真动手了。6. 让 JSP 论文不止于及格3 个低成本高价值的加分项实操导师看课程设计表面查功能实则考工程素养。一个能跑通的论坛得 70 分但加上以下三个细节就能冲到 90。它们都不需要重写架构只需在现有代码上加几行、改几个配置却能让答辩时老师眼睛一亮。6.1 给所有 SQL 添加执行日志用 Log4j2 记录慢查询与错误JSP 项目常被诟病“无日志”导致问题排查靠猜。在src/log4j2.xml中配置 JDBC 日志让每次executeQuery()都留下痕迹Configuration statusWARN Appenders File nameJDBCLog fileNamelogs/jdbc.log PatternLayout pattern%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n/ /File /Appenders Loggers Logger namecom.mysql.jdbc.StatementImpl leveldebug additivityfalse AppenderRef refJDBCLog/ /Logger Root levelerror AppenderRef refConsole/ /Root /Loggers /Configuration然后在BaseServlet.java所有 Servlet 的父类中添加private static final Logger LOGGER LogManager.getLogger(BaseServlet.class); // 在每个 doXxx 方法开头 LOGGER.debug(Executing SQL: {} with params {}, sql, Arrays.toString(params));效果logs/jdbc.log会记录每条 SQL 执行时间、参数值。当用户反馈“发帖卡住”你不用重启服务直接查日志就能定位是INSERT INTO posts还是UPDATE users慢——这比说“我检查了代码”有力十倍。6.2 为帖子内容添加简易 Markdown 支持用commonmark-java渲染轻量格式动漫论坛用户爱用**加粗**、*斜体*、[链接](url)。在post_detail.jsp中不直接out.print(post.getContent())而是用 Markdown 解析器% page importorg.commonmark.node.* % % page importorg.commonmark.parser.* % % page importorg.commonmark.renderer.html.* % % Parser parser Parser.builder().build(); HtmlRenderer renderer HtmlRenderer.builder().build(); Node document parser.parse(post.getContent()); String htmlContent renderer.render(document); % div classpost-content% htmlContent %/div注意需在lib/中加入commonmark-0.20.0.jar和commonmark-ext-gfm-tables-0.20.0.jar支持表格。用户输入**标题**页面就渲染为strong标题/strong——这种“小而美”的体验提升远胜堆砌无用功能。6.3 实现登录态持久化用 Cookie 保存 7 天免登录LoginServlet.java中登录成功后添加 Cookie// 登录成功后 Cookie loginCookie new Cookie(anime_forum_token, generateToken(uid)); loginCookie.setPath(/); loginCookie.setMaxAge(7 * 24 * 3600); // 7 天 loginCookie.setHttpOnly(true); // 防 XSS 读取 response.addCookie(loginCookie);并在Filter如AuthFilter.java中拦截请求自动登录Cookie[] cookies request.getCookies(); if (cookies ! null) { for (Cookie cookie : cookies) { if (anime_forum_token.equals(cookie.getName())) { int uid verifyToken(cookie.getValue()); // 自定义 token 校验逻辑 if (uid 0) { request.getSession(true).setAttribute(uid, uid); break; } } } }价值用户关浏览器再打开仍保持登录。这不需要改数据库只需加 20 行代码却让系统从“教学玩具”变成“可用产品”。答辩时演示这个功能老师会立刻问“怎么实现的”你答出 Cookie Token分数就稳了。我带的最后一届学生有个姑娘在答辩时被问“你这个论坛如果我想加个‘热门帖子’功能怎么改”她没说“要加 Redis”而是打开ListPostsServlet.java指着ORDER BY view_count DESC LIMIT 5那行说“老师我把这里改成ORDER BY view_count DESC再加个缓存开关就行。” 全场安静三秒导师笑了“行这思路我认。” ——技术深度不在框架多炫而在你是否真的摸透了每一行代码的来龙去脉。希望帮到你。本文还有配套的精品资源点击获取
返回列表