news 2026/9/28 15:23:04

JavaWeb文章管理系统源码解析:从环境搭建到软删除与恢复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb文章管理系统源码解析:从环境搭建到软删除与恢复

简介:这是一套基于JavaWeb的文章管理系统完整源码与数据库,面向计算机相关专业学生及企业开发者,可用于课程设计、毕业设计、大作业或初期项目立项演示。系统区分用户与管理员两种登录角色,支持用户发布新文章、查看文章详情、修改文章,以及文章的删除与恢复,功能链路完整,适合作为JavaWeb入门到进阶的实战练习素材。压缩包共396个文件,约7.32MB,包含15个java源文件、12个jsp页面、27个html页面,以及大量js、css、png、gif等前端资源,另有26个jar依赖、1个sql数据库脚本和若干xml配置,覆盖后端逻辑、前端界面与数据存储各层。项目代码经过测试运行成功、功能正常后才上传,已有153人学习下载。读者可借此理解DAO层封装、登录鉴权、文章增删改查与恢复等典型实现,快速搭建可运行环境并对照源码梳理开发思路。

1. 从一份 JavaWeb 文章管理系统源码说起:它到底能跑出什么

课程设计选题里,文章管理系统几乎是每年都被翻牌子的那类。原因很直接:业务逻辑不复杂,但登录、权限、增删改查、软删除这些核心动作一个不少,正好能把 JavaWeb 的 Servlet、JSP、JDBC 三层串起来。这份资源给的就是一套能跑通的完整源码加数据库,用户和管理员两套登录入口,用户能发文章、看详情、改文章、删文章,管理员能审核、恢复被删的文章。压缩包里能看到NewsRealeseDao.class、QueryOneNews.class、InsertOneNews.class、UpdateOneNews.class、DeleteOneNews.class、ResumeNews.class、checkLogin_user.class、Authorize.class这些编译产物,基本覆盖了从数据访问到登录校验再到权限判断的整条链路。

它适合谁?计算机相关专业要交大作业、课程设计、毕设初期演示的同学,或者想拿一个结构完整的小型 JavaWeb 项目练手的人。不适合指望它直接上线生产的人——这是一套教学向的实现,重点在流程完整、逻辑清晰,而不是高并发和工程化。下面按「资源是什么 → 怎么跑起来 → 坑在哪 → 怎么改」的顺序拆开讲,每一步都尽量落到能照着敲的程度。

2. 环境搭建与数据库还原:让项目在你机器上先跑起来

这套源码是典型的「Servlet + JSP + JDBC + MySQL」结构,没有用 Spring 那一套,所以依赖少、启动快,但也意味着配置得手动对齐。很多人拿到压缩包第一反应是直接丢进 IDE 点运行,结果一片红,问题基本都出在环境没对齐。这一章把环境、数据库、连接配置三件事按顺序理清楚。

2.1 JDK、Tomcat、IDE 的版本对齐

先确认三样东西的版本,这是最容易翻车的地方。老式 JavaWeb 项目对 JDK 版本敏感,用 JDK 17 去跑一个为 JDK 8 写的项目,javax.servlet包直接找不到。

组件推荐版本说明
JDK1.8(8u 系列)项目用的是javax.servlet.*,JDK 8 最稳
Tomcat8.5 或 9.0不要用 Tomcat 10,包名从javax变成jakarta,会全线报错
IDEIDEA 或 EclipseIDEA 对 Web 项目支持更顺,下面以 IDEA 为例
MySQL5.7 或 8.08.0 要注意驱动包和连接串的时区参数

Tomcat 10 这个坑值得单独说一句。它的 Servlet API 包名从javax.servlet改成了jakarta.servlet,而这套源码里的checkLogin_user.class、Authorize.class都是按javax编译的,放到 Tomcat 10 里会直接抛ClassNotFoundException。所以要么用 Tomcat 9,要么就别碰这套源码,别在这上面浪费时间。

IDEA 里的配置步骤:

  1. File → Project Structure → Project,把 Project SDK 设成 1.8,Language level 设成 8。
  2. Modules → Dependencies,确认mysql-connector-java的 jar 包已经加进去,没有的话从WEB-INF/lib里找,或者手动 Add JARs。
  3. Facets里确认 Web 描述符指向web.xml,Web 资源目录指向web或WebContent。
  4. Artifacts里新建一个Web Application: Exploded,输出目录默认即可。

2.2 数据库还原:建库、导表、改连接串

数据库是这套项目的另一半,源码里那些 DAO 类全靠它。压缩包里一般会带一个.sql文件,先把它导进去。

# 登录 MySQL,创建数据库 mysql -u root -p CREATE DATABASE news_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE news_db; # 导入 sql 文件(在系统命令行里执行,不是 MySQL 交互界面里) mysql -u root -p news_db < /path/to/your_project.sql

导入完成后,用SHOW TABLES;确认表都进去了。通常会有用户表、文章表两张核心表,文章表里会有一个类似is_deleted或status的字段,用来标记删除状态——这是「删除与恢复」功能的关键,后面会细讲。

接下来改连接配置。JDBC 连接串一般写在某个工具类里,或者直接写在 DAO 的构造函数里。找到类似DBUtil.java或BaseDao.java的文件,把用户名密码改成你自己的:

// 数据库连接配置,改成你本机的实际参数 private static final String URL = "jdbc:mysql://localhost:3306/news_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; // 加载驱动,MySQL 8.0 用 com.mysql.cj.jdbc.Driver static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } }

参数说明:serverTimezone=Asia/Shanghai是 MySQL 8.0 必须加的,不加会报时区错误;useSSL=false去掉 SSL 警告;characterEncoding=utf8保证中文不乱码。如果你用的是 MySQL 5.7,驱动类名可以写com.mysql.jdbc.Driver,连接串里的cj也可以去掉,但加上也不影响。

2.3 部署运行与登录验证

配置好之后,在 IDEA 里配置 Tomcat 运行环境:Run → Edit Configurations → + → Tomcat Server → Local,在Deployment标签页里把刚才建的 Artifact 加进去,Application context 设成/news之类的短路径。启动后浏览器访问http://localhost:8080/news/,应该能看到登录页。

登录验证分两条线:普通用户走checkLogin_user,管理员走Authorize。先用数据库里预置的账号登录,如果登录失败,先别急着改代码,按这个顺序排查:数据库里有没有这条用户记录、密码字段是不是明文(教学项目多半是明文)、checkLogin_user里查的字段名和表结构对不对。这三步能解决八成登录问题。

3. 核心功能链路拆解:登录、发布、删除与恢复怎么串起来

环境跑通只是第一步,真正有价值的是看懂这套代码怎么把业务串起来。这一章按「登录鉴权 → 文章发布 → 软删除与恢复」三条链路拆,每条链路都对应源码里的具体类,看懂了就能自己改。

3.1 登录鉴权:checkLogin_user 与 Authorize 的分工

登录这块有两个类,checkLogin_user管普通用户,Authorize管权限判断。常见做法是:用户提交表单后,Servlet 拿到用户名密码,调checkLogin_user去数据库查,查到就session.setAttribute("user", user),然后跳转;查不到就回登录页带个错误提示。

// 登录校验的典型写法 String username = request.getParameter("username"); String password = request.getParameter("password"); User user = checkLogin_user.check(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("currentUser", user); // 根据角色跳不同页面 if ("admin".equals(user.getRole())) { response.sendRedirect("admin/index.jsp"); } else { response.sendRedirect("user/home.jsp"); } } else { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); }

逻辑说明:checkLogin_user.check()内部就是一条SELECT * FROM user WHERE username=? AND password=?,用PreparedStatement防注入。Authorize类通常是个过滤器或者在每个敏感 Servlet 开头调用的工具,判断session里有没有用户、角色对不对。这里有个新手常犯的错:只在登录时校验,后续每个操作页面不校验 session,导致没登录也能直接访问发布页。正确做法是在Authorize里统一拦截,或者在web.xml里配一个 Filter。

参数上要注意的是角色字段。如果用户表里没有role字段,那用户和管理员可能是两张表,或者靠某个标志位区分。看Authorize里怎么判断的,跟着它的逻辑走,别自己臆造。

3.2 文章发布与详情:InsertOneNews 和 QueryOneNews 的配合

发布文章走InsertOneNews,详情查看走QueryOneNews和QueryOneNews_user。这两个查询类的区别值得说:QueryOneNews多半是管理员视角,能查到所有文章包括待审核的;QueryOneNews_user是用户视角,只查自己发的或者已审核通过的。这种「同一张表、不同查询口径」的设计在教学项目里很常见,理解了这个,你就理解了权限控制最朴素的做法——不是靠数据库权限,而是靠代码里查不同的 SQL。

// 发布文章:InsertOneNews String title = request.getParameter("title"); String content = request.getParameter("content"); int userId = ((User) session.getAttribute("currentUser")).getId(); News news = new News(); news.setTitle(title); news.setContent(content); news.setUserId(userId); news.setCreateTime(new Date()); news.setStatus(0); // 0 表示待审核,1 表示已发布 boolean ok = InsertOneNews.insert(news); if (ok) { response.sendRedirect("list.jsp"); } else { request.setAttribute("msg", "发布失败"); request.getRequestDispatcher("publish.jsp").forward(request, response); }

参数说明:status字段是审核状态的核心,发布时设 0,管理员审核后改成 1,用户端列表只查status=1的。createTime用java.util.Date还是java.sql.Timestamp要看表字段类型,datetime对应Timestamp更稳。userId从 session 里取,不要从表单隐藏域取,否则用户可以伪造。

详情页的查询就是按 id 查单条:SELECT * FROM news WHERE id=?。这里要注意的是,如果文章被软删除了,详情页要不要能访问?一般做法是详情页也带上is_deleted=0的条件,否则用户通过历史链接还能看到已删除的文章。

3.3 删除与恢复:DeleteOneNews 和 ResumeNews 的软删除设计

「删除与恢复」是这套项目里最有含金量的功能点,也是课程设计里容易拿分的地方。它的实现方式几乎肯定是软删除:DeleteOneNews不是DELETE FROM,而是UPDATE news SET is_deleted=1 WHERE id=?;ResumeNews就是反过来UPDATE news SET is_deleted=0。

// 软删除:标记删除而不是物理删除 public static boolean delete(int id) { String sql = "UPDATE news SET is_deleted = 1 WHERE id = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, id); return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } // 恢复:把标记改回来 public static boolean resume(int id) { String sql = "UPDATE news SET is_deleted = 0 WHERE id = ?"; // 其余同上 }

逻辑说明:软删除的好处是数据不丢,管理员能恢复,用户误删也有后悔药。代价是所有查询都要带上is_deleted=0的条件,漏一个就会把已删除的数据查出来。这是这套代码里最容易出 bug 的地方——QueryOneNews加了条件,QueryOneNews_user忘了加,结果用户列表里还能看到自己删掉的文章。

参数上,is_deleted字段类型用tinyint(1)或int都行,默认值设 0。如果表里用的是status字段同时表示审核和删除,那逻辑会更绕,得看清楚每个状态值的含义,别把「待审核」和「已删除」搞混。

4. 避坑与排查:这套源码最容易翻车的五个地方

教学向的 JavaWeb 项目,坑往往不在业务逻辑,而在环境、编码、路径这些「玄学」层面。下面五条是我实际跑这类项目时踩过的,按「现象 → 原因 → 解决」写清楚。

4.1 中文乱码:从表单到数据库一路问号

现象:发布文章后,标题和内容在页面上显示成???或者乱码方块。

原因:三处编码没对齐——JSP 页面pageEncoding、请求体编码、数据库连接串编码。任何一处是 ISO-8859-1,中文就废了。

解决:JSP 头部写<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>;在 Servlet 取参数前加request.setCharacterEncoding("UTF-8");连接串带上characterEncoding=utf8;数据库和表的字符集设成utf8mb4。四处都对齐,乱码基本消失。

4.2 ClassNotFoundException:驱动包没进对地方

现象:启动后访问登录,控制台报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。

原因:mysql-connector-java的 jar 包没放到WEB-INF/lib下,或者 IDEA 的 Artifact 里没包含它。

解决:确认 jar 包在WEB-INF/lib目录里;在 IDEA 的Project Structure → Artifacts里,看 Output Layout 的WEB-INF/lib下有没有这个 jar,没有就手动加进去。Tomcat 运行时用的是 Artifact 里的东西,不是你 Module 依赖里的东西,这点要分清。

4.3 404:路径对不上或者 web.xml 配错

现象:登录页能打开,点登录按钮跳 404。

原因:表单action写的路径和web.xml里 Servlet 的url-pattern对不上,或者项目部署的 context path 和你以为的不一样。

解决:先看浏览器地址栏的实际路径,确认 context path;再看web.xml里 Servlet 映射,或者注解@WebServlet("/login")的值;最后核对表单action。三者一致才行。IDEA 里 Application context 设成/news,那访问就是http://localhost:8080/news/login,别写成/login。

4.4 登录成功但 session 丢失:跳转方式用错了

现象:登录提示成功,但跳转后页面又变成未登录状态。

原因:用了response.sendRedirect跳转,但 session 没设成功,或者跳转到了不同 context path 下,session 不共享。

解决:确认session.setAttribute在跳转前执行了;确认跳转前后是同一个应用 context;如果用了sendRedirect,它会产生新请求,但 session 是基于 cookie 的,同一浏览器下不会丢。真正丢 session 多半是因为session.invalidate()被提前调用了,或者 Tomcat 重启了。检查代码里有没有误调invalidate。

4.5 删除后列表还在:查询漏了 is_deleted 条件

现象:用户删除文章后,刷新列表还能看到那篇文章。

原因:列表查询的 SQL 没加is_deleted=0条件,软删除只改了标记,查询没过滤。

解决:找到列表查询对应的 DAO 方法,在WHERE后面补上AND is_deleted=0。同时检查详情页、编辑页的查询是不是也漏了。软删除方案下,所有面向用户的查询都必须带这个条件,这是纪律。

5. 二次开发与验证:把教学项目改成能写进简历的东西

跑通只是及格线,这套源码真正的价值在于它是个干净的底座,你能在上面加东西。课程设计答辩时,老师最烦的就是「照着教程敲了一遍」,最想看到的是你自己加了什么、改了什么、为什么这么改。这一章给几个可落地的改造方向和验证方法。

5.1 加一个分页查询,把 QueryOneNews 用起来

原项目的列表多半是全量查出来,数据一多就卡。加分页是最容易体现工程思维的改造。核心是两条 SQL:一条查总数,一条查当前页。

// 分页查询:page 从 1 开始,pageSize 每页条数 public static List<News> queryByPage(int page, int pageSize) { List<News> list = new ArrayList<>(); String sql = "SELECT * FROM news WHERE is_deleted = 0 ORDER BY create_time DESC LIMIT ?, ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, (page - 1) * pageSize); // 偏移量 ps.setInt(2, pageSize); ResultSet rs = ps.executeQuery(); while (rs.next()) { // 封装成 News 对象,略 } } catch (SQLException e) { e.printStackTrace(); } return list; }

参数说明:LIMIT ?, ?第一个问号是偏移量(page-1)*pageSize,第二个是每页条数。总数查询用SELECT COUNT(*) FROM news WHERE is_deleted=0,算出总页数传给 JSP 渲染页码。改造完记得在 JSP 里加页码导航,否则前端看不到效果。

5.2 用 Filter 统一做登录拦截,替掉散落的 Authorize 调用

原项目里Authorize可能是在每个 Servlet 里手动调的,容易漏。改成 Filter 更规范,也更像真实项目。

@WebFilter(urlPatterns = {"/user/*", "/admin/*"}) public class AuthFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; HttpSession session = request.getSession(false); // 放行登录页和登录接口 String uri = request.getRequestURI(); if (uri.endsWith("login.jsp") || uri.endsWith("/login")) { chain.doFilter(req, resp); return; } if (session == null || session.getAttribute("currentUser") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); } }

逻辑说明:urlPatterns拦截用户和管理员目录下的所有请求,登录页和登录接口放行。getSession(false)表示不新建 session,避免给未登录用户也建一个。这样改造后,Authorize类就可以退休了,代码更集中,也不容易漏。

5.3 验证改造是否成功:三个必测场景

改完别急着交,按这三个场景走一遍,能挡住大部分低级错误。

场景操作预期结果
未登录访问直接访问/user/publish.jsp跳转到登录页
普通用户访问管理页用普通账号访问/admin/index.jsp被拦截或提示无权限
删除后恢复用户删文章,管理员恢复列表先消失,恢复后重新出现

这三个场景覆盖了鉴权、权限、软删除三条核心链路。如果都过了,说明改造没破坏原有逻辑。答辩时把这三个场景演示一遍,比念代码强得多。

5.4 一个我自己的习惯

从那以后我每次拿到这类教学源码,第一件事不是急着跑,而是先把数据库表结构导出来看一遍,尤其是状态字段和删除标记字段,把每个值的含义写在纸上。因为这类项目的业务逻辑全藏在这些字段里,看懂了字段,代码就是顺藤摸瓜。第二件事是把所有查询 SQL 列出来,逐个检查有没有漏is_deleted=0,这一步能省掉后面大量的调试时间。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 15:22:13

RSTSM 2026国际学术会议:SPIE出版+EI检索投稿指南

每年年初我都会收到大量学术会议的征稿邮件&#xff0c;这个时节最让人纠结的就是"要不要投、该投哪个"。如果你正在做遥感、测绘、地理信息相关的研究&#xff0c;想找一个相对靠谱的渠道把手头成果发表出去&#xff0c;这篇分享值得看完。第三届遥感技术与测量测绘…

作者头像 李华
网站建设 2026/9/28 15:21:39

Jev接入实战:从密钥配置到Codex集成与Python API调优

上个月我接了个活&#xff1a;给一个跑了好几年的遗留系统做性能排查。那代码写得跟迷宫似的&#xff0c;我对着日志一行行啃&#xff0c;效率低得离谱。同事看我抓狂&#xff0c;说了一嘴&#xff1a;你试试Jev&#xff1f;我一开始还以为是某个新的前端框架&#xff0c;结果研…

作者头像 李华
网站建设 2026/9/28 15:18:56

OpenClaw启动失败排查:从Gateway到依赖环境的全链路攻略

1. 先搞清楚&#xff1a;OpenClaw靠什么启动1.1 启动链路大致是怎样的OpenClaw 这类网关式 Agent 项目&#xff0c;启动不是敲一条命令就完事的。它内部通常有好几个进程&#xff1a;入口 Gateway、会话管理、Agent Worker、外部服务连接器&#xff0c;再加上后端依赖的数据库和…

作者头像 李华
网站建设 2026/9/28 15:18:45

Arduino+CNC Shield V3搭建微型CNC雕刻机完全指南

如果你问一个玩了好几年CNC的人&#xff0c;第一台机器是怎么来的&#xff0c;十有八九会听到一句话&#xff1a;用Arduino和CNC Shield V3攒的。这几乎是DIY圈子的标准起点&#xff0c;硬件便宜&#xff0c;资料多&#xff0c;踩坑案例也多&#xff0c;但正因为前人替你踩过了…

作者头像 李华
网站建设 2026/9/28 15:18:31

私有化部署AI Agent稳定性设计:两级任务分流架构实战指南

私有化部署AI Agent这件事&#xff0c;这两年已经被问烂了&#xff0c;但我要说&#xff0c;大部分团队卡住的不是模型选型&#xff0c;也不是知识库效果&#xff0c;而是部署之后线上稳定性根本扛不住真实业务流量。GPU买了、模型跑了、Agent能对话了&#xff0c;结果生产环境…

作者头像 李华
网站建设 2026/9/28 15:17:55

HT7038+STM32工业电能计量系统设计与高精度实现

1. 为什么HT7038STM32组合成了工业级电能采集的“黄金搭档”&#xff1f;在电力监控、智能电表、能源管理系统这些实际场景里&#xff0c;我见过太多项目卡在“数据不准”这道坎上——不是电流采样漂移&#xff0c;就是功率因数算错&#xff0c;更常见的是三相不平衡时总有某一…

作者头像 李华