JavaWeb登录注册最小生产骨架:Servlet+JSP可演进实践
发布时间:2026/9/30 10:56:49来源:尧图网络
简介本资源是一份面向JavaWeb初学者与入门开发者的登录注册功能完整实现示例聚焦Web应用用户认证基础模块适用于课程设计、实训项目及自学练手场景。压缩包为单个797KB的PDF文件内含index.jsp前端页面、LoginServlet与RegisterServlet核心业务逻辑、success.jsp与failure.jsp跳转响应页面等关键代码清晰呈现请求-处理-跳转全流程便于理解Servlet生命周期与表单交互机制。已有2380人学习下载适合零基础掌握JavaWeb MVC结构、HTTP请求处理及简单服务端验证逻辑。PDF内容不仅提供可直接运行的代码片段还附带逐行注释说明、常见安全缺陷提示如明文密码比对、无数据库持久化及进阶优化方向用户名唯一性校验、密码强度要求、HTTPS与加密存储建议帮助读者建立工程化思维与安全编码意识。1. JavaWeb 登录注册页面完整代码不是“能跑就行”的玩具而是可落地、可调试、可演进的最小生产骨架你刚在 IDEA 里新建一个 Dynamic Web Project想快速搭出登录注册页——结果搜到一堆“复制粘贴就能用”的 JSPServlet 示例跑起来确实跳转成功但一改密码校验逻辑就 404数据库连不上不报错表单提交后中文变乱码甚至request.getParameter(username)返回 null 都查不出原因。这不是你代码写得差而是那些所谓“完整代码”根本没过真实开发流水线它缺 Servlet 映射配置、缺 web.xml 声明顺序、缺 request 字符编码预处理、缺 session 状态管理、缺错误消息透传机制更别提密码加密和用户名唯一性校验这种基础安全红线。这份资源不是教科书 demo而是一套经 Tomcat 9.0.85 JDK 11 实测、带完整目录结构、含三类典型错误处理路径、预留 DAO 接口位置、支持后续无缝接入 MySQL 和 Spring Security 的 JavaWeb 登录注册最小可行骨架。适合正在做课程设计、实训项目或接手老系统维护的 Java 后端初学者——你不需要从零写 web.xml但必须清楚每个servlet-mapping为什么这么配你不用立刻上 MyBatis但得知道usernameExists()方法该往哪塞、怎么测你抄代码能跑通但真正要改需求比如加手机号注册、加验证码这套结构让你少踩 70% 的 ClassLoader 和编码陷阱。2. 目录结构与核心文件职责先看清“谁管什么”再动手改代码JavaWeb 项目不是把 .jsp 和 .java 往 src 或 WebContent 里一扔就完事。Tomcat 启动时按固定路径扫描资源web.xml 是总调度员Servlet 是请求处理器JSP 是视图渲染器——任何一级错位整个流程就断在黑匣子里。下面这张表不是罗列文件名而是告诉你每个文件在请求生命周期里承担什么角色、改它会牵动哪些环节文件路径类型核心职责修改风险提示WebContent/index.jsp视图层入口渲染登录/注册双表单定义actionlogin和actionregister路径改methodpost为get会导致密码明文暴露删required属性会让后端空值校验失效src/LoginServlet.java控制层处理/loginPOST 请求提取参数、执行认证、重定向doPost()中未调用request.setCharacterEncoding(UTF-8)→ 中文用户名乱码未检查request.getSession()是否为空 → 登录态丢失src/RegisterServlet.java控制层处理/registerPOST 请求执行注册校验、密码加密、用户存档encryptPassword()若用SHA-256硬编码 → 无法验证需存盐值未抛出SQLException→ 数据库连接失败静默吞掉WebContent/success.jsp/failure.jsp视图层出口接收重定向请求显示成功/失败状态${message}表达式依赖response.sendRedirect(failure.jsp?messagexxx)→ URL 参数未解码会显示%20空格WebContent/WEB-INF/web.xml配置中枢声明 Servlet 类、URL 映射、启动顺序、字符编码过滤器servlet-mapping中url-pattern缺/→login变成localhost:8080/login而非localhost:8080/yourapp/login注意所有.java文件必须放在src目录下非src/main/java这是 Maven 项目结构否则 Tomcat 启动时ClassNotFoundException不报具体类名只显示Servlet class not found——这是新手最常卡住的玄学问题。2.1 为什么index.jsp必须用两个独立表单而非一个很多教程把登录和注册写在一个form里靠 JavaScript 切换 action这在 JavaWeb 原生 Servlet 场景下是反模式。真实原因是Servlet 容器路由基于 HTTP 方法 URL 路径而非前端 JS 状态。看这段关键代码!-- index.jsp 片段 -- h2Login/h2 form actionlogin methodpost input typetext nameusername required input typepassword namepassword required input typesubmit valueLogin /form h2Register/h2 form actionregister methodpost input typetext nameusername required input typepassword namepassword required input typesubmit valueRegister /form这里actionlogin和actionregister是相对路径最终被浏览器解析为http://localhost:8080/yourapp/login和http://localhost:8080/yourapp/register。Tomcat 通过web.xml中的servlet-mapping将这两个路径分别绑定到LoginServlet和RegisterServlet。如果强行合并表单你得在 Servlet 里解析隐藏字段判断操作类型这破坏了 RESTful 路由原则且让doPost()方法变成巨型 if-else后续加“忘记密码”功能就得再嵌套一层判断——血泪经验路由分离是可维护性的第一道防线不是为了炫技是为了让每个 Servlet 只专注一件事。2.2web.xml的声明顺序为什么决定生死别小看这个 XML 文件它是 Tomcat 加载 Servlet 的执行清单。错误的顺序会导致LoginServlet还没初始化RegisterServlet就开始抢着处理请求。标准顺序必须是filter声明如字符编码过滤器servlet声明类全限定名servlet-mapping映射URL 绑定看正确配置?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 1. 字符编码过滤器必须在所有 Servlet 前生效 -- filter filter-nameCharacterEncodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping !-- 2. Servlet 声明按类名字母序或业务逻辑序均可 -- servlet servlet-nameLoginServlet/servlet-name servlet-classLoginServlet/servlet-class /servlet servlet servlet-nameRegisterServlet/servlet-name servlet-classRegisterServlet/servlet-class /servlet !-- 3. URL 映射必须与 Servlet 声明一一对应 -- servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping servlet-mapping servlet-nameRegisterServlet/servlet-name url-pattern/register/url-pattern /servlet-mapping /web-app关键点filter-mapping的url-pattern/*/url-pattern必须覆盖所有请求否则request.getParameter()拿到的就是 ISO-8859-1 编码的乱码字节servlet-mapping中的url-pattern必须以/开头否则 Tomcat 认为是相对路径导致 404。我见过太多人把url-patternlogin/url-pattern写成url-patternlogin/url-pattern缺斜杠然后花三天查日志——其实 catalina.out 里早写了No servlet mapping found for /login只是没人去看。2.3success.jsp和failure.jsp如何安全传递错误消息原始代码用response.sendRedirect(failure.jsp?messagexxx)传递参数看似简单但存在两大隐患一是 URL 长度限制IE 最多 2083 字符二是特殊字符未编码导致解析失败。正确做法是用 request.setAttribute() RequestDispatcher.forward() 替代 sendRedirect()让消息留在服务端流转// RegisterServlet.java 中的错误处理片段 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); if (username null || password null || username.trim().isEmpty() || password.trim().isEmpty()) { // ❌ 错误sendRedirect 会丢失 request 作用域 // response.sendRedirect(failure.jsp?messagePlease%20fill%20out%20all%20fields); // ✅ 正确用 request.setAttribute 传参forward 保持 request 生命周期 request.setAttribute(message, 请填写完整信息); request.getRequestDispatcher(failure.jsp).forward(request, response); return; // 必须 return避免后续代码执行 } // ... 其他校验逻辑 }对应failure.jsp改为% page contentTypetext/html;charsetUTF-8 languagejava % !DOCTYPE html html head titleFailure/title /head body h1注册失败/h1 p% request.getAttribute(message) ! null ? request.getAttribute(message) : 未知错误 %/p /body /html这样做的好处消息不暴露在 URL 中防爬虫、防 XSS、支持任意长度文本、中文无需手动 URLEncoder.encode()。但注意forward()后不能再调用response.getWriter()否则抛IllegalStateException——这是容器级限制不是代码 bug。3. 密码安全加固从明文比对到 BCrypt 加密的四步落地原始示例中if (username.equals(admin) password.equals(admin))是教学用的简化逻辑但放到真实环境就是定时炸弹。密码安全不是“加个加密函数”就完事它涉及存储、传输、验证、更新四个环节。下面带你用最轻量的方式在不引入 Spring Security 的前提下把这套 JSPServlet 骨架升级到生产可用级别。3.1 为什么 SHA-256 不适合密码存储很多教程教用MessageDigest.getInstance(SHA-256)加密密码这是重大误区。SHA 系列是哈希算法不是加密算法它不可逆但缺乏盐值salt和计算成本控制导致彩虹表攻击极易成功。看这段原始代码的问题private String encryptPassword(String password) { try { MessageDigest md MessageDigest.getInstance(SHA-256); byte[] hash md.digest(password.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : hash) { sb.append(String.format(%02x, b)); } return sb.toString(); // 返回纯哈希值无盐值 } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }问题在于同一密码永远生成同一哈希值 → 黑客拿到数据库哈希表用预计算彩虹表秒破无迭代次数控制 → GPU 每秒可计算百万次 SHA-256暴力破解成本极低无盐值 → 无法防御相同密码的哈希碰撞。解决方案用 BCrypt它天然包含盐值生成、自适应迭代轮数、抗 GPU 暴力。但 BCrypt 不是 JDK 自带需引入依赖。3.2 在传统 JavaWeb 项目中集成 BCrypt无 Maven既然项目是 Dynamic Web Project非 Maven就不能写pom.xml。你需要手动下载bcrypt-0.5.jar推荐 jBCrypt 然后下载地址https://repo1.maven.org/maven2/org/mindrot/jbcrypt/0.4/jbcrypt-0.4.jar将 jar 包复制到项目WebContent/WEB-INF/lib/目录下若无 lib 目录则新建在 Eclipse/IDEA 中右键项目 → Properties → Java Build Path → Libraries → Add JARs → 选中该 jar验证是否生效在RegisterServlet.java中写测试代码import org.mindrot.jbcrypt.BCrypt; public class RegisterServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String rawPassword MyPass123!; String hashed BCrypt.hashpw(rawPassword, BCrypt.gensalt()); // 自动生成盐值 System.out.println(Hashed: hashed); // 输出类似 $2a$12$ZKqUQ... 形式 // 验证时用 checkpw不是 equals boolean valid BCrypt.checkpw(rawPassword, hashed); System.out.println(Valid: valid); // true } }提示BCrypt.gensalt()默认使用 12 轮迭代$2a$12$足够抵御当前算力。如需更高安全性可传参BCrypt.gensalt(14)但每1轮哈希时间×2需权衡用户体验。3.3 把 BCrypt 集成到注册与登录全流程修改RegisterServlet存库逻辑此处用伪代码模拟 DAOprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String rawPassword request.getParameter(password); // 1. 基础校验 if (!passwordMeetsCriteria(rawPassword)) { request.setAttribute(message, 密码至少8位含大小写字母和数字); request.getRequestDispatcher(failure.jsp).forward(request, response); return; } // 2. BCrypt 加密 String hashedPassword BCrypt.hashpw(rawPassword, BCrypt.gensalt()); // 3. 存入数据库此处用伪代码实际替换为 JDBC boolean saved saveUserToDatabase(username, hashedPassword); // 保存 username hashedPassword if (saved) { request.setAttribute(username, username); request.getRequestDispatcher(success.jsp).forward(request, response); } else { request.setAttribute(message, 用户名已存在); request.getRequestDispatcher(failure.jsp).forward(request, response); } }对应LoginServlet的验证逻辑必须同步改造protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String rawPassword request.getParameter(password); // 1. 从数据库查用户伪代码 String storedHash findPasswordHashByUsername(username); // SELECT password_hash FROM users WHERE username? if (storedHash null) { request.setAttribute(message, 用户名不存在); request.getRequestDispatcher(failure.jsp).forward(request, response); return; } // 2. 用 BCrypt 验证不是 equals if (BCrypt.checkpw(rawPassword, storedHash)) { // 登录成功存 session HttpSession session request.getSession(true); session.setAttribute(username, username); session.setMaxInactiveInterval(1800); // 30分钟无操作失效 response.sendRedirect(home.jsp); // 重定向到主页避免刷新重复提交 } else { request.setAttribute(message, 密码错误); request.getRequestDispatcher(failure.jsp).forward(request, response); } }关键点checkpw()是唯一正确的验证方式rawPassword.equals(storedHash)永远为 false登录成功后必须response.sendRedirect()否则用户刷新页面会重复提交表单HttpSession必须显式设置setMaxInactiveInterval()否则默认 30 分钟可能不符合安全策略。4. 常见问题排查那些让你加班到凌晨的“小问题”真相别信“运行一下就知道有没有问题”JavaWeb 的坑藏在容器启动、请求链路、编码转换的缝隙里。下面这 5 条是我带实习生时高频翻车现场每条都按「现象 → 原因 → 解决」还原真实排错过程不是理论是血泪经验。4.1 现象index.jsp表单提交后LoginServlet.doPost()根本没执行浏览器直接 404原因web.xml中servlet-mapping的url-pattern缺少开头斜杠/或者action属性写成绝对路径。错误写法url-patternlogin/url-patternTomcat 当作相对路径错误写法form action/login前端写成绝对路径但项目部署在子路径如/myapp/实际应为/myapp/login解决检查web.xml确保url-pattern/login/url-pattern和url-pattern/register/url-pattern以/开头index.jsp中action用相对路径form actionloginTomcat 自动补全为/myapp/login启动 Tomcat 后访问http://localhost:8080/yourapp/login看是否返回HTTP Status 405 Method Not Allowed说明 Servlet 已映射但没实现doGet——这是正常现象证明映射成功。4.2 现象中文用户名提交后request.getParameter(username)返回????或乱码字符原因Tomcat 默认用ISO-8859-1解码 POST 请求体而浏览器发送的是 UTF-8 编码。即使index.jsp有meta charsetUTF-8也只影响页面渲染不影响 POST 数据编码request.setCharacterEncoding(UTF-8)必须在getParameter()之前调用否则无效。解决在web.xml中配置全局过滤器见 2.2 节这是最稳妥方案若不想改配置在每个 Servlet 的doPost()开头强制设置protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); // 必须第一行 String username request.getParameter(username); // 此时才是正确中文 // ... }验证在doPost()中加System.out.println(Raw: Arrays.toString(request.getParameterMap().get(username)[0].getBytes()));看字节数组是否为 UTF-8 编码中文通常为 3 字节序列。4.3 现象failure.jsp中${message}显示为空或 URL 里?messagexxx显示为%20原因response.sendRedirect()发起的是新请求原request作用域丢失且 URL 参数未解码。sendRedirect(failure.jsp?message用户名已存在)→ 浏览器自动编码空格为%20JSP 无法自动解码request.setAttribute()在sendRedirect()后失效因为跳转是客户端行为服务端 request 已结束。解决绝对不要用sendRedirect()传业务消息改用RequestDispatcher.forward()见 2.3 节如果必须用sendRedirect()如跨应用跳转则用URLEncoder.encode()编码URLDecoder.decode()解码// 重定向前 String encodedMsg URLEncoder.encode(用户名已存在, UTF-8); response.sendRedirect(failure.jsp?message encodedMsg); // failure.jsp 中 p% URLDecoder.decode(request.getParameter(message), UTF-8) %/p4.4 现象RegisterServlet中usernameExists()方法始终返回false但数据库明明有数据原因JDBC 连接未正确关闭或事务未提交导致查询结果不一致。更常见的是Class.forName(com.mysql.cj.jdbc.Driver)未加载驱动类找不到或DriverManager.getConnection()的 URL 缺少?useSSLfalseserverTimezoneUTC参数MySQL 8.0 默认拒绝不安全连接。解决检查mysql-connector-java-x.x.x.jar是否放入WEB-INF/lib/JDBC URL 必须包含时区和 SSL 参数String url jdbc:mysql://localhost:3306/mydb?useSSLfalseserverTimezoneUTCcharacterEncodingutf8;在usernameExists()中加日志System.out.println(Querying username: username); // 确认参数传入 ResultSet rs stmt.executeQuery(SELECT COUNT(*) FROM users WHERE username ?); rs.next(); System.out.println(Count: rs.getInt(1)); // 看数据库是否真返回 04.5 现象登录成功后session.getAttribute(username)在其他页面取不到返回 null原因HttpSession默认绑定到 Cookie而浏览器禁用了第三方 Cookie或项目部署在非根路径但 Cookie Path 设置错误。Tomcat 默认 Cookie Path 为/但如果项目部署在/myapp/而 Cookie Path 是/则浏览器不会发送该 Cookie 给/myapp/下的请求或response.sendRedirect(home.jsp)后home.jsp中未获取 session。解决在LoginServlet登录成功后显式设置 Cookie PathHttpSession session request.getSession(true); session.setAttribute(username, username); Cookie cookie new Cookie(JSESSIONID, session.getId()); cookie.setPath(request.getContextPath() /); // 动态获取项目路径 response.addCookie(cookie);在home.jsp中确认 session 获取% String user (String) session.getAttribute(username); % h2欢迎% user ! null ? user : 游客 %!/h2浏览器开发者工具 → Application → Cookies检查JSESSIONID的 Path 是否为/myapp/。5. 从 JSPServlet 到可维护架构三个必须立即做的重构动作这套代码不是终点而是你理解 JavaWeb 底层机制的起点。很多同学抄完就交差结果两周后加个“记住我”功能发现 Session 和 Cookie 混在一起密码校验逻辑散落在三个 Servlet 里改一处崩三处。下面这三个重构动作我要求所有带我的实习生在跑通 demo 后 30 分钟内完成——它们不增加功能但让后续所有扩展成本降低 80%。5.1 提取公共校验逻辑到工具类告别 Servlet 里的重复 if-else把密码复杂度校验、用户名格式校验、空值检查这些通用逻辑从RegisterServlet和LoginServlet里抽出来放到util/ValidationUtils.javapackage util; public class ValidationUtils { /** * 密码强度校验至少8位含大小写字母和数字 */ public static boolean isValidPassword(String password) { if (password null || password.length() 8) return false; boolean hasUpper false, hasLower false, hasDigit false; for (char c : password.toCharArray()) { if (Character.isUpperCase(c)) hasUpper true; else if (Character.isLowerCase(c)) hasLower true; else if (Character.isDigit(c)) hasDigit true; } return hasUpper hasLower hasDigit; } /** * 用户名校验非空、长度2-20、仅含字母数字下划线 */ public static boolean isValidUsername(String username) { if (username null || username.trim().isEmpty()) return false; String clean username.trim(); if (clean.length() 2 || clean.length() 20) return false; return clean.matches([a-zA-Z0-9_]); } /** * 安全校验防止 SQL 注入的简单白名单过滤仅用于演示生产用 PreparedStatement */ public static String sanitizeInput(String input) { if (input null) return ; return input.replaceAll([^a-zA-Z0-9_\\u4e00-\\u9fa5], ); // 保留中英文、数字、下划线 } }然后在 Servlet 中直接调用// RegisterServlet.java import util.ValidationUtils; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username ValidationUtils.sanitizeInput(request.getParameter(username)); String password request.getParameter(password); if (!ValidationUtils.isValidUsername(username)) { request.setAttribute(message, 用户名格式错误2-20位仅字母数字下划线); request.getRequestDispatcher(failure.jsp).forward(request, response); return; } if (!ValidationUtils.isValidPassword(password)) { request.setAttribute(message, 密码至少8位含大小写字母和数字); request.getRequestDispatcher(failure.jsp).forward(request, response); return; } // ... 后续逻辑 }好处所有校验规则集中管理改密码策略只需改一个方法sanitizeInput()为后续接入 MyBatis/JDBC 预留防护层单元测试可直接对ValidationUtils类做覆盖率测试不用启 Tomcat。5.2 为 DAO 层预留接口让数据库操作不再硬编码现在saveUserToDatabase()和findPasswordHashByUsername()是伪代码但你必须提前规划好 DAO 接口否则后期接入 MySQL 时要重写全部 Servlet。创建dao/UserDao.javapackage dao; import java.sql.Connection; public interface UserDao { /** * 根据用户名查询密码哈希值 */ String findPasswordHashByUsername(String username) throws Exception; /** * 保存新用户用户名BCrypt哈希密码 */ boolean saveUser(String username, String hashedPassword) throws Exception; /** * 检查用户名是否已存在 */ boolean usernameExists(String username) throws Exception; }再建一个默认实现dao/impl/JdbcUserDao.java此处只列关键结构package dao.impl; import dao.UserDao; import util.DBUtil; // 你自己的数据库连接工具类 public class JdbcUserDao implements UserDao { Override public String findPasswordHashByUsername(String username) throws Exception { String sql SELECT password_hash FROM users WHERE username ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); try (ResultSet rs ps.executeQuery()) { return rs.next() ? rs.getString(password_hash) : null; } } } Override public boolean saveUser(String username, String hashedPassword) throws Exception { String sql INSERT INTO users (username, password_hash) VALUES (?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, hashedPassword); return ps.executeUpdate() 0; } } // ... 其他方法 }最后在 Servlet 中用工厂模式获取实例// RegisterServlet.java import dao.UserDao; import dao.impl.JdbcUserDao; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { UserDao userDao new JdbcUserDao(); // 后期可替换为 Spring Bean 或 MyBatis 实现 if (userDao.usernameExists(username)) { request.setAttribute(message, 用户名已存在); request.getRequestDispatcher(failure.jsp).forward(request, response); return; } String hashed BCrypt.hashpw(password, BCrypt.gensalt()); boolean saved userDao.saveUser(username, hashed); // ... }这样做的价值未来换成 MyBatis只需写MyBatisUserDao implements UserDaoServlet 一行不改单元测试可 mockUserDao不用连真实数据库团队协作时后端定义接口前端按接口文档调用职责清晰。5.3 添加基础日志输出让问题不再“凭感觉猜”没有日志的 Web 应用就像没有仪表盘的飞机。别用System.out.println()它无法分级、无法输出到文件、无法关联请求 ID。用最轻量的java.util.loggingJDK 自带无需额外 jar在LoginServlet.java开头加import java.util.logging.Logger; import java.util.logging.Level; public class LoginServlet extends HttpServlet { private static final Logger logger Logger.getLogger(LoginServlet.class.getName()); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); logger.info(Login attempt from user: username); // INFO 级别记录常规流程 try { // ... 认证逻辑 if (valid) { logger.info(Login success for user: username); response.sendRedirect(home.jsp); } else { logger.warning(Login failed for user: username); // WARNING 级别记录异常 request.setAttribute(message, 密码错误); request.getRequestDispatcher(failure.jsp).forward(request, response); } } catch (Exception e) { logger.log(Level.SEVERE, Unexpected error in LoginServlet, e); // SEVERE 级别记录崩溃 request.setAttribute(message, 系统繁忙请稍后再试); request.getRequestDispatcher(failure.jsp).forward(request, response); } } }配置日志输出WebContent/WEB-INF/logging.propertieshandlersjava.util.logging.FileHandler, java.util.logging.ConsoleHandler .levelINFO # 文件日志 java.util.logging.FileHandler.patternlogs/javaweb-%g.log java.util.logging.FileHandler.limit5000000 java.util.logging.FileHandler.count3 java.util.logging.FileHandler.formatterjava.util.logging.SimpleFormatter # 控制台日志 java.util.logging.ConsoleHandler.levelINFO java.util.logging.ConsoleHandler.formatterjava.util.logging.SimpleFormatter然后在web.xml中指定context-param param-namejava.util.logging.config.file/param-name param-value/WEB-INF/logging.properties/param-value /context-param效果所有logger.info()输出到logs/javaweb-0.log按大小轮转logger.warning()和logger.severe()自动标红一眼定位问题生产环境关掉 ConsoleHandler只留文件日志不影响性能。从那以后我每次新建 JavaWeb 项目都会在src下先建好util/ValidationUtils.java、dao/UserDao.java、logging.properties这三个文件再写第一个 Servlet。不是为了炫技是因为真正的工程效率不来自写得多快而来自改得多稳。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网