
简介一套基于JSP、Servlet与MySQL的博客系统网站源码面向Java Web初学者和需要快速搭建博客平台的开发者可套用于课程设计、毕业设计或项目起步。整个压缩包共327个文件大小约2.43MB包含49个JSP页面、52个Java源文件及对应Class文件以及19个JS、11个CSS、113个GIF图标等静态资源另附SQL脚本与配置文件覆盖展示层、业务层与数据层目前已有1161人学习下载。系统实现了用户注册登录、个人信息管理文章发布、编辑、删除和分类管理评论发表、回复与删除关键词搜索以及管理员对用户权限的区分与设置等完整功能模块运行环境需JDK、Tomcat与MySQL支持部署后即可使用。研读这套源码可以看清JSP中指令、脚本元素、表达式语言如何拼接动态页面Servlet如何经历加载、初始化、服务、销毁四个阶段来处理请求以及利用JDBC对用户表、文章表、评论表做增删改查完整串联浏览器到数据库再返回页面的过程。对于想进阶的开发者也可以基于这套结构用Spring Boot、Vue等更现代的技术栈进行改造或自行增加富文本编辑、标签管理、个人主页等功能来继续打磨。1. 这份 JSPServletMySQL 博客源码解压之后到底该怎么用解压这份 zip 时我猜你已经把博客系统的三块拼图听熟了JSP 负责页面展示Servlet 负责接收请求和调度MySQL 负责存文章、用户和评论。但真正双击 index.jsp 只会看到一屏服务器报错——因为 JSP 必须交给 Tomcat 这种 Servlet 容器编译执行它从来不是静态网页。这套 JSPServletMySQL 的博客源码解决的就是“一个标准 JavaWeb 项目从哪来、怎么跑、怎么改”的问题它自带完整的前后台、文章 CRUD、登录和评论用教科书式的三层结构组织代码。适合正在为 JSP 毕设选题发愁的学生也适合刚学完 JavaSE、想第一次把 Web 应用跑起来的初学者。下面按我自己的落地顺序把从解压到跑通、从改代码到避坑的完整路线过一遍。2. 先把环境凑齐JDK、Tomcat、MySQL 与 IDEA 的版本搭配拿到源码先别急着解压看代码环境不齐后面所有步骤都会变成“换个电脑就跑不起来”的玄学问题。JSP、Servlet、MySQL 这套组合在 Java 生态里算“老三代”最大的敌人不是代码本身而是版本漂移。2.1 版本搭配是第一步JDK 8 Tomcat 8.5 MySQL 的稳妥组合老项目的代码是用javax.servlet命名空间写的而 Tomcat 10 换成了jakarta.servlet直接把 10.x 当作“新版”装上去启动时大概率报NoClassDefFoundError。所以拿到源码第一件事不是下载最新版而是按老代码的兼容范围选环境。组件推荐版本理由JDK1.88u202 及以后绝大多数老源码编译目标就是 1.8高版本 JDK 对反射、JSP 编译偶有兼容问题Tomcat8.5 或 9.0仍在支持期内且沿用javax.servlet老代码零修改跑起来MySQL5.7 或 8.05.7 最省事8.0 也常见但要连带改驱动和连接串参数IDEA2021 之后的社区版社区版免费且自带 Tomcat 集成旗舰版对 Web 工程更顺但非必需我一般会直接用 JDK 8 Tomcat 8.5 的组合先把项目跑起来确认没报错之后再决定要不要往 MySQL 8 和更高版本迁移。顺序反过来的话你会分不清报错到底是代码问题还是环境问题。IDE 不做强求但新手我建议 IDEA。常有人问 VSCode 能不能写 Servlet——能但要自己装 Java Extension Pack、配 Tomcat 插件、还要手工管理 classpath对新手来说配置成本远高于收益。IDEA 社区版一条龙省下的时间足够把源码读一遍。2.2 IDEA 导入传统 Web 工程从解压目录到可运行的 Artifact解压后的目录通常有两种形态一种只有src和web平级、没有 Maven 的pom.xml这是纯传统 Web 工程另一种带pom.xml可以按 Maven 项目导入。先分清形态再动手。传统形态的导入步骤把 zip 解压到一个路径里没有中文和空格的目录比如D:\blog-project避免后面 Tomcat 和部署路径出现编码问题。IDEA 里点File - Open选中解压后的目录IDEA 会自动识别为普通 Java 工程。打开File - Project Structure在Project里把 SDK 选成 1.8Language Level选 8。切到Modules确认有个名为web的模块并且Web Resource Directory指向web目录WEB-INF/web.xml能被识别到。进入Run - Edit Configurations点左上角选Tomcat Server - Local。在Deployment选项卡里点 - Artifact选择xxx:war explodedApplication context填/blog。启动 Tomcat浏览器访问http://localhost:8080/blog/。这里有个关键点IDEA 里一定要选war exploded而不是war。war exploded是解包后的目录形式改完 JSP 或 Java 类能热加载调试效率高war是压缩包形式适合最后发布不适合开发期反复重启。Application context填什么决定你访问路径里带什么前缀填/blog就是http://localhost:8080/blog/。导入时如果 IDEA 提示No artifacts marked说明Project Structure里的Artifacts还没建。手动加一个 - Web Application Exploded - From Modules把web模块挂进去再回运行配置里选 Artifact 就有东西了。2.3 MySQL 初始化建库、建用户、导入 SQL 脚本源码包里一般会带一个.sql文件名字可能是blog.sql或database.sql。这一步的目标是把它变成你本机 MySQL 里一个能连的库。# 进入 MySQL 命令行root 密码按你自己的填 mysql -u root -p # 在 MySQL 命令行里依次执行 CREATE DATABASE blog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER blog_userlocalhost IDENTIFIED BY blog_pass; GRANT ALL PRIVILEGES ON blog.* TO blog_userlocalhost; FLUSH PRIVILEGES; EXIT; # 退出后把 sql 脚本导入刚建好的库 mysql -u blog_user -p blog /path/to/blog.sql建库时用utf8mb4而不是老代码里常见的utf8是因为utf8mb4能存 emoji 和四字节生僻字兼容性更好排序规则utf8mb4_general_ci对中英文排序足够用性能也比utf8mb4_unicode_ci略好。新建的blog_user账号密码要和源码里DBUtil.java或db.properties里配置的保持一致。很多项目跑不起来并不是 SQL 装错而是源码连接串里写死了某个账号和你手动建的账号对不上。改代码前先花五分钟把src目录下所有.properties、.xml里的数据库账号密码统一成一处这是血泪经验。3. 从源码里找到博客的骨架Servlet 生命周期、路由与三层调用链环境跑通后别急着改页面。先用半小时读目录和路由配置搞清楚哪个 URL 对应哪个 Servlet哪个 Servlet 又跳回哪个 JSP。这个“请求地图”一旦建立后续改需求就是填空而不是大重构。3.1 从 web.xml 到 WebServlet两种路由配置的差异与兼容老项目的路由信息主要集中在web/WEB-INF/web.xml里。打开它你会看到大量servlet和servlet-mapping标签作用是把一个 Servlet 类绑定到一个 URL 上。servlet servlet-nameArticleServlet/servlet-name servlet-classcn.blog.servlet.ArticleServlet/servlet-class /servlet servlet-mapping servlet-nameArticleServlet/servlet-name url-pattern/article/url-pattern /servlet-mapping这段配置的意思是浏览器访问/blog/article时Tomcat 会把请求交给cn.blog.servlet.ArticleServlet这个类处理。url-pattern里没有.jsp后缀所以你看地址栏是/article但页面内容最终由 Servlet 转发给某个 JSP 渲染这就是 JSP 和 Servlet 的分工。新一点的写法是用注解在 Servlet 类上直接写WebServlet(/article)省掉 web.xml 里那一大段。两套机制在 Tomcat 8.5/9 下是共存的但如果同一个 URL 在注解和 web.xml 里都声明了启动时 Tomcat 会报 “servlet mapping conflict” 之类的冲突错误这是传统项目里很常见的翻车点。排查时优先看是不是重复映射。我读老项目习惯先看web.xml因为它还包含另外两个重要配置welcome-file-list决定访问根路径时默认打开哪个页面session-config里的超时时间决定登录状态能维持多久。这两个配置往往是“路径不对”和“登录老掉线”的根因。3.2 一次请求在 Servlet 里的完整旅程doGet 与 doPostServlet 的生命周期是面试常客也是排查问题的钥匙加载与实例化、init()初始化一次、service()按请求方法分发到doGet或doPost、容器关闭时执行destroy()。博客系统里打开文章列表是 GET 请求提交登录表单是 POST 请求。你会在ArticleServlet里看到doGet和doPost两个方法前者负责查数据渲染页面后者通常先处理表单再重定向或转发。一个典型流程protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 从数据库查文章列表 ListArticle list articleDao.listAll(); // 放到 request 作用域 request.setAttribute(articleList, list); // 转发到 index.jsp 渲染 request.getRequestDispatcher(index.jsp).forward(request, response); }request.setAttribute把数据塞进请求作用域JSP 页面里用c:forEach或表达式语言取出来forward是服务器内部跳转浏览器地址栏不会变。与之相对的是response.sendRedirect那是浏览器重新发起请求地址栏会变。很多“点完按钮页面没反应”的问题就是该转发时用了重定向request 里塞的数据丢了。Servlet 是单实例多线程设计同一个 Servlet 对象会同时服务多个请求所以不要在doGet/doPost里往成员变量写数据成员变量是所有线程共享的会造成脏读。局部变量是安全的。这个点不光面试考实际并发一上来就会暴露。3.3 把功能映射到文件文章、登录、评论分别在哪几个 Java 类里博客系统再怎么包装核心功能就四块文章列表、文章详情、登录、评论。先确认源码目录里这几个类的位置再动改。典型结构src/ cn/blog/ dao/ ArticleDao.java UserDao.java CommentDao.java entity/ Article.java User.java Comment.java servlet/ ArticleServlet.java LoginServlet.java CommentServlet.java util/ DBUtil.java MD5Util.java web/ index.jsp article.jsp login.jsp admin/ WEB-INF/web.xmldao层是纯 JDBC 操作ArticleDao.listAll()返回ListArticleservlet层调用 dao 并控制页面跳转entity层是数据对象字段基本和数据库表一一对应。文章表的典型字段包括主键、标题、正文、发布时间外加一个状态字段status默认值为 00 表示草稿、1 表示发布——这个默认值设计决定列表页要不要写WHERE status 1是筛选“已发布文章”的关键。文章详情页通常是/article.jsp?id1这种形式ArticleServlet从 request 里取id参数按主键查一条记录转发到article.jsp展示。这个页面加一点个人资料展示区就是现成的jsp 个人信息展示页面改造模板。登录逻辑集中在LoginServlet登录成功会往HttpSession里放用户对象之后判断“是否已登录”都靠检查 Session 里有没有这个对象。评论模块则会涉及事务先插入评论再更新文章的评论计数两步要么都成功要么都回滚这就是一个标准的 JavaWeb 事务案例。4. 连接与部署JDBC 驱动、连接池参数到 Tomcat 启动代码读懂之后最影响“能不能跑起来”的其实是数据库连接这一层。传统 JSP 项目的连接代码通常集中在DBUtil.java所有翻车现场也大多从这里开始。4.1 驱动选择mysql-connector-java 5.1 与 8.0 的差异源码WEB-INF/lib目录下一般会带一个mysql-connector-java的 jar 包。这个包的版本直接决定Class.forName(...)里的驱动类名和 jdbcUrl 写法。驱动版本driverClass 写法jdbcUrl 注意点适用场景5.1.xcom.mysql.jdbc.Driver不强制带时区但 MySQL 8 下会有警告老项目、MySQL 5.7 环境8.0.xcom.mysql.cj.jdbc.Driver必须带serverTimezone否则报时区异常MySQL 8.0 环境最常见的坑是本机装的是 MySQL 8.0但源码 lib 里带的是 5.1 驱动连接 MySQL 8 时直接报ClassNotFoundException: com.mysql.jdbc.Driver。解决方法是把 lib 里的老 jar 换成 8.0.x 版本并把DBUtil.java里的驱动类名同步改成com.mysql.cj.jdbc.Driver。驱动包的版本差还会引出另一个隐蔽问题MySQL 8 默认使用 caching_sha2_password 认证插件老驱动不一定支持会报认证失败。所以遇到“密码明明对但连不上”先查驱动版本再查连接串。4.2 c3p0 连接池配置driverClass、jdbcUrl、账号密码与三个必调参数很多源码里已经用了 c3p0 连接池配置文件是src/c3p0-config.xml。它比裸 JDBC 好在连接复用不每次请求都新建和销毁连接页面并发时不至于把数据库拖垮。?xml version1.0 encodingUTF-8? c3p0-config default-config property namedriverClasscom.mysql.cj.jdbc.Driver/property property namejdbcUrljdbc:mysql://localhost:3306/blog?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf8/property property nameuserblog_user/property property namepasswordblog_pass/property property nameinitialPoolSize5/property property namemaxPoolSize20/property property namecheckoutTimeout3000/property property namemaxIdleTime1800/property /default-config /c3p0-configdriverClass和jdbcUrl是连接池找数据库的路线图user/password对应 2.3 节建的账号。三个必调参数里initialPoolSize是启动时预建的连接数太小会导致刚启动时前几个请求很慢maxPoolSize决定并发上限毕设项目 10–20 就够checkoutTimeout是等连接的超时时间单位毫秒设成 3000 表示 3 秒内拿不到连接就报错比默认值更能快速暴露连接泄漏。注意 jdbcUrl 里的在 XML 里必须转义成amp;写成会导致 XML 解析报错。参数useSSLfalse是关掉 SSL 加密传输本地开发不需要serverTimezoneAsia/Shanghai解决 MySQL 8 的 8 小时时区偏差characterEncodingutf8保证中文写入不乱码。这三个参数是 MySQL 8 下让老项目“起死回生”的后悔药。如果源码里没有连接池而是每个方法里DriverManager.getConnection建议先把DBUtil改成 c3p0 版再往下走。连接池不是锦上添花是毕设答辩时被问“如何优化并发”的保底答案。4.3 在 IDEA 里启动 Tomcat运行配置、Application context 与 404 排查环境、数据库都就绪后第一次启动大概率还是会 404。先检查三件事。第一运行配置里的Application context是否和访问路径一致。配的是/blog就要访问http://localhost:8080/blog/访问根路径http://localhost:8080/必然 404。我见过太多人把 context 填成/然后 index.jsp 里所有链接都按绝对路径写导致整个站全是死链接。第二Artifact类型。开发期必须用war exploded选成war的话每次修改 Java 类都要重新打包体验极差。第三端口占用。Tomcat 默认 8080如果之前启动过另一个实例会报Port 8080 is already in use。解决方式不是盲目换端口而是先排查是谁占的Windows 下netstat -ano | findstr 8080找到 PID 后去任务管理器结束进程Linux/macOS 下lsof -i:8080。Artifact 类型产物开发期使用发布使用war exploded解包后的目录推荐支持热加载不适合war单个 war 压缩包不推荐推荐部署简单热加载不是次次都灵。改了web.xml或新增 Servlet 类时IDEA 的依赖分析经常漏掉表现为“改了没生效”。这时手动重启 Tomcat别在玄学问题上耗时间。4.4 传统 JSP 项目打包 warIDEA Artifacts 与 Maven 两条路开发调试没问题后要把项目部署到服务器最常见的形式就是打包 war。IDEA 里操作路径很短Build - Build Artifacts - 选 war - Action - Build产物在out/artifacts/下文件名就是 war 名。把这个 war 扔到 Tomcat 的webapps目录里启动 Tomcat 就会自动解压部署。带 Maven 的项目则更省事直接在项目根目录执行# 跳过测试打 war 包 mvn clean package -DskipTests # 把 war 部署到 Linux 服务器的 Tomcat webapps 目录 cp target/blog.war /opt/tomcat9/webapps/ # 启动 Tomcat 并盯日志 /opt/tomcat9/bin/startup.sh tail -f /opt/tomcat9/logs/catalina.outwar 包的名字就是访问路径默认值blog.war部署后访问http://服务器IP:8080/blog/。想让项目直接跑在根路径就把 war 改名为ROOT.war再扔进去。生产环境常见架构是 Nginx 反代 TomcatNginx 本身不解析 JSP它只负责把请求转发给 Tomcat静态资源由 Nginx 直接返回JSP 部分给 Tomcat 处理。这套组合里war 包部署逻辑完全不变只是入口从 8080 端口换成了 80 端口。Linux 服务器上如果 MySQL 是全新安装的记得在服务器上重新执行一遍 2.3 节的建库和导入步骤再把源码里的数据库地址从localhost改成服务器的内网 IP。很多人本地跑通、上传服务器就跑不通就差这一步。5. 常见问题排查与避坑从 404、500 到中文乱码的 5 个真实案例这一章全是按“现象 → 原因 → 解决”的结构写。每一条都是我在这类老项目上反复踩过的坑按出现频率排序。5.1 页面 404Application context 与 war 名对不上现象首页能开点文章链接 404或者 IDEA 里启动正常单独部署 war 后所有页面 404。原因页面里写的链接是/blog/article?id1这种绝对路径而当前运行环境的 context 不是/blog。IDEA 里 context 是运行配置里配的war 部署时是 war 文件名决定的两边不一致就全站 404。解决统一 context。IDEA 里把Application context改成和 war 名一致或反过来把所有 JSP 里的项目前缀改成当前 context。更彻底的办法是让 JSP 里所有链接都用${pageContext.request.contextPath}拼前缀这样不管部署成什么名字都不会断链。5.2 Tomcat 10 启动即报错javax.servlet 找不到了现象把项目部署到 Tomcat 10启动时大量NoClassDefFoundError: javax.servlet.ServletException或者ClassNotFoundException: javax.servlet.http.HttpServlet。原因Tomcat 10 起Java EE 的javax.servlet命名空间整体迁移到了jakarta.servlet老代码里所有import javax.servlet.*都失效了。解决最省事的是把 Tomcat 换回 8.5 或 9.0代码一行不用改。如果想升级到 Tomcat 10需要全局替换javax.servlet→jakarta.servlet以及把javax.annotation等一并替换工作量大且容易漏。对这套博客源码我建议至少先在这个阶段用 Tomcat 9 跑通升级放后面单独做。5.3 MySQL 8 连不上Public Key Retrieval 与 SSL 报错现象项目启动报Public Key Retrieval is not allowed或Communications link failure但用 Navicat 连同一个库却正常。原因MySQL 8 的 caching_sha2_password 认证机制需要获取服务端公钥JDBC 默认不允许同时 MySQL 8 默认开启 SSL老连接串没处理这两个新参数。解决在 jdbcUrl 里追加三个参数jdbc:mysql://localhost:3306/blog?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiuseSSLfalse关掉 SSL 加密本地和内部网络没必要allowPublicKeyRetrievaltrue允许客户端从服务端获取公钥完成认证serverTimezoneAsia/Shanghai修正时区。注意连接串里有时如果写在 XML 文件里要写成amp;否则解析报错。5.4 JSP 页面中文全是问号三处字符集不一致现象页面上的中文正常但往数据库插入的中文变成???或者反过来数据库里数据正常页面显示乱码。原因字符集链路断了一环。浏览器请求、JSP 页面编码、JDBC 连接编码、MySQL 表编码这四处必须统一。老项目最常见的是 JSP 页面声明了GBK而数据库建库用了utf8mb4两边一交接就是乱码。解决统一成 UTF-8。JSP 首行加% page contentTypetext/html;charsetUTF-8 languagejava %JDBC 连接串加characterEncodingutf8建库时用utf8mb4如果表已经建好用ALTER TABLE t_article CONVERT TO CHARACTER SET utf8mb4;补救。注意 MySQL 连接串里的编码参数只对写入生效已经变成???的数据要清掉重插。5.5 Navicat 能连、项目连不上驱动没打包与连接池耗尽现象数据库工具能连JDBC 启动时报ClassNotFoundException或者运行一段时间后报checkoutTimeout超时。原因前半段是驱动 jar 没被打进 Artifact——IDEA 里 lib 目录的 jar 要手动添加到 Artifact 的WEB-INF/lib下不然编译能过、运行时找不到后半段是连接池耗尽——maxPoolSize20但代码里用连接没有finally里close()连接越借越少最后全部超时。解决先在Project Structure - Artifacts - WEB-INF/lib确认 mysql 驱动 jar 在列表里没有就 - Library加进去然后重新构建。连接池耗尽时用SHOW PROCESSLIST;看 MySQL 上的连接数如果堆积的是同一个应用的大量 Sleep 连接去代码里检查有没有用完归还——尤其注意异常分支里漏了close()。修复思路是统一在finally块里释放 connection、preparedStatement、resultSet 三个资源。6. 进阶把这份博客源码改造成你的毕设或上线项目先做这三件事基础功能跑通只是起点。如果你打算拿这套代码交毕设、写进简历或者真放到公网跑下面三件事优先级最高。6.1 第一件事用 Filter 统一处理字符编码和登录校验代码越散乱码和越权问题越难查。用一个 Filter 把请求编码统一掉顺便把需要登录的后台页面拦住。WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); chain.doFilter(req, resp); } }req.setCharacterEncoding必须放在读取任何参数之前才有效Filter 就是在 Servlet 之前执行所以这是最安全的位置。它解决 POST 表单的中文乱码而 GET 请求的乱码主要靠 Tomcat 的URIEncodingUTF-8配置来解决。加上这个 Filter 之后再把admin目录下的 JSP 都改成先检查 Session没登录就重定向到登录页后台防越权就搭起来了。6.2 第二件事给文章列表加分页并换成 PreparedStatement博客文章会越来越多列表页SELECT * FROM t_article很快会变慢。先改 SQL再加索引-- 第 2 页每页 10 条LIMIT 偏移量, 每页条数 SELECT id, title, publish_time FROM t_article ORDER BY publish_time DESC LIMIT 10, 10; -- 给排序字段加索引否则数据量大时 ORDER BY 会全表扫描 ALTER TABLE t_article ADD INDEX idx_publish_time (publish_time);JDBC 里执行分页查询时偏移量和每页条数必须用PreparedStatement的setInt绑定不要字符串拼接。一方面防 SQL 注入另一方面 MySQL 的预处理语句能复用执行计划。深分页也有坑LIMIT 100000, 10时数据库要扫过前十万行越往后越慢这是面试里常说的深分页问题。毕设阶段做到LIMIT 索引已经足够。6.3 第三件事上线前的验证清单最后别急着把 war 往服务器一扔就完事。我上线前的固定检查项是第一确认连接池有上限maxPoolSize不是 0 或无限大第二用mysqldump -u root -p blog blog_backup.sql留一份可恢复的备份第三把 Tomcat 日志级别调到INFO跑一轮完整流程后grep -i exception catalina.out看有没有异常第四用并发工具或简单地开两个浏览器窗口同时访问确认不出现数据错乱。我早期带项目时最常犯的错就是本地跑通后直接部署结果上线第一晚连接池就被打爆因为代码里所有数据库操作都在裸用连接根本没还回去。那之后我把“连接是否在 finally 里释放”当成上线一票否决项宁可功能少一个也不能带病上线。这套 JSPServletMySQL 博客源码的价值就是让你在一个足够小、足够完整的例子里把“从开发到上线”的整条链路走一遍把上面这三件事做完它就不再是别人的毕业设计而是你自己的完整项目了。希望帮到你。本文还有配套的精品资源点击获取