简介:这是一套基于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包直接找不到。
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 1.8(8u 系列) | 项目用的是javax.servlet.*,JDK 8 最稳 |
| Tomcat | 8.5 或 9.0 | 不要用 Tomcat 10,包名从javax变成jakarta,会全线报错 |
| IDE | IDEA 或 Eclipse | IDEA 对 Web 项目支持更顺,下面以 IDEA 为例 |
| MySQL | 5.7 或 8.0 | 8.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.8,Language level 设成 8。Modules → Dependencies,确认mysql-connector-java的 jar 包已经加进去,没有的话从WEB-INF/lib里找,或者手动 Add JARs。Facets里确认 Web 描述符指向web.xml,Web 资源目录指向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?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,这一步能省掉后面大量的调试时间。希望帮到你。
本文还有配套的精品资源,点击获取