资讯动态

JavaWeb入门实战:注册登录+学生信息增删改查全流程解析

发布时间:2026/10/23 3:42:40 来源:尧图企业网站定制
如果有人问我 JavaWeb 入门阶段最值得动手敲一遍的小实例是什么我十有八九会推荐“注册登录 一套完整的增删改查”。别看这个组合听起来不惊艳它保护的恰恰是 JavaWeb 最核心的那条链路Servlet 生命周期、JSP 渲染、JDBC 操作、Session 状态管理、Filter 拦截再到 Maven 构建和 Tomcat 部署。你把这条链路完整跑通一遍再回头看什么框架、什么高级特性心里都有底。这篇博文里的全部撒花-小实例附源码就是一个典型的 JavaWeb 综合练习项目功能上并不复杂用户注册登录、登录状态校验、学生信息列表、新增、修改、删除。但它把 JavaWeb 日常开发里最高频的知识点全部串起来了。整理这篇文章的时候我会直接把项目的设计思路、数据库表结构、核心代码和排查过程都写出来你要是想动手做一遍跟着走基本就能落地也可以直接参考源码去改造成自己的管理后台。适合正在学 JavaWeb 的同学、准备做课程设计的开发者以及想复习 Servlet/JSP/JDBC 这套经典技术栈的从业者。1. 项目定位与技术选型思路1.1 这个小实例究竟覆盖了哪些知识点先把这个小实例的功能边界画清楚。项目分两部分第一部分是用户模块支持注册、登录、退出登录状态用 Session 保存同时用 Filter 做统一拦截第二部分是业务管理模块选了一个很常见的“学生信息管理”场景包含列表分页查询、新增、编辑、删除。整体数据量不大但“增删改查”四个字一个都不少。之所以说这个组合值得做是因为它刚好把 JavaWeb 开发的主线全部覆盖了。从浏览器发起请求开始请求先被 Servlet 接收Servlet 调用 DAO 层的方法操作数据库拿到数据之后决定是跳转 JSP 还是重定向JSP 里用 EL 表达式和 JSTL 标签动态渲染页面。这个过程中你还得处理表单参数的中文编码问题、用户未登录时访问受限页面的拦截问题、数据库连接怎么获取和释放的问题。这些点单独拆开每一个都不难但串在一起才是真正的 JavaWeb 全流程。更现实地说这套技能是后续学习 Spring MVC、Spring Boot 的底层基础。很多同学直接用 Spring Boot 写接口写得很溜但问到请求是怎么被 DispatcherServlet 分发到 Controller 的、Session 是怎么自动注入的就答不上来。其实这些机制的原型就在 Servlet 和 Filter 这套原生体系里。亲手做一遍原生版本你以后用框架时会顺畅很多。1.2 为什么选 ServletJSP原生JDBC而不是一上来就 Spring Boot我在带身边的同学做这个项目时经常被问到现在谁还用 JSP 写页面不都是前后端分离了吗这个问题问得有道理但得分场景。Spring Boot Vue 确实更贴近当下企业开发可如果你连 Servlet 和 HTTP 请求响应的基本过程都没体验过直接上全家桶会非常容易陷入“配置都会、原理全无”的状态。Servlet JSP JDBC 这套组合赢在“透明”。一个请求进来Tomcat 创建 Servlet 实例调用 service 方法你的代码从 HttpServletRequest 里拿参数调 DAO 去数据库取数再把结果 set 到 request 作用域最后 forward 到 JSP。每一步都是你能亲眼看到、能调试的。Spring Boot 封装得太好很多环节被自动配置藏起来初学者一旦遇到问题反而无处下手。一个比较贴切的类比是学开车先开手动挡换挡逻辑、离合配合你都得自己操作等动作形成肌肉记忆之后再开自动挡就非常轻松直接学自动挡虽然也能上路但遇到特殊情况很难理解车为什么会那样表现。当然这不是说 Servlet JSP 有多先进它只是更适合打基础。如果你已经做完了这个项目并且能清楚说出每个请求从发出到响应经历了哪些环节那就完全可以直接往 Spring Boot 的方向走到时候你会发现框架的抽象思路理解起来特别快。1.3 项目运行效果与技术栈清单项目最终跑起来的效果是浏览器打开首页如果没登录会自动跳转到登录页注册新账号后跳回登录页输入正确账号密码进入学生管理列表页可以看到分页展示的学生数据支持按姓名模糊查询、新增学生、编辑、删除。所有操作都需要登录状态未登录直接访问列表页会被 Filter 拦截并重定向到登录页。技术栈清单如下表所示这套组合在学习和课程设计场景里很经典也足够稳定组件选型说明语言JDK 8 / JDK 11兼容性最好不要追求太高版本Web 容器Tomcat 9支持 Servlet 4.0配置简单核心规范Servlet JSP EL JSTLJavaWeb 主链路数据库MySQL 8.x使用 InnoDB字符集 utf8mb4持久化方式原生 JDBC 工具类封装手写加载驱动、连接、释放构建工具Maven统一管理依赖和打包前端辅助Bootstrap 4可选页面不追求华丽清晰即可密码加密BCrypt规避明文存储和 MD5 彩虹表风险选 BCrypt 而不是 MD5 算是这个项目里一个比较重要的细节。MD5 虽然快但现在彩虹表和暴力字典在简单密码面前基本等于裸奔。BCrypt 是自带盐值的哈希算法每次加密结果都不同校验时用专门的方法比对安全性高出一个级别。很多老教程还在用简单 MD5你在做项目时可以直接跨过这个坑。2. 功能拆解与数据库设计2.1 模块划分从登录到管理的完整链路这个小实例虽然体量小但作为完整项目必须有清晰的模块边界。我的划分思路是这样的用户模块、数据管理模块、基础设施模块三部分。用户模块负责注册、登录、退出对应 RegisterServlet、LoginServlet、LogoutServlet以及配套的 UserDao 和 User 实体类。注册时要做基本的格式校验比如用户名长度、两次密码是否一致校验通过后加密密码再入库。登录成功后把用户信息放进 Sessionkey 命名为 loginUser后续所有页面都通过这个会话标记来判断登录态。数据管理模块是业务核心。以学生信息管理为例包含列表查询和增删改。列表查询我选择了分页参数判断当前页码从 request 参数里取默认第一页每页固定显示 5 条总页数通过总数除以每页条数向上取整得到。新增和编辑共用同一个表单页面编辑时根据 id 回显数据提交时按 id 是 null 还是有值来区分是新增还是更新。删除操作走单独的 Servlet按 id 删除后返回列表页。基础设施模块包含数据库连接工具类 DBUtil、登录过滤器 LoginFilter、编码过滤器 EncodingFilter以及统一的错误页面。这一层是被很多人忽略但极其重要的部分。Filter 解决的是“哪些请求需要拦截、哪些能放行”的问题编码过滤器解决的是中文乱码问题。没有这些基础设施项目可以说根本没法走出本地环境。2.2 数据库表结构与初始化 SQL数据库我建了一个名为 javaweb_demo 的库里面两张表用户表 t_user 和学生表 t_student。t_user 的字段尽量精简核心就是用户名、密码、创建时间密码字段需要额外注意长度因为 BCrypt 加密后的字符串固定是 60 个字符所以 varchar(60) 就足够但如果你预留多一些比如 varchar(100)也没问题避免以后换算法时长度不够。CREATE DATABASE IF NOT EXISTS javaweb_demo DEFAULT CHARACTER SET utf8mb4; USE javaweb_demo; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT BCrypt加密后的密码, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 姓名, gender VARCHAR(10) COMMENT 性别, phone VARCHAR(20) COMMENT 手机号, email VARCHAR(100) COMMENT 邮箱, remark VARCHAR(255) COMMENT 备注, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;插入几条测试数据方便页面一启动就有内容看。INSERT INTO t_student (name, gender, phone, email, remark) VALUES (张同学, 男, 13800000001, zhangexample.com, 计算机系), (李同学, 女, 13800000002, liexample.com, 软件工程系), (王同学, 男, 13800000003, wangexample.com, 网络工程系);这里有一个实际开发中常见的细节数据库连接 URL 要带上 useUnicodetruecharacterEncodingutf8 和 serverTimezoneAsia/Shanghai 参数。MySQL 8 的驱动对时区要求比较严格不写时区参数的话连接阶段可能直接报 CST 相关的异常。另外表名和字段名尽量别用 MySQL 的保留字user 虽然不是保留字但为了避免个别环境下的兼容问题我习惯性加上了 t_ 前缀。DBUtil 的设计也要注意不能每次操作都重复写一遍加载驱动、获取连接的模板代码。我封装了一个静态方法 getConnection()同时提供了一个关闭资源的 close() 方法把 Connection、Statement、ResultSet 三个资源的释放都收拢到一个方法里。public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/javaweb_demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USERNAME root; private static final String PASSWORD 你的数据库密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USERNAME, PASSWORD); } public static void close(ResultSet rs, Statement stmt, Connection conn) { if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } }驱动只要加载一次就够了所以放在静态代码块里。资源关闭必须放在 finally 中执行否则查询异常时连接会一直占着不放。实际生产环境一般用连接池比如 Druid 或 HikariCP来管理连接但教学项目里先用 DriverManager 理解连接获取的过程也是合理的选择。3. 核心代码实现与关键链路解析3.1 项目目录结构与 Maven 依赖配置写 JavaWeb 项目我建议从一开始就用 Maven 来管理依赖而不是手动把 jar 包塞进 WEB-INF/lib。Maven 带来的最大好处是依赖传递和版本统一你只要在 pom.xml 里声明需要什么构建工具自动下载关联库。项目的标准目录结构如下javaweb-demo ├── pom.xml └── src/main ├── java │ └── com/example │ ├── dao │ │ ├── UserDao.java │ │ └── StudentDao.java │ ├── entity │ │ ├── User.java │ │ └── Student.java │ ├── filter │ │ ├── EncodingFilter.java │ │ └── LoginFilter.java │ ├── util │ │ └── DBUtil.java │ └── web │ ├── LoginServlet.java │ ├── RegisterServlet.java │ ├── LogoutServlet.java │ ├── StudentListServlet.java │ ├── StudentEditServlet.java │ └── StudentDeleteServlet.java └── webapp ├── static │ ├── css │ └── js └── WEB-INF └── jsp ├── login.jsp ├── register.jsp ├── list.jsp └── edit.jspJSP 页面放在 WEB-INF 目录下这是一个很关键的细节。WEB-INF 下的资源不能通过浏览器 URL 直接访问用户只能通过 Servlet 的 forward 跳转进入页面。这么做的好处是所有页面的访问入口都收口在 Controller 层配合后面的 LoginFilter就形成了完整的访问控制链而不是把页面裸露出来任人访问。pom.xml 里的关键依赖并不复杂真正需要留意的反而是依赖的作用域配置。Servlet 和 JSP 相关的依赖在 Tomcat 里已经内置了如果不指定 provided 作用域打出的 war 包里会带上这些库部署到 Tomcat 后可能因为类冲突出现奇怪问题。dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdorg.mindrot/groupId artifactIdjbcrypt/artifactId version0.4/version /dependency /dependencies3.2 注册登录从表单到 Session 的完整链路登录注册这块是整个项目的门面也是所有权限控制的基础。我先说注册的流程。用户在前端注册页填写用户名、密码、确认密码提交的时候先由前端 JS 做一层简单校验比如用户名不能为空、密码长度至少 6 位、两次密码一致。但前端的校验只是提升体验真正可靠的校验必须放在后端 Servlet 里再做一遍因为绕过前端直接发请求是很简单的事情。RegisterServlet 的处理逻辑按照这个顺序执行接收参数、后端校验、密码加密、调用 DAO 判断用户名是否已存在、执行插入、重定向到登录页。用户重复注册的情况尤其要注意如果用户名已存在大概率你是用 result.addError 的思路回显错误信息而不是直接弹一个死提示让用户回退。我在这个项目里采用的方式是 request.setAttribute(errorMsg, 用户名已存在)然后 forward 回注册页注册页里用 EL 表达式展示这段提示。这样用户能看到具体原因不需要重新填整张表单。密码加密的代码是核心之一String bcryptHash BCrypt.hashpw(password, BCrypt.gensalt()); user.setPassword(bcryptHash);登录时校验密码用 BCrypt.checkpw(inputPassword, storedHash) 方法。它的原理是取出存入的哈希值中的盐再用这个盐对用户输入做同样次数的加密最后和存储值比对。整个过程对调用方来说就是一行代码但安全性远远强于 MD5 加固定盐的做法。接下来看 LoginServlet。用户登录成功后要做三个动作把用户信息放入 Session、保存登录时间等辅助信息、重定向到列表页。Session 的 key 我固定命名为 loginUser后面所有需要登录态的页面都判断这个 key 是否存在。这里有一个新手很容易踩的坑登录成功用 forward 跳转。这样做的后果是地址栏仍然是 /login用户刷新页面会重复提交表单而且之后访问列表页时浏览器地址栏不对整站的感觉非常别扭。正确做法是用重定向 sendRedirect让浏览器重新发起一次对目标地址的请求既避免了表单重复提交也让 URL 变得干净。登录页的 JSP 片段里提示信息的展示逻辑值得单独领略一下c:if test${not empty errorMsg} div classalert alert-danger${errorMsg}/div /c:if这里要特别提醒你检查 JSP 顶部是否加了% page isELIgnoredfalse %。Servlet 3.0 之后 EL 是默认开启的但如果你用了某些老旧的 web.xml 配置或者不完整的模板EL 会被当成普通文本直接输出在页面上看到{errorMsg}这种字面量时优先怀疑这个问题。3.3 登录拦截器Filter 的放行规则与实现登录拦截是权限控制的核心我在这个项目里通过一个 LoginFilter 来实现。很多人写 Filter 时最容易犯的错误是试图把所有请求都放行只拦截某一个路径结果要么漏掉了某个资源要么把 CSS 和 JS 也拦截导致页面样式全丢。正确思路是静态资源无条件放行、登录相关接口放行、其余请求都检查会话。Filter 的注解写法很简单直接用 WebFilter(/*) 声明拦截所有请求然后在 doFilter 方法里做判断WebFilter(/*) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); // 放行静态资源 if (uri.startsWith(/static/)) { chain.doFilter(req, resp); return; } // 放行登录/注册相关 if (uri.endsWith(/login) || uri.endsWith(/register) || uri.endsWith(/login.jsp) || uri.endsWith(/register.jsp)) { chain.doFilter(req, resp); return; } // 校验登录会话 Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }这个拦截器的执行时机在 Servlet 处理请求之前所以它能做统一的预判。只要 Session 里没有 loginUser就直接重定向到登录页。这比在每个 Servlet 里复制粘贴登录判断的代码要优雅得多。需要注意的是重定向时一定要使用 request.getContextPath() 拼接项目路径否则如果应用部署在 Tomcat 的非根路径下浏览器重定向会找不到项目。编码过滤器 EncodingFilter 也值得一写它的核心工作是设置请求和响应的字符集WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); resp.setContentType(text/html; charsetUTF-8); chain.doFilter(req, resp); } }如果你发现表单提交中文到数据库后是问号八成是请求编码没有设置如果页面显示乱码八成是响应编码或 JSP 页面本身的 pageEncoding 设置有问题。设置编码的 Filter 要放在 LoginFilter 前面执行因为登录校验本身也可能需要读取中文参数。Filter 的执行顺序遵循 web.xml 中的声明顺序如果用注解方式配置可以通过 WebFilter 的 filterName 和 web.xml 里对应的映射顺序来控制。3.4 学生管理模块分页列表与增删改落地写法学生列表页是整个项目里最常打交道的页面。在设计 StudentListServlet 时我加入了分页和按姓名模糊查询两个功能。分页参数是新手最容易搞混的地方我简化一下总共需要四个变量当前页 currentPage、每页条数 pageSize、总记录数 total、总页数 totalPages。currentPage 从请求参数 currentPage 获取没有则默认为 1pageSize 固定为 5total 通过SELECT COUNT(*) FROM t_student WHERE name LIKE ?查询在用户没有输入关键字时就是不带条件的统计totalPages 用公式(total pageSize - 1) / pageSize算出这个公式的本质是向上取整除法贯通写法。接下来 sql 就是带 limit 的查询语句SELECT * FROM t_student WHERE name LIKE ? ORDER BY id DESC LIMIT ?, ?;LIMIT 的第一个参数是偏移量等于 (currentPage - 1) * pageSize第二个参数是 pageSize。实际操作中分页数据要放进一个工具类 PageInfo 里把当前页数据、总页数、当前页、总记录数、关键字全部包裹进去再 set 到 request 作用域最后 forward 到 list.jsp。JSP 里用 JSTL 的 forEach 循环遍历配合 EL 表达式逐行输出c:forEach items${pageInfo.list} varstu tr td${stu.id}/td td${stu.name}/td td${stu.gender}/td td${stu.phone}/td td${stu.email}/td td a href${pageContext.request.contextPath}/student/edit?id${stu.id}编辑/a a hrefjavascript:; onclickconfirmDelete(${stu.id})删除/a /td /tr /c:forEach注意上下文路径的写法${pageContext.request.contextPath}是 JSP 里获取项目根路径的标准方式。硬编码路径在本地部署没问题但换一个部署环境就全部失效所以统一用它来拼接。删除操作这里我想单独强调一下安全性。很多人图省事直接写一个a href/student/delete?id1删除/a这个写法致命的地方在于搜索引擎爬虫、浏览器预加载、甚至误触都有可能触发 GET 请求于是数据就被删了。正确的做法是给删除按钮绑定一个点击事件用 confirm 弹出确认框确认后才发起删除请求。更进一步可以用>function confirmDelete(id) { if (confirm(确定要删除这条记录吗)) { const form document.getElementById(deleteForm); form.querySelector(input[nameid]).value id; form.submit(); } }对应的 HTML 里放一个隐藏的表单提交到/student/deleteServlet 端必须是 POST。新增和编辑这两块共用 edit.jsp 页面。页面打开时判断 request 作用域里有没有 student 对象如果有说明是编辑回显将每个属性值填到输入框的 value 里如果没有说明是新增输入框保持空白。提交后StudentEditServlet 做的事情是接收参数、构造 Student 对象、判断 student.getId() 是否为 null为空就走新增逻辑否则走更新逻辑。这个“表单页复用”的思路在真实项目里非常常见它比写两个页面省去大量重复代码也降低维护成本。4. 常见问题排查与避坑指南4.1 项目启动阶段的典型报错和解决顺序启动阶段的问题最磨人因为往往还没跑到你的业务代码环境就先出了问题。我按经验把最高频的几类问题整理成一张速查表希望你能在遇到报错时按这个顺序排查现象可能原因排查方式Tomcat 启动失败提示端口占用8080 或 8005 被其他进程占用换端口或杀掉占用进程启动时 ClassNotFoundException: com.mysql.cj.jdbc.DriverMySQL 驱动没有打包进 lib检查 pom.xml 依赖执行 mvn clean package连接数据库超时URL、用户名、密码配置错误先在本机用数据库客户端连一次排除库本身不可达部署后访问报 404无 context 路径或路径名与 web.xml 不一致检查项目部署名和访问 URL连上库但中文出错数据库字符集或连接参数不对确认 utf8mb4URL 带 characterEncodingutf8端口占用是最常见的第一道坎。Windows 下用netstat -ano | findstr 8080找到进程号然后在任务管理器结束进程Linux 下用lsof -i:8080或fuser -k 8080/tcp。不过我更建议你从根源上避免冲突打开 Tomcat 的 conf/server.xml把 HTTP 端口改成 8081把 shutdown 端口从 -1 改成其他数值这样基本不会再撞车。有时候 Tomcat 能启动但应用一直报 404排查方向往往不是启动失败而是访问路径不对。用 IDEA 部署时项目的 context path 通常在 Run Configuration 的 Deployment 选项卡里配置默认是/javaweb-demo_war_exploded特别长。我习惯把它改成/javaweb-demo这样访问地址就是http://localhost:8080/javaweb-demo/login.jsp。改完之后要 clean 一下 target 目录再重新部署否则旧产物会干扰判断。4.2 运行期乱码、数据回显异常和更新失效的定位思路运行期最让人头疼的就是中文乱码。我在排查时有个固定的检查链先看数据库表字符集是不是 utf8mb4再看连接 URL 有没有带 characterEncodingutf8再看请求编码过滤器有没有生效最后看 JSP 页面pageEncoding和contentType是否为 UTF-8。这四环只要有一环断裂中文就可能乱码。尤其注意 idea 里新建 JSP 文件时默认的页面编码可能不是 UTF-8写中文前要确认编辑器右下角编码。这个问题之所以经典是因为它同时跨越了前端、中间层和数据库三个环节排查一次之后你会对编码链路有非常深的印象。回显异常和数据更新失效这类问题很多时候不在后端而在前端。编辑页面回显时如果输入框的 value 值里包含双引号拼接出来的 HTML 结构会被破坏看起来像数据回显失败。我处理时会把 JSP 里的 value 用 fn:escapeXml 转义或者在前端 JS 中做一层 encodeURIComponent。这个细节容易忽略但遇到一次就长记性。更新失效还有一个隐蔽原因表单里没有携带 id 字段。很多人在编辑页只关心展示的数据忘了加input typehidden nameid value${student.id}结果提交到 Servlet 时 id 始终为空程序误判成新增插入了一条重复数据或者因为 id 为空执行了错误的 SQL。这是我在实际操作中见过最多的低级错误写出来提醒你避坑。4.3 排查原则和效率工具遇到 Bug第一反应不要是到处打 System.out.println而是要有一套自己的排查顺序。先从控制台看有没有异常堆栈异常信息会直接告诉你是空指针、SQL 语法错误还是类找不到再确认请求到底有没有到达 Servlet最简单的方法是在 Servlet 的第一行加日志输出如果没打印问题在 Filter 或 URL 映射如果打印了再逐步缩小范围到 DAO 层和 SQL。我比较推荐用统一异常处理来兜底而不是让每个 Servlet 都自己写 try-catch。你可以写一个 BaseServlet把通用的异常捕获和日志记录放在父类里子类只写业务逻辑。顺手在项目的 web.xml 里配置 404 和 500 的错误页页面出问题时不至于让用户看到一堆英文堆栈。这些细节做不做的差别正是“作业型项目”和“工程型项目”的关键分水岭。调试手段方面除了控制台日志还可以借助浏览器的开发者工具。F12 打开网络面板看请求的 URL、状态码和请求头再配合响应体基本能定位是前端发起的问题还是后端返回的问题。很多“页面没反应”的情况其实是 JS 报错导致后续逻辑没执行这时看 Console 面板立刻就能发现问题。4.4 从这个小实例出发下一步还能怎么升级项目收官之后如果还想继续投入精力我建议按照下面的顺序做渐进式升级每一步的改动都不会推翻现有代码但能学到新东西。第一个方向是查询能力增强。现在列表只有按姓名模糊查询你可以加上性别筛选、班级筛选、多条件组合查询同时把分页组件从简单数字改成上一页/下一页的完整组件。这一步能让你熟练掌握动态 SQL 拼装的边界问题锻炼你在拼接条件时避免 SQL 注入的意识。第二个方向是增加验证码功能。可以用 Java 原生绘图 API 在服务端生成图片把验证码字符串放到 Session提交时比对。现在很多项目依然在用验证码自己手写一遍能直观感受图形验证码的生成和校验原理也顺便接触一下 BufferedImage、Graphics2D 这套绘图类。第三个方向是引入连接池和 DAO 层框架。把 DBUtil 里的 DriverManager 替换成 Druid 连接池再把 UserDao 里手写的 JDBC 代码替换成 MyBatis。这是从原生 JDBC 走向主流持久层框架最关键的一步。你会发现理解了手写 DAO 的繁琐之后再使用 MyBatis 的自动映射会更有感觉。第四个方向是接口化改造。把 JSP 页面逐步换成 AJAX 请求后端 Servlet 返回 JSON 数据前端通过 JavaScript 渲染。这算是提前接触前后端分离的开发模式为你后面学习 Spring Boot 和 Vue 打下一个衔接过渡。我个人在带新人做这个项目时最深的体会是代码本身不值钱值钱的是把“一个请求从哪里来、到哪里去”这条脉络想清楚。这个项目表面上只是一个学生管理的小后台但如果你能不看源码、独立把它从零写出来再讲清楚每段代码为什么存在JavaWeb 这条主线就算真正入门了。源码里我还额外加了注释方便你对照拿到之后建议不要直接跑而是对照本文的拆分思路抽掉一部分你自己补全那样收获会更大。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑