ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

jsp+servlet+jdbc+mysql后台管理系统源码解析与复现指南

2026/10/8 3:30:23 拓冰建站 浏览量
jsp+servlet+jdbc+mysql后台管理系统源码解析与复现指南 简介一套基于 jspjsservletjdbcmysql 原生界面开发的后台管理系统源码适合 Java Web 初学者、课程设计或毕业设计参考。项目不依赖复杂前端框架完整实现登录、注册、用户/产品增删改查、分页查询、图片上传、退出及未登录过滤器拦截等后台常见功能可帮助读者理解 servletjdbc 的请求处理与数据库交互流程。压缩包共92个文件含14个java与12个jsp源码、28个class编译文件以及jar依赖、xml配置、数据库脚本和docx导入步骤说明整体仅3.04MB便于快速下载与本地部署。附带的 mydb.sql 可直接建库工程导入步骤与运行必读文档能减少环境配置弯路。已有3371人学习下载适合需要完整可运行后台项目或希望逐模块拆解原生 Java Web 开发的读者。1. 这套 jspservletjdbcmysql 后台管理系统老技术栈为什么还能救急去年我带的一个练手小组把课程设计题目定为“产品后台管理系统”要求不许用 Spring Boot必须用 Java 原生的 jspservletjdbcmysql 把界面和逻辑写出来。折腾两周后他们交上来的东西和这份 xianfengProject 的结构几乎一样登录、注册、产品增删改查、分页、图片上传、过滤器拦 session。我当时把这套源码当参考答案发给组里新人时说了一句话把这几条链路跑通再去碰框架脑子里才有“请求到底是怎么穿过后端”的那张地图。现在很多培训项目上来就是 Spring BootMyBatis-Plus模板一生成servlet 和 jdbc 反而成了黑匣子。这套源码值钱的地方恰恰是它把黑匣子拆开给你看分页用 limit 怎么算、图片上传怎么落盘、过滤器怎么拦未登录用户。适合三类人刚学完 JavaWeb 想找完整闭环案例的在校生准备 java 面试想复习 servlet 和 jdbc 底层细节的求职者以及需要一套老技术栈管理系统改改就交差的在职工程师。我照着跑通一次全程没遇到玄学问题但确实有几个坑后面单开一章说。2. 项目功能与原理解剖登录、产品管理、过滤器是怎么串起来的2.1 五个功能模块的请求映射关系这套系统的主入口是 index.jsp上面放了登录和注册两个入口。登录成功之后 session 里会写入用户标识然后跳转到产品管理总页面。产品管理页承载了最重的业务查询某个产品、添加、修改、批量删除、查看详情、分页展示。退出功能就是清 session 回主页逻辑最简单但承载着整个权限闭环。从请求路径来看整个系统的流转关系大概是这样一张映射表请求路径对应处理说明/index.jsp主页展示未登录时的默认落地页/login.doLoginServlet校验用户名密码成功则写入 session/register.doRegisterServlet新增用户写入 t_user 表/product/list.doProductServlet(list)分页查询产品默认 pageIndex1/product/add.doProductServlet(add)添加产品涉及图片上传/product/update.doProductServlet(update)修改产品/product/batchDelete.doProductServlet(batchDelete)批量删除/logout.doLogoutServlet清除 session 后 redirect 到 index.jsp我在实际复现时会先画这张表再去对照源码里的 servlet 映射。因为原生 servlet 项目没有框架里那种注解路由或者只在 servlet 上用 WebServlet通读一遍 web.xml 或注解整个功能边界就清楚了。2.2 目录结构里藏着的老工程痕迹压缩包解开后的内容很有意思它同时保留了 .idea 和 .mymetadata、.project、.classpath说明这份源码在 IntelliJ IDEA 和 MyEclipse 里都被打开过。xianfengProject.iml 是 IDEA 的模块文件out 目录是 IDEA 编译输出目录WebRoot 是 web 根目录。这些细节对复现有用用 IDEA 导入时直接 Open 这个目录IDEA 会读取 .iml 恢复模块结构用 Eclipse/MyEclipse 导入时则看 .project 和 .classpath。src 目录放 Java 源码servlet、dao、bean、utilWebRoot 目录放 jsp 页面、WEB-INF/web.xml、js、css、上传图片目录压缩包内附带的 mydb.sql 是初始化脚本工程导入步骤.docx 和运行必读.txt 是作者写的操作说明我一般建议先打开运行必读.txt 再动手因为这种老工程最容易翻车的就是环境版本不匹配。正文里写了 jdk1.7、tomcat7、mysql5.5/5.6/5.7这三个版本必须对齐别脑子一热装个 tomcat10servlet 包名都变了代码直接编译不过。2.3 mydb.sql 里的核心表结构设计这套系统只需要两张核心表用户表 t_user 和产品表 t_product。产品表要存图片路径所以单独留了一个 image_path 字段价格用 DECIMAL 而不是 FLOAT避免精度问题。下面是按常规设计补全的建表 SQL和源码里 mydb.sql 的思路一致CREATE DATABASE IF NOT EXISTS xianfeng_db DEFAULT CHARSET utf8mb4; USE xianfeng_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, description TEXT, image_path VARCHAR(255), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO t_user(username, password) VALUES(admin, 123456);这里有两个细节值得注意。第一字符集要 utf8mb4 而不是 utf8否则插入 emoji 或生僻字时 jdbc 连接 mysql 会直接报 Incorrect string value。第二密码字段设计成 VARCHAR(50) 是课程设计常见做法生产环境绝不这么干至少得加盐哈希。我复现时只是为了跑通流程所以沿用原设计的明文存储但如果你要拿这套资源改造成真正的工程密码存储是第一件必须改的事。3. 从压缩包到跑通环境、导入、建库、部署四步走3.1 环境版本对齐少一步后期就是玄学现场老工程最怕的就是版本漂移。项目说明明确写了 jdk1.7、tomcat7、mysql5.5/5.6/5.7。我本地用的组合是 jdk1.7 tomcat7.0.94 mysql5.7.44一次跑通。如果你电脑上已经装了高版本 JDK比如 JDK8 或 JDK11编译目标可以调成 1.7 来兼容但 tomcat 建议老老实实用 7.x。tomcat8.5 以上对老式 jsp 的隐式对象处理有细微差异虽说大概率没问题但没必要为了一个复现去赌兼容性。还有 jdbc 驱动版本mysql5.7 用 mysql-connector-java-5.1.49.jar 最常见驱动类名是 com.mysql.jdbc.Driver。如果你手头是 8.x 的驱动包驱动类名要换成 com.mysql.cj.jdbc.Driver连接串还要额外加 serverTimezone 参数。这一步是 jdbc 连接 mysql 的经典坑后面避坑章细说。3.2 用 IDEA 导入工程源码自带 .idea 目录和 iml 文件所以用 IntelliJ IDEA 打开最省事。不是新建工程而是 File - Open 直接选中 xianfengProject 目录IDEA 会根据 iml 恢复模块配置。打开后第一件事是检查 Project StructureProject SDK: 选 1.7 或按需调成 1.8编译级别 Level 7 Project language level: 7 - Diamonds, ARM, multi-catch 等 Modules - Dependencies: 确认 lib 下的 mysql-connector 等 jar 已加入之后配置 TomcatRun - Edit Configurations - 左上角 - Tomcat Server - Local在 Deployment 页签里添加 ArtifactApplication context 填 /xianfengProject。这个 context path 决定了你访问地址是 http://localhost:8080/xianfengProject/index.jsp。逻辑说明老式 web 工程在 IDEA 里的部署方式是 Exploded解压目录不是 war 包。这样做的好处是 jsp 修改后刷新页面就能看到不用重新打包。你在 Deployment 里看到的 artifact 类型应该是 Web Application: Exploded。3.3 初始化数据库与账号配置打开命令行进入 mysql 的 bin 目录执行mysql -u root -p mydb.sqlmydb.sql 在压缩包根目录建议先用记事本打开看一眼确认里面的库名和表名。如果脚本里没有 CREATE DATABASE你需要手动先建库再使用。导入成功后在 mysql 命令行执行 SHOW TABLES; 能看到 t_user 和 t_product 就说明数据库这边齐活了。然后打开源码里的 jdbc 配置文件一般是 src 下名为 db.properties 或 jdbc.properties 的文件改成你本地的账号密码jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/xianfeng_db?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password你的数据库密码参数说明useUnicodetrue 和 characterEncodingutf8 这两个参数建议保留否则从 mysql 读中文再写进 jsp 显示时容易出现乱码。端口 3306 如果你本地改过也要同步换。这条连接串是 jdbc 连接 mysql 最经典的写法很多框架的底层数据源本质上也在拼这么一串。3.4 启动 Tomcat 并跑通第一个用例启动前先确认四件事tomcat 端口没被占、jdk 版本匹配、artifact 已经部署、mysql 服务是启动状态。然后点 IDEA 里的 Run 启动 Tomcat控制台出现 Server startup 日志后浏览器访问http://localhost:8080/xianfengProject/index.jsp首次访问能看到主页说明 web 应用没问题。然后用 admin/123456 登录如果能进产品管理页这就算跑通了。接下来建议立刻做两件事验证功能完整性随便添加一个带图片的产品然后在前台翻几页看分页是否正常。第一轮复现只要这两条链路通了这套源码的核心功能就全部验证到位了。4. 源码里值得一行行读的三个实现分页、图片上传、过滤器4.1 分页查询limit 参数与页脚逻辑原生 jdbc 做分页思路比 MyBatis-Plus 的 IPage 要直接得多。核心就是 SQL 拼 limit前端把当前页码传给 servletservlet 通过 request.getParameter 拿参数dao 层计算偏移量后执行查询。下面是 ProductDao 分页方法的核心代码public ListProduct findByPage(int pageIndex, int pageSize) { ListProduct list new ArrayListProduct(); String sql SELECT id, name, price, description, image_path FROM t_product ORDER BY id DESC LIMIT ?, ?; int start (pageIndex - 1) * pageSize; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, start); ps.setInt(2, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Product p new Product(); p.setId(rs.getInt(id)); p.setName(rs.getString(name)); p.setPrice(rs.getBigDecimal(price)); p.setDescription(rs.getString(description)); p.setImagePath(rs.getString(image_path)); list.add(p); } } } catch (SQLException e) { e.printStackTrace(); } return list; }代码逻辑start 计算的是跳过的记录数pageIndex 从 1 开始第一页就是 limit 0, 10。ORDER BY id DESC 表示新品在前后来我在改造时加了“默认按添加时间倒序”的需求逻辑同理。参数说明pageSize 是每页行数一般在 servlet 里写死成常量或者从前端下拉框传入这里用 PreparedStatement 预编译也顺便防了 SQL 注入是原生 jdbc 写法的标准姿势。配套的还要有查询总记录数的 count 方法页脚才能显示共 X 页public int count() { String sql SELECT COUNT(*) FROM t_product; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { if (rs.next()) { return rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } return 0; }前端页脚计算总页数时用 (total pageSize - 1) / pageSize 代替除法向上取整避免浮点误差。这是原生分页里最后一步很多新手在这里翻车。4.2 图片上传servlet3.0 的 Part 接口图片上传是整套系统里最有技术含量的环节。摘要里写了图片上传功能实现上在 tomcat7 环境可以直接用 servlet3.0 的 Part 接口不需要 commons-fileupload。product 表单里 input 标签要加 enctypemultipart/form-data这是第一个必须注意的点。WebServlet(/product/add.do) public class ProductAddServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(utf-8); String name request.getParameter(name); Part part request.getPart(image); String fileName getFileName(part); String uploadPath getServletContext().getRealPath(/) upload; File dir new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } part.write(uploadPath File.separator fileName); String price request.getParameter(price); String desc request.getParameter(description); // 组装 Product 对象调用 dao 层插入 t_product // image_path 字段存 upload/文件名方便页面拼接展示 response.sendRedirect(list.do?pageIndex1); } private String getFileName(Part part) { String header part.getHeader(content-disposition); String[] arr header.split(;); for (String s : arr) { if (s.trim().startsWith(filename)) { return s.substring(s.indexOf() 2, s.length() - 1); } } return default.png; } }这段代码有几个关键细节。getRealPath(/) 返回的是 webapp 部署后的磁盘绝对路径upload 目录要在第一次上传前创建否则 part.write 会报 FileNotFoundException。getFileName 这个方法不能省Part 对象没有直接提供原文件名方法只能从 content-disposition 头里截取。图片存储路径建议存相对路径 upload/文件名页面 标签直接写 srcupload/xxx.png 就能展示这也是摘要里提到“jsp 图片如何定位”的常见解法。4.3 过滤器session 验证与放行路径过滤器是这套系统安全机制的核心。如果用户没登录直接访问产品管理页面必须被拦回 index.jsp。实现就是实现 javax.servlet.Filter 接口在 doFilter 里检查 sessionWebFilter(/*) public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); String ctx request.getContextPath(); String path uri.substring(ctx.length()); boolean isStatic path.startsWith(/js/) || path.startsWith(/css/) || path.startsWith(/upload/) || path.endsWith(.js) || path.endsWith(.css); boolean isPublic path.equals(/index.jsp) || path.equals(/login.do) || path.equals(/register.do) || path.equals(/logout.do); HttpSession session request.getSession(false); boolean loggedIn session ! null session.getAttribute(user) ! null; if (loggedIn || isStatic || isPublic) { chain.doFilter(req, resp); } else { response.sendRedirect(ctx /index.jsp); } } }这个过滤器的逻辑要仔细理解放行三类请求——已登录用户的请求、静态资源、公开页面。没有登录且不满足放行条件的重定向回 index.jsp。我见过很多新手写的过滤器只判断了 session结果把登录页面和静态 JS 全都拦了页面样式全丢或者死循环重定向。注意 getSession(false) 不要写成 getSession()带参 false 不会自动创建 session避免未登录用户每次访问都被强制生成 session这是个资源浪费的隐形坑。5. 避坑指南复现这套源码最容易翻车的五个位置5.1 环境部署与启动过程中的典型问题现象一执行 net start mysql 时提示服务名无效或者服务正在启动后立刻停止。我遇到这台机器装的是 mysql5.7.44之前用安装版装过一次后来又解压了一份免安装版注册表里的服务名根本没对上。原因服务名不是默认的 mysql而是你安装时指定的名称可能是 MySQL57 或自定义的名字。解决用管理员身份打开 CMD执行 sc query 或直接到服务管理器里看服务名再执行 net start 服务名如果服务列表里没有就用 mysqld --install 先注册服务再启动。现象二本地装了 JDK8但源码在编译时报错看不到 API 或语法级别不对。这套工程注释里写的是 jdk1.7如果直接用 IDEA 默认 SDK 打开编译级别可能带着 1.8 语法servlet 代码没问题但某些老 lib 会和 JDK8 产生 bytecode 版本冲突。解决Project Structure 里把 Project SDK 定义为 1.7没有就先装一个Language level 选 7Modules 里的 SDK 也同步改。如果你机器上只有 JDK8 不想再装旧的把编译 target 调成 1.7 也能缓解大部分问题。现象三Tomcat 启动时端口被占用报 SEVERE 级别的 Socket bind 失败。原因多半是上次 IDEA 非正常关闭java.exe 进程还占着 8080。解决CMD 执行 netstat -ano | findstr :8080 查到 PID再 taskkill /PID 你的PID /F杀完重启就好。血泪经验不要一上来就把 tomcat 的 server.xml 端口改成 8081治标不治本而且 session 里的回跳地址不会自动适配新端口。现象四页面访问 404显示源服务器未能找到目标资源的表示。原因一般是 artifact 没部署成功或者 context path 不是 /xianfengProject。Tomcat 的部署机制里war 或 exploded artifact 要挂在指定 context 下访问路径是 ip:port/context/资源路径少一层或多一层都会 404。解决检查 Run Configuration 的 Deployment 页签确认 artifact 已经添加且 Application context 写的 /xianfengProject然后重启 Tomcat 重新部署。现象五数据库连接报 java.sql.SQLException: Unknown database。使用 mysql -u root -p mydb.sql 导入前如果 mydb.sql 里没有 CREATE DATABASE或者当前用户没权限建库表就落在系统库里甚至没落进去但 JDBC URL 里却写了 xianfeng_db结果就是连接报 Unknown database。解决先手动执行 CREATE DATABASE xianfeng_db DEFAULT CHARSET utf8mb4再进入该库导入脚本或者直接在 mysql 命令行用 source 方式导入 mydb.sql。5.2 代码运行与数据层面的常见踩坑点现象一前端中文乱码界面显示“锟斤拷”。原因有三个位置必须同时设置 UTF-8JSP 页面顶部的 pageEncoding、servlet 里 request.setCharacterEncoding(utf-8)、jdbc 连接串里的 characterEncodingutf8。少一个就乱这是老项目的连环坑。解决三个位置全部补齐product 添加页面和注册页面的表单都过一遍。现象二图片上传后 标签显示裂图刷新后图片丢失。原因开发环境用 getRealPath 把图片写到 tomcat 部署目录下的 upload 里但 IDEA 重启或 clean 后整个部署目录被重建上传的文件随之被删除。这是 tomcat explode 部署的经典副作用。解决把上传路径改成工程外目录比如 D:/upload然后通过映射或单独 servlet 提供图片访问。如果只是跑课程设计接受重启丢图片的现实也行但心里要清楚这个边界。现象三过滤器把登录页的 css/js 也拦截了页面虽然能打开但是光秃秃的没有样式。原因WebFilter(/*) 匹配所有请求css 和 js 也在拦截范围内session 为空时全部重定向到 index.jsp最终浏览器加载静态资源时循环重定向。我在 4.3 节里已经写了放行逻辑的完整代码把路径判断提前到 session 判断之前问题就解决了。现象四分页点下一页时说 pageIndex 为 null 的 NumberFormatException或者总页数算出来是 1 但数据有 50 条。原因servlet 里直接 Integer.parseInt(request.getParameter(pageIndex))第一页时链接没带参数就报空指针总页数用除法直接取整10 条数据 pageSize10 时总页数算成 1恰好第 11 条就消失了。解决parseInt 前先判空并给默认值 1总页数用 (total pageSize - 1) / pageSize 计算。6. 进阶改造把 jdbc 直连换成 Druid 连接池顺便解决连接资源释放问题如果你准备把这份源码改造成能拿去实习面试里讲的工程第一个要动刀的地方绝对不是界面美化而是数据源。原生 jdbc 最尴尬的场景就是每次请求都 DriverManager.getConnection用 complete 关 Connection、Statement、ResultSet代码丑且容易漏关。我的习惯是引入 Druid把数据源初始化挤到 static 块里让整个 webapp 只维护一个连接池。先在 lib 目录放 druid-1.1.24.jar然后在 src 下加一个 DBUtil 改造版import com.alibaba.druid.pool.DruidDataSource; public class DBUtil { private static DruidDataSource dataSource; static { dataSource new DruidDataSource(); dataSource.setDriverClassName(com.mysql.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/xianfeng_db?useUnicodetruecharacterEncodingutf8); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(3); dataSource.setMaxActive(10); dataSource.setMaxWait(3000); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }逻辑说明Druid 的 setInitialSize 是启动时预建的连接数setMaxActive 是池中最多同时活跃的连接数setMaxWait 是拿不到连接时最长等待毫秒数。这样就省去了每个 dao 方法里 DriverManager.getConnection 的重复代码连接获取速度也快了一个数量级。原有 dao 层的 try-with-resources 不用动因为 Druid 的 getConnection 返回的是代理连接调用 close 只是归还池子不会真的断开。dao 层代码里所有 Class.forName 加载驱动的语句都可以删掉了数据源初始化时已经完成。接下来把这段改动跑一遍回归登录、添加产品、上传图片、分页浏览、退出五个链路全部过一遍重点观察 Tomcat 控制台有没有连接泄漏提示。改完你会发现两件事第一页面响应速度在连续点击分页时明显变快因为连接复用而不是每次新建第二Tomcat 重启时再也不会因为连接未释放报警。从那以后我每次拿到这种课程设计源码都会强制先做一遍数据源改造再谈功能顺手也会把过滤器放行路径完整走查一遍。这套方法希望你也能用上帮你少走弯路。本文还有配套的精品资源点击获取