当前位置: 首页 > news >正文

JavaWeb期末通关指南——从Servlet到MVC的实战复习路线

1. Servlet生命周期:从出生到退休的完整旅程

第一次接触Servlet时,我完全被它的生命周期搞晕了。直到把Tomcat比作餐厅,Servlet比作厨师,才恍然大悟——init()是厨师上岗培训,service()是日常做菜,destroy()是退休离职。这个类比让我瞬间理解了Servlet的工作机制。

Servlet的生命周期严格遵循三个阶段:

初始化阶段:当Tomcat第一次收到某个Servlet的请求时,会调用init()方法。这个方法在整个生命周期中只执行一次,就像厨师的入职培训。我常在这里加载配置文件或建立数据库连接:

public void init(ServletConfig config) throws ServletException { // 读取web.xml中的初始化参数 String dbUrl = config.getInitParameter("database_url"); // 建立数据库连接池 connectionPool = new ConnectionPool(dbUrl); }

服务阶段:每次请求都会触发service()方法。Tomcat会创建新的request和response对象传入,就像顾客点单时给厨师递菜单。这里要注意线程安全问题:

protected void service(HttpServletRequest req, HttpServletResponse resp) { // 错误示范:在方法内定义共享变量 int visitCount = 0; // 这个会被多个线程同时修改 // 正确做法:使用局部变量或同步块 synchronized(this) { totalVisits++; // 线程安全的计数器 } }

销毁阶段:服务器关闭时调用destroy()方法。这里要优雅地释放资源,就像厨师下班前要收拾厨房:

public void destroy() { // 关闭数据库连接池 connectionPool.close(); // 清理临时文件 tempFileCleaner.cleanUp(); }

常见坑点:很多同学在init()里初始化耗时资源,导致应用启动缓慢。我的经验是使用懒加载,第一次请求时再初始化非关键资源。

2. JSP九大内置对象:你的随身工具包

刚开始用JSP时,我总在纠结该用request还是session。直到把Web应用比作商场购物才明白:request是购物篮(一次结账就清空),session是会员卡(多次消费都有效),application是商场公告栏(所有顾客可见)。

核心四大作用域对比

对象类比有效范围典型应用场景
pageContext收银小票当前页面页面内变量传递
request购物篮同一次请求表单提交、请求转发
session会员卡同一次会话用户登录状态
application商场公告栏整个应用网站访问计数器

高频方法实战

处理表单时,我常用request的这几个方法:

<% // 获取单个参数 String username = request.getParameter("user"); // 获取复选框多个值 String[] hobbies = request.getParameterValues("hobby"); // 设置属性供转发使用 request.setAttribute("welcomeMsg", "Hello " + username); %>

登录验证时session的典型用法:

<% // 存储用户信息 session.setAttribute("currentUser", userObj); // 设置30分钟超时 session.setMaxInactiveInterval(1800); // 退出时清空session session.invalidate(); %>

易错点:很多同学分不清sendRedirect和forward。记住重定向是两次请求(地址栏变,session有效),转发是一次请求(地址栏不变,request有效)。

3. 会话管理:Cookie与Session的默契配合

有次做购物车功能,我完全依赖Session存储,结果用户清理Cookie后购物车就空了。这才明白Session依赖名为JSESSIONID的Cookie,两者必须配合使用。

Cookie实战要点

// 创建Cookie Cookie userCookie = new Cookie("lastVisit", new Date().toString()); // 设置7天有效期 userCookie.setMaxAge(60 * 60 * 24 * 7); // 设置路径使全站可用 userCookie.setPath("/"); // 添加到响应 response.addCookie(userCookie);

Session底层原理

  1. 首次请求时服务器创建Session并生成ID
  2. 通过Set-Cookie头将JSESSIONID发给浏览器
  3. 后续请求浏览器自动携带这个Cookie
  4. 服务器根据ID找到对应的Session对象

安全增强技巧

// 防止Session固定攻击 request.getSession().invalidate(); // 先使旧session失效 HttpSession newSession = request.getSession(true); // 创建新session // 重要操作前验证session新鲜度 long createTime = session.getCreationTime(); if(System.currentTimeMillis() - createTime > 1000*60*5) { // 超过5分钟的session需要重新登录 }

性能优化:对于大型应用,可以将会话数据存储在Redis等缓存中,减轻服务器内存压力。

4. JDBC进阶:从基础操作到连接池优化

早期项目我直接用DriverManager,当并发量上来后数据库连接成了瓶颈。改用连接池后性能提升了3倍,这让我深刻认识到资源管理的重要性。

传统JDBC流程

// 1. 注册驱动(新版可省略) Class.forName("com.mysql.cj.jdbc.Driver"); // 2. 获取连接 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/mydb?useSSL=false", "user", "pass"); // 3. 创建PreparedStatement String sql = "SELECT * FROM users WHERE age > ?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setInt(1, 18); // 4. 执行查询 ResultSet rs = stmt.executeQuery(); // 5. 处理结果 while(rs.next()) { System.out.println(rs.getString("username")); } // 6. 关闭资源(倒序关闭) rs.close(); stmt.close(); conn.close(); // 重要!不关闭会导致连接泄漏

Druid连接池配置

<!-- web.xml中配置 --> <context-param> <param-name>druid.url</param-name> <param-value>jdbc:mysql://localhost:3306/mydb</param-value> </context-param> <context-param> <param-name>druid.maxActive</param-name> <param-value>20</param-value> </context-param>

PreparedStatement防注入示例

// 危险!容易被SQL注入 String sql = "SELECT * FROM users WHERE username='" + name + "'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); // 安全做法 String sql = "SELECT * FROM users WHERE username=?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, name); // 自动处理特殊字符

性能对比:测试显示连接池方案比传统方式QPS提升5倍,PreparedStatement比Statement快30%。

5. MVC设计模式:分层解耦的艺术

第一次参与团队项目时,所有逻辑都写在JSP里,改个按钮颜色都要排查半天。采用MVC分层后,前端改动再也不会影响后台逻辑,维护效率直线上升。

经典MVC组件分工

  • Model(JavaBean):负责数据存取和业务逻辑
public class UserService { public User login(String name, String pwd) { // 验证逻辑... return user; } }
  • View(JSP):专注页面展示
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:if test="${not empty user}"> 欢迎, ${user.name}! </c:if>
  • Controller(Servlet):处理请求流转
protected void doPost(HttpServletRequest req, HttpServletResponse resp) { String name = req.getParameter("username"); String pwd = req.getParameter("password"); User user = new UserService().login(name, pwd); if(user != null) { req.getSession().setAttribute("user", user); req.getRequestDispatcher("/welcome.jsp").forward(req, resp); } else { resp.sendRedirect("/login.jsp?error=1"); } }

三层架构演进

  1. Model1时代:JSP+JavaBean,像大杂烩
  2. Model2雏形:引入Servlet作为控制器
  3. 现代框架:Spring MVC等提供更优雅的实现

实际项目中,我常用以下包结构组织代码:

/src /main /java /com /example /controller // 存放Servlet /service // 业务逻辑 /dao // 数据访问 /model // 实体类 /webapp /WEB-INF /views // JSP文件

调试技巧:在Filter中添加日志记录请求耗时,快速定位性能瓶颈:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { long start = System.currentTimeMillis(); chain.doFilter(req, res); long end = System.currentTimeMillis(); logger.info("请求耗时:" + (end - start) + "ms"); }

6. 过滤器与监听器:Web应用的守门人

做国际化功能时,我通过Filter统一设置编码,再也不用在每个Servlet里写request.setCharacterEncoding()了,代码整洁度提升明显。

编码过滤器实战

public class EncodingFilter implements Filter { private String encoding; public void init(FilterConfig config) { this.encoding = config.getInitParameter("encoding"); } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(encoding); res.setCharacterEncoding(encoding); chain.doFilter(req, res); // 必须调用! } }

web.xml配置

<filter> <filter-name>encodingFilter</filter-name> <filter-class>com.example.filter.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

监听器典型应用

@WebListener public class AppListener implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { // 应用启动时初始化数据库连接池 DataSource ds = createDataSource(); sce.getServletContext().setAttribute("dataSource", ds); } public void contextDestroyed(ServletContextEvent sce) { // 应用关闭时释放资源 DataSource ds = (DataSource)sce.getServletContext().getAttribute("dataSource"); ds.close(); } }

开发经验:过滤器链的顺序很重要,比如应该先执行权限检查再处理编码。在web.xml中filter-mapping的声明顺序决定执行顺序。

7. 实战高频考点剖析

期末考试中,有些知识点就像明星一样频繁出场。根据我帮学弟学妹复习的经验,这些是必掌握的重点:

请求转发 vs 重定向

// 请求转发(服务器内部跳转) request.getRequestDispatcher("target.jsp").forward(request, response); // 重定向(客户端跳转) response.sendRedirect("http://example.com/newpage.jsp");

对比总结:

特性请求转发重定向
地址栏变化
请求次数1次2次
数据传递通过request属性通过URL参数或session
目标资源必须为同一应用可以跨应用

连接池配置参数解析

# Druid连接池典型配置 initialSize=5 # 初始连接数 maxActive=20 # 最大连接数 minIdle=5 # 最小空闲连接 maxWait=60000 # 获取连接超时时间(ms) timeBetweenEvictionRunsMillis=60000 # 检测间隔 minEvictableIdleTimeMillis=300000 # 连接空闲时间阈值

EL表达式简化操作

<!-- 传统写法 --> <%= ((User)session.getAttribute("user")).getName() %> <!-- EL表达式 --> ${user.name} <!-- 处理null值 --> ${empty user ? '未登录' : user.name} <!-- 访问Map --> ${scores['math']}

JSTL核心标签

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!-- 条件判断 --> <c:if test="${user.role == 'admin'}"> 管理员菜单 </c:if> <!-- 循环遍历 --> <c:forEach items="${products}" var="prod" varStatus="status"> ${status.index}. ${prod.name} - ${prod.price} </c:forEach>

备考建议:手写Servlet配置、JDBC完整流程、过滤器链调用顺序等高频考点,考试时能节省大量时间。我在考前会默写三遍Servlet生命周期方法,确保不丢分。

http://www.jsqmd.com/news/1190884/

相关文章:

  • SHAW模型驱动数据配置与常见运行问题解析
  • InterProScan本地化部署、性能调优与自动化批处理实战
  • HNU-计算机网络-实验2-从零构建Python3 Socket聊天室与文件传输系统
  • GPT-5.6与主流AI模型免魔法使用指南:API接入与实战代码
  • 基于ISO标准路面激励的被动悬架三响应仿真:加速度/动挠度/动载荷时频分析
  • 从零实现Linux C语言WebSocket服务器:协议解析与网络编程实战
  • Sqribble文档自动化:模板驱动的结构化出版流水线
  • F28335 ADC实战:从寄存器配置到多通道采样策略
  • 工业相机电气接口选型指南:从USB到CoaXPress的深度解析
  • BMS芯片BQ76952校准与保护机制深度解析及工程实践指南
  • AI角色对话生成:突破安全限制的技术方案与实践
  • 数字滤波器设计实战:从模拟原型到数字实现的映射方法抉择
  • 数据科学家必避的10大统计陷阱与实战解决方案
  • Unity中glTF模型导入与运行时加载:GLTFUtility插件实战指南
  • 图像分割技术解析:从U-Net到YOLO-Seg的实战应用
  • UE4电影级景深调校指南:从Focal Distance到实战应用
  • Erlang observer UI:可视化监控与性能分析实战指南
  • 项目管理(五):WBS实战——从理论到落地的分解艺术
  • PyTorch多卡训练通信瓶颈实战优化:All-Reduce、梯度累加与DDP调优
  • 从原理到选型:深入剖析位置式与增量式PID的工程应用差异
  • R Markdown数据预处理实战:医疗风控场景下的可复现清洗流水线
  • MSP430FR247x外设时序深度解析:从参数表到可靠嵌入式设计
  • 仅剩72小时生效!ChatGPT最新更新触发理财建议逻辑变更——资深投顾紧急发布的3条硬核拦截策略
  • 智谱GLM与Kimi大模型在金融科技领域的应用实践
  • ES6数组搜索方法实战指南:find()、findIndex()、indexOf()、includes()与some()的精准选择
  • 从杰斐逊的实践智慧看现代技术人的成长路径
  • 深入解析GitHub开源项目:从仓库创建到自动化管理的实战使用指南
  • MATLAB中find_system的进阶搜索与性能优化
  • C++有限元编程实战:从零构建结构电池应力-电化学耦合模型
  • ABAP新语法实战:Open SQL内置函数在数据转换与业务逻辑下推中的应用