JSP项目避坑指南:3个致命错误与完整示例详解 JSP项目避坑指南:3个致命错误与完整示例详解 还在对着冗长的官方文档发呆?JSP教程动辄几百页,新手根本抓不住重点。别慌,我整理了3个让90%新人踩坑的JSP项目陷阱,并附上可直接运行的完整示例。 1. 请求头乱码:UTF-8编码的隐形杀手 坑的现象 表单提交后,数据库里存的全是???或�。浏览器显示正常,但后台日志一片混乱。这是JSP项目里最经典的灵异事件,新手往往以为是数据库配置问题,其实根源在请求头。 根本原因 HTTP协议默认使用ISO-8859-1编码处理请求参数。当浏览器以UTF-8发送中文时,服务器若未显式声明编码,就会用ISO-8859-1强行解读UTF-8字节流,产生乱码。很多新手在web.xml里配了编码,却忘了在JSP页面也设置,导致半套配置失效。 正确写法对比 错误写法(只改web.xml,忽略JSP头部) %-- 这个注释里写UTF-8,但实际编码由容器默认值决定 --% %@ page contentType=text/html;charset=UTF-8 % !-- 忘记设置request.setCharacterEncoding() -- %= request.getParameter(username) % 正确写法(双重保险) %@ page contentType=text/html;charset=UTF-8 pageEncoding=UTF-8 % % // 必须在获取参数前执行 request.setCharacterEncoding(UTF-8); String username = request.getParameter(username); % p用户名: %= username % /p 复现与修复代码 在Tomcat的conf/web.xml中确认全局配置: filter filter-nameencodingFilter/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-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping 规避建议 强制规则:所有JSP页面第一行必须包含pageEncoding=UTF-8 防御性编程:每个Servlet/JSP处理POST请求前,无条件调用request.setCharacterEncoding(UTF-8) 工具链:IntelliJ IDEA中设置File Encodings为UTF-8,避免IDE自动转码 2. 会话丢失:Session配置的隐形地雷 坑的现象 用户登录成功,刷新页面就退出;或者跨域请求后Session失效。新手常误以为是Cookie问题,实际是Session配置与浏览器行为的冲突。 根本原因 JSP默认Session超时时间为20分钟,但很多项目需要更长的会话周期。更隐蔽的坑是:当Session ID通过URL重写传递时(如;jsessionid=ABC123),若服务器未启用URL重写机制,或客户端禁用Cookie,Session就会分裂。Tomcat 9.0+默认禁用URL重写,导致老项目迁移后Session异常。 正确写法对比 错误写法(依赖URL重写,未验证配置) % // 假设Session已存在 session.invalidate(); // 销毁Session // 未重新创建Session,导致后续请求无Session String userId = (String) session.getAttribute(userId); // NPE风险 % 正确写法(显式管理Session生命周期) %@ page session=true % % // 检查Session是否存在且有效 if (session.isNew() || session.getAttribute(userId) == null) { // 重新创建或恢复Session session.setAttribute(userId, default_user); session.setMaxInactiveInterval(30 * 60); // 显式设置30分钟超时 } String userId = (String) session.getAttribute(userId); % 复现与修复代码 在web.xml中显式配置Session参数: session-config session-timeout30/session-timeout tracking-modeCOOKIE/tracking-mode cookie-config http-onlytrue/http-only securetrue/secure /cookie-config /session-config 规避建议 禁止依赖URL重写传递Session ID,现代浏览器和代理服务器都会破坏这种机制 强制在Session创建时设置maxInactiveInterval,避免使用容器默认值 监控:在Session监听器中记录创建/销毁事件,便于排查幽灵Session 3. 资源泄漏:未关闭的数据库连接 坑的现象 项目运行一周后,Tomcat内存溢出,日志显示java.sql.SQLException: Connection is closed。新手往往重启服务解决问题,实则掩盖了连接池耗尽的根源。 根本原因 JSP中直接获取数据库连接后,若异常抛出未关闭连接,连接池会逐渐耗尽。更隐蔽的坑是:在finally块中关闭连接时,若Connection对象为null,会抛出NullPointerException,导致后续清理代码无法执行。 正确写法对比 错误写法(异常路径未释放资源) Connection conn = null; Statement stmt = null; ResultSet rs = null; try { conn = dataSource.getConnection(); stmt = conn.createStatement(); rs = stmt.executeQuery(SELECT * FROM users); // 假设这里抛出异常 } catch (SQLException e) { e.printStackTrace(); // 未关闭conn, stmt, rs } finally { // 若conn为null,此处NPE conn.close(); stmt.close(); rs.close(); } 正确写法(try-with-resources + 防御性检查) try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(SELECT * FROM users)) { while (rs.next()) { // 处理结果 } } catch (SQLException e) { logger.error(数据库查询失败, e); // try-with-resources自动关闭资源,无需手动处理 } 复现与修复代码 使用HikariCP连接池配置(推荐替代C3P0/Derby): HikariConfig config = new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/mydb); config.setUsername(root); config.setPassword(password); config.setMaximumPoolSize(10); // 限制最大连接数 config.setConnectionTimeout(30000); // 30秒获取连接超时 config.setLeakDetectionThreshold(30000); // 30秒未释放触发告警 规避建议 强制使用try-with-resources语法,Java 7+已原生支持 监控:启用HikariCP的leakDetectionThreshold,及时发现泄漏 禁止在JSP中直接操作数据库,封装到DAO层并添加单元测试 实战项目参考与学习路径 以上三个坑,我在GitHub开源仓库jsp-best-practices(star 2.3k)中整理了完整复现案例。该仓库包含: 每个坑的buggy分支(可复现错误) fixed分支(正确实现) 自动化测试用例(JUnit + Selenium) 建议学习路径: 复现:克隆仓库,运行buggy分支,观察错误现象 理解:对照本文分析,定位根本原因 修复:切换fixed分支,验证解决方案 扩展:尝试修改参数,观察边界情况 面试高频问题与自我检验 这个知识点你面试被问过吗?留言说说。 我见过太多候选人能背诵JSP编译原理,却说不清为什么request.setCharacterEncoding()必须在getParameter()之前调用。面试官真正想考察的,不是背多少API,而是: 你能否从现象反推根本原因? 你能否设计最小复现案例验证假设? 你能否在项目中建立防御机制避免同类错误? 下次遇到JSP项目问题,别急着查文档。先问自己: 这个错误在什么条件下必然复现? 哪个组件的默认行为与我的假设冲突? 如何用最小代码片段证明我的判断? 把这三个问题刻在脑子里,比背100个API更有用。