JSP+JavaBean+Servlet手搓登录注册与留言板教学闭环

发布时间:2026/10/23 20:36:58
JSP+JavaBean+Servlet手搓登录注册与留言板教学闭环 简介这是一份面向Java Web初学者的MVC架构实战项目资源基于JSPJavaBeanServlet技术栈完整实现用户登录注册、留言板管理及分页展示功能适用于高校Web开发课程实验、毕业设计基础模块或自学练手。资源包共78个文件包含14个JSP页面负责视图渲染、14个Java源码含Servlet控制器与JavaBean模型、14个编译后class文件、10个CSS样式文件、9个XML配置文件如web.xml、数据库连接配置以及1个SQL建库脚本整体压缩包仅1.86MB轻量易部署。已有1611人学习下载适配无密码本地MySQL环境数据库名webapp结构清晰体现经典三层分层思想Model层封装DAO操作View层专注展示逻辑Controller层协调请求流转。读者可直接导入NetBeans运行快速掌握传统JavaWeb开发流程、目录组织规范及前后端交互细节。1. 这不是复古怀旧而是 JavaWeb 教学闭环里最扎实的一块砖用 JSP JavaBean Servlet 手搓登录注册留言板不碰框架、不绕弯子专治“学了 Servlet 不知道串哪”“写了 JSP 不懂怎么传值”“JavaBean 写成 POJO 就停笔”的典型卡点很多刚学完 HTTP 协议、Servlet 生命周期、JSP 九大内置对象的开发者一到写完整功能就卡在“不知道数据从哪来、到哪去、中间谁该干啥”。这个资源不是炫技它是一套被某高校实验课反复验证过、某公司新员工岗前训练用过的最小可运行闭环用户填表单 → 提交到 Servlet → Servlet 调用 JavaBean 封装/校验/存库 → 转发回 JSP 展示结果或错误 → 留言列表实时刷新。它不依赖 Spring MVC 的自动绑定不靠 Thymeleaf 的模板语法糖所有请求流转、作用域传递、状态保持都暴露在代码里。适合两类人一是想真正吃透 JavaWeb 底层协作逻辑的初学者二是需要快速搭建教学 Demo、验证某个 Servlet 生命周期细节比如doPost中request.setAttribute()在转发后是否还有效的带教者。它不是生产级系统但它是你理解 Filter、Listener、Session 管理之前必须亲手拧紧的那几颗螺丝。2. 为什么是 JSP JavaBean Servlet 这个组合不是 Struts、不是 Spring Boot更不是纯前端调 API2.1 选型逻辑三件套各自不可替代的“职责锚点”在现代 JavaWeb 开发中JSP、JavaBean、Servlet 各自承担着清晰且不可被上层框架完全抹平的职责边界。这不是历史包袱而是分层设计的具象化体现Servlet 是协议守门人它直接对接 HTTP 请求/响应是唯一能原生读取request.getParameter()、设置response.setStatus()、控制重定向sendRedirect与请求转发RequestDispatcher.forward的组件。任何框架最终都要把它包装成RequestMapping或PostMapping但底层仍是 Servlet 容器调用service()方法。本项目中LoginServlet和RegisterServlet不做业务逻辑只做三件事收参、调 Bean、决定跳转——这是对“Servlet 职责单一性”的严格践行。JavaBean 是数据契约体它不是简单的 POJO。本项目中的User.java和Message.java必须满足 JavaBean 规范私有属性、公共 getter/setter、无参构造。为什么因为 JSP 的jsp:useBean标签和jsp:setProperty property*依赖反射按名称匹配属性若缺少 setter 或命名不规范如setUserName写成setusernameJSP 页面会静默失败连报错都不抛。它强制你写出符合 JavaBeans Introspection 规则的类这是理解后续框架中“属性绑定”机制的起点。JSP 是视图编排器不是模板引擎它不负责计算、不处理分支逻辑% if (...) { %是反模式本项目禁用脚本片段只做两件事通过 EL 表达式${user.name}渲染 JavaBean 属性用 JSTLc:ifc:forEach做纯粹的展示控制。它的本质是 Servlet 容器编译后的 Java 类.java文件在work/Catalina/...下可查这让你直面“页面即 Servlet”的事实——没有魔法只有编译时生成的out.print(...)。提示本项目刻意避开web.xml全量配置采用WebServlet注解但保留web.xml中的jsp-config配置段如el-ignoredfalse这是为了显式声明 EL 表达式启用状态避免新手因默认配置差异导致${}不解析。2.2 目录结构即架构5 个核心文件讲清数据流向项目采用标准 Maven Webapp 结构关键文件精简到 5 个每个都承担明确角色文件路径类型核心职责关键技术点src/main/java/com/example/LoginServlet.javaServlet接收 POST 登录请求调用UserService.checkLogin()根据返回结果request.getRequestDispatcher()转发或response.sendRedirect()重定向request.getSession().setAttribute(currentUser, user)实现登录态保持src/main/java/com/example/User.javaJavaBean封装用户名、密码、邮箱等字段提供validate()方法校验密码长度、邮箱格式validate()返回MapString, String存储各字段错误信息供 JSP 显示src/main/webapp/login.jspJSP展示登录表单用form actionlogin methodpost提交用${errorMap.password}显示校验错误c:if test${not empty errorMap}判断是否有错误并高亮输入框src/main/webapp/messageBoard.jspJSP显示留言列表用c:forEach items${messageList} varmsg遍历每条留言含作者、内容、时间时间戳用fmt:formatDate value${msg.time} patternyyyy-MM-dd HH:mm:ss格式化src/main/resources/db.properties配置文件存放数据库连接参数driver、url、username、password由DBUtil工具类加载使用ClassLoader.getSystemResourceAsStream()加载确保打包后仍可读这个结构拒绝“大而全”删掉所有非必要文件如index.html、error.jsp迫使你关注主链路表单提交 → Servlet 处理 → Bean 校验 → 数据库操作 → 视图渲染。每一步的输入输出都肉眼可见。2.3 数据库设计3 张表撑起全部功能字段定义直指业务痛点本项目使用 HSQLDB内存数据库启动即建库无需安装表结构极简但覆盖核心场景-- 用户表密码明文存储仅用于教学演示实际必须加盐哈希 CREATE TABLE users ( id INTEGER IDENTITY PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, email VARCHAR(100), created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 留言表关联用户ID实现“谁发的留言” CREATE TABLE messages ( id INTEGER IDENTITY PRIMARY KEY, user_id INTEGER NOT NULL, content VARCHAR(500) NOT NULL, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); -- 会话表模拟简单登录态管理实际应使用 HttpSession CREATE TABLE sessions ( session_id VARCHAR(100) PRIMARY KEY, user_id INTEGER NOT NULL, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_time TIMESTAMP );关键设计意图users.username设为UNIQUE强制注册时校验用户名重复UserService.register()中INSERT INTO users ...失败时捕获SQLException并返回具体错误。messages.user_id外键约束确保每条留言必属某用户MessageService.addMessage()先查users表确认用户存在再插入messages体现数据一致性校验。sessions表虽未在代码中直接使用登录态走HttpSession但其结构为后续扩展“记住我”、“多端登录踢出”埋下伏笔——只需在LoginServlet中增加INSERT INTO sessions操作即可。3. 从零跑通5 步命令行实操不依赖 IDE 图形界面3.1 环境准备JDK 8 Tomcat 9 是黄金组合版本错配是第一大坑本项目基于 Servlet 3.1 规范Tomcat 8.5 支持但刻意选择JDK 8 Tomcat 9.0.x组合原因有三JDK 8 的javac编译器对泛型擦除、Lambda 表达式支持稳定避免 JDK 11 的模块化--add-modules干扰Tomcat 9 默认启用EL 3.0支持${user?.name}安全导航操作符比 Tomcat 7 的 EL 2.2 更健壮Maven 插件tomcat7-maven-plugin对 Tomcat 9 兼容性差必须改用tomcat9-maven-plugin。验证环境命令Linux/macOS# 检查 JDK 版本必须 1.8.x java -version javac -version # 检查 Maven需 3.6.3 mvn -v # 检查 Tomcat解压后 bin/catalina.sh 可执行 $TOMCAT_HOME/bin/version.sh注意若java -version输出11.0.x需切换 JDKexport JAVA_HOME$(/usr/libexec/java_home -v 1.8)macOS或export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64Ubuntu。版本不匹配会导致javax.servlet.http.HttpServlet类找不到、WebServlet注解失效等静默失败。3.2 项目构建Maven 打包时的三个关键插件配置pom.xml中必须包含以下三个插件缺一不可build plugins !-- 1. 编译插件强制使用 JDK 8避免 Maven 默认用高版本 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.8.1/version configuration source1.8/source target1.8/target /configuration /plugin !-- 2. 打包插件生成 WAR 包排除测试类和源码 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-war-plugin/artifactId version3.2.3/version configuration failOnMissingWebXmlfalse/failOnMissingWebXml warSourceExcludes**/*.java,**/test/**/warSourceExcludes /configuration /plugin !-- 3. Tomcat 插件内嵌 Tomcat一键启动开发用 -- plugin groupIdorg.apache.tomcat.maven/groupId artifactIdtomcat9-maven-plugin/artifactId version1.0/version configuration port8080/port path/demo/path uriEncodingUTF-8/uriEncoding /configuration /plugin /plugins /build关键参数说明failOnMissingWebXmlfalse/failOnMissingWebXml允许无web.xml启用注解驱动WebServletwarSourceExcludes确保打包时不把.java源文件打进 WAR避免线上暴露源码uriEncodingUTF-8/uriEncoding解决中文表单提交乱码GET 请求需额外在server.xml中加URIEncodingUTF-8本项目用 POST故此处足够。3.3 启动与验证一条命令跑起来三步验证链路通进入项目根目录执行mvn clean compile tomcat9:run等待日志出现INFO: Server startup in [xxx] milliseconds即启动成功。此时访问http://localhost:8080/demo/login.jsp。三步验证法必须手动执行不能跳过表单提交验证在login.jsp输入admin/123456点击登录。观察浏览器地址栏是否变为http://localhost:8080/demo/messageBoard.jsp重定向或保持login.jsp但显示“登录成功”转发。这验证了LoginServlet的跳转逻辑。EL 表达式验证在messageBoard.jsp中检查h2欢迎${currentUser.username}/h2是否正确显示用户名。若显示${currentUser.username}字面量说明 EL 被禁用需检查web.xml中jsp-configel-ignoredfalse/el-ignored/jsp-config。数据库验证在register.jsp注册新用户后直接访问http://localhost:8080/demo/hsqlHSQLDB 内置控制台执行SELECT * FROM users;查看新记录是否插入。这确认UserService.register()的 JDBC 操作生效。提示若mvn tomcat9:run报错Address already in use: bind说明 8080 端口被占用。修改pom.xml中port为8081或执行lsof -i :8080macOS/netstat -ano | findstr :8080Windows查杀进程。4. 避坑指南5 个血泪经验总结专治“页面空白”“404”“参数为空”“登录态丢失”4.1 现象访问login.jsp显示 404但mvn tomcat9:run日志无报错原因login.jsp文件未放在src/main/webapp/目录下而是误放至src/main/resources/或src/main/java/。Maven WAR 插件只打包webapp目录下的资源其他路径文件不会出现在最终 WAR 包的根路径。解决严格遵循标准目录结构login.jsp必须位于src/main/webapp/login.jsp。检查target/demo.war解压后是否存在login.jsp。4.2 现象表单提交后LoginServlet.doPost()中request.getParameter(username)返回null原因HTML 表单的name属性与 Servlet 获取参数的 key 不一致。常见错误是表单写input typetext idusername只有id没有name或nameuserName驼峰而 Servlet 写request.getParameter(username)下划线。解决检查login.jsp中form内所有input标签确保name属性值与 Servlet 中getParameter()的字符串完全一致区分大小写例如input typetext nameusername。4.3 现象登录成功后跳转到messageBoard.jsp但${currentUser.username}显示为空或报错原因LoginServlet中使用了response.sendRedirect(messageBoard.jsp)重定向导致request.setAttribute(currentUser, user)失效。重定向会发起新请求原request对象生命周期结束。解决登录成功时必须用RequestDispatcher.forward()转发而非sendRedirect()。正确代码// ✅ 正确转发request 作用域内属性可见 request.setAttribute(currentUser, user); request.getRequestDispatcher(/messageBoard.jsp).forward(request, response); // ❌ 错误重定向request 属性丢失 response.sendRedirect(messageBoard.jsp);4.4 现象注册时输入中文用户名数据库存入乱码如李四但login.jsp显示正常原因HSQLDB 连接 URL 未指定字符集。db.properties中jdbc.urljdbc:hsqldb:mem:demo缺少;hsqldb.default_table_typecached;charsetUTF-8参数。解决修改db.propertiesjdbc.urljdbc:hsqldb:mem:demo;hsqldb.default_table_typecached;charsetUTF-8 jdbc.driverorg.hsqldb.jdbc.JDBCDriver同时在DBUtil.getConnection()中DriverManager.getConnection(url, props)前添加System.setProperty(file.encoding, UTF-8);。4.5 现象关闭浏览器再打开messageBoard.jsp仍显示“欢迎admin”登录态未清除原因HttpSession默认超时时间为 30 分钟且未在退出时主动session.invalidate()。关闭浏览器只是销毁了 Cookie服务端 Session 仍在。解决在LogoutServlet需自行添加中执行HttpSession session request.getSession(false); if (session ! null) { session.invalidate(); // 彻底销毁 Session } response.sendRedirect(login.jsp);并在web.xml中配置全局超时可选session-config session-timeout1/session-timeout !-- 单位分钟设为 1 便于测试 -- /session-config5. 进阶技巧用一个 Filter 实现全站登录拦截三行代码让未登录用户无法访问留言板5.1 为什么不用web.xml配置 Filter手写WebFilter更直观本项目不使用web.xml声明 Filter而是用注解方式让拦截逻辑与 Servlet 一样“看得见、摸得着”WebFilter(urlPatterns {/messageBoard.jsp, /addMessage}) public class LoginCheckFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; // 1. 获取当前 Session不创建新 Session HttpSession session req.getSession(false); // 2. 检查 Session 中是否存在 currentUser if (session null || session.getAttribute(currentUser) null) { // 3. 未登录重定向到登录页并保存原请求路径便于登录后跳回 String originalUrl req.getRequestURL().toString(); resp.sendRedirect(req.getContextPath() /login.jsp?redirect URLEncoder.encode(originalUrl, UTF-8)); return; // ✅ 关键return 阻止后续 chain.doFilter() } // 已登录放行 chain.doFilter(request, response); } }三行核心逻辑解析req.getSession(false)false参数表示“不创建新 Session”避免未登录用户访问/messageBoard.jsp时意外生成空 Sessionsession.getAttribute(currentUser) null检查登录态currentUser是LoginServlet中session.setAttribute(currentUser, user)设置的 keyresp.sendRedirect(...)重定向到登录页并用redirect参数携带原始 URL登录成功后可跳回需在LoginServlet中读取request.getParameter(redirect)。5.2 登录成功后自动跳回原页面LoginServlet的增强写法在LoginServlet.doPost()验证成功后加入跳转逻辑String redirectUrl request.getParameter(redirect); if (redirectUrl ! null !redirectUrl.isEmpty()) { // 清洗 redirectUrl防止开放重定向漏洞只允许跳转到本应用内 if (redirectUrl.startsWith(request.getContextPath())) { response.sendRedirect(redirectUrl); return; } } // 默认跳转到留言板 response.sendRedirect(request.getContextPath() /messageBoard.jsp);提示redirectUrl.startsWith(request.getContextPath())是基础防护生产环境需用白名单校验如SetString allowedPaths Set.of(/messageBoard.jsp, /profile.jsp);。5.3 留言防刷用 Session 计数器限制每小时最多发 5 条在AddMessageServlet中加入频率控制无需 Redis纯内存实现// 获取用户ID从Session中 User user (User) request.getSession().getAttribute(currentUser); if (user null) { response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } // 构造计数器keyuser:id:hour String counterKey user: user.getId() : LocalDateTime.now().getHour(); // 从Session中获取计数器Integer类型 HttpSession session request.getSession(); Integer count (Integer) session.getAttribute(counterKey); if (count null) { count 0; } if (count 5) { request.setAttribute(error, 您已达到今日留言上限请稍后再试); request.getRequestDispatcher(/messageBoard.jsp).forward(request, response); return; } // 更新计数器 session.setAttribute(counterKey, count 1); // 执行正常留言逻辑...关键点计数器 key 包含小时数LocalDateTime.now().getHour()保证每小时重置session.setAttribute()是线程安全的同一用户 Session 内操作无需额外同步若需跨节点集群部署此方案失效需升级为 Redis 计数器——这正是你理解“Session 共享”痛点的入口。从那以后我每次带新人做 JavaWeb 实战都会先让他们删掉spring-boot-starter-web用这套 JSPJavaBeanServlet 手搓一遍登录和留言板。不是守旧是逼他们看清request怎么从 TCP 包里解析出来、session怎么靠 Cookie 维持、forward和redirect的字节码差异在哪。当messageBoard.jsp上终于刷出自己刚提交的留言那种“原来如此”的顿悟感是任何框架文档都给不了的。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询