
简介这是一套基于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.88u 系列项目用的是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 里的配置步骤File → Project Structure → Project把 Project SDK 设成 1.8Language level 设成 8。Modules → Dependencies确认mysql-connector-java的 jar 包已经加进去没有的话从WEB-INF/lib里找或者手动 Add JARs。Facets里确认 Web 描述符指向web.xmlWeb 资源目录指向web或WebContent。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?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse; 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(); } }参数说明serverTimezoneAsia/Shanghai是 MySQL 8.0 必须加的不加会报时区错误useSSLfalse去掉 SSL 警告characterEncodingutf8保证中文不乱码。如果你用的是 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用户端列表只查status1的。createTime用java.util.Date还是java.sql.Timestamp要看表字段类型datetime对应Timestamp更稳。userId从 session 里取不要从表单隐藏域取否则用户可以伪造。详情页的查询就是按 id 查单条SELECT * FROM news WHERE id?。这里要注意的是如果文章被软删除了详情页要不要能访问一般做法是详情页也带上is_deleted0的条件否则用户通过历史链接还能看到已删除的文章。3.3 删除与恢复DeleteOneNews 和 ResumeNews 的软删除设计「删除与恢复」是这套项目里最有含金量的功能点也是课程设计里容易拿分的地方。它的实现方式几乎肯定是软删除DeleteOneNews不是DELETE FROM而是UPDATE news SET is_deleted1 WHERE id?ResumeNews就是反过来UPDATE news SET is_deleted0。// 软删除标记删除而不是物理删除 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_deleted0的条件漏一个就会把已删除的数据查出来。这是这套代码里最容易出 bug 的地方——QueryOneNews加了条件QueryOneNews_user忘了加结果用户列表里还能看到自己删掉的文章。参数上is_deleted字段类型用tinyint(1)或int都行默认值设 0。如果表里用的是status字段同时表示审核和删除那逻辑会更绕得看清楚每个状态值的含义别把「待审核」和「已删除」搞混。4. 避坑与排查这套源码最容易翻车的五个地方教学向的 JavaWeb 项目坑往往不在业务逻辑而在环境、编码、路径这些「玄学」层面。下面五条是我实际跑这类项目时踩过的按「现象 → 原因 → 解决」写清楚。4.1 中文乱码从表单到数据库一路问号现象发布文章后标题和内容在页面上显示成???或者乱码方块。原因三处编码没对齐——JSP 页面pageEncoding、请求体编码、数据库连接串编码。任何一处是 ISO-8859-1中文就废了。解决JSP 头部写% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %在 Servlet 取参数前加request.setCharacterEncoding(UTF-8)连接串带上characterEncodingutf8数据库和表的字符集设成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_deleted0条件软删除只改了标记查询没过滤。解决找到列表查询对应的 DAO 方法在WHERE后面补上AND is_deleted0。同时检查详情页、编辑页的查询是不是也漏了。软删除方案下所有面向用户的查询都必须带这个条件这是纪律。5. 二次开发与验证把教学项目改成能写进简历的东西跑通只是及格线这套源码真正的价值在于它是个干净的底座你能在上面加东西。课程设计答辩时老师最烦的就是「照着教程敲了一遍」最想看到的是你自己加了什么、改了什么、为什么这么改。这一章给几个可落地的改造方向和验证方法。5.1 加一个分页查询把 QueryOneNews 用起来原项目的列表多半是全量查出来数据一多就卡。加分页是最容易体现工程思维的改造。核心是两条 SQL一条查总数一条查当前页。// 分页查询page 从 1 开始pageSize 每页条数 public static ListNews queryByPage(int page, int pageSize) { ListNews 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_deleted0算出总页数传给 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_deleted0这一步能省掉后面大量的调试时间。希望帮到你。本文还有配套的精品资源点击获取