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

【JavaWeb】三大组件之——Servlet

一、什么是Servlet

ServletServer Applet的简称,是用 Java 编写的服务器端程序。Servlet 被部署在 Web 服务器(如 Tomcat)中,由服务器负责管理并调用,用于处理客户端发送的请求并返回响应。

Servlet 的作用可以概括为三个核心步骤:

  1. 接收请求数据
  2. 处理请求
  3. 完成响应

💡 简单来说:Servlet 就是运行在服务器上的 Java 类,专门用来处理 HTTP 请求和响应。


二、Servlet 的核心特点

特点说明
功能强大基于 Java 语言编写,可调用 Java API,能处理各种 HTTP 请求
可移植Java 语言跨平台,Servlet 可在任何支持 Java 的服务器上运行
性能高效采用单例多线程模型,首次实例化后驻留内存,后续请求复用实例
安全性高继承 Java 安全框架,Servlet 容器提供额外安全机制
可扩展面向对象设计,支持封装、继承、多态等特性

三、Servlet 的体系结构

Servlet 的框架由两个核心 Java 包组成:

  • javax.servlet:定义了所有 Servlet 类必须实现或扩展的通用接口和类
  • javax.servlet.http:定义了采用 HTTP 协议通信的HttpServlet 类

3.1 核心接口与类的继承关系

Servlet (接口) │ ▼ GenericServlet (抽象类) │ ▼ HttpServlet (抽象类) │ ▼ 自定义 Servlet (如 MyServlet)

3.2 Servlet 接口中的核心方法

方法声明功能描述
void init(ServletConfig config)Servlet 实例化后,容器调用该方法完成初始化工作
ServletConfig getServletConfig()获取 Servlet 对象的配置信息
String getServletInfo()返回 Servlet 的描述信息(作者、版本等)
void service(ServletRequest req, ServletResponse res)负责响应用户请求,是 Servlet 的核心方法
void destroy()负责释放 Servlet 占用的资源

四、Servlet 的生命周期

Servlet 的生命周期由Web 容器(如 Tomcat)管理,分为以下四个阶段:

4.1 生命周期图解

4.2 各阶段详解

① 加载和实例化阶段
  • 触发时机
    • 容器启动时(配置了<load-on-startup>
    • 客户端首次请求该 Servlet 时
  • 过程:容器通过 Java 反射调用 Servlet 的无参构造方法创建实例
  • 注意事项:编写 Servlet 时不要提供带参数的构造方法
② 初始化阶段(init)
publicvoidinit(ServletConfigconfig)throwsServletException{// 初始化代码:加载配置文件、建立数据库连接等super.init(config);}
  • 特点:只执行一次
  • 用途:完成一些初始化工作,如加载配置文件、建立数据库连接、创建必要对象等
  • 参数ServletConfig对象包含了 web.xml 或@WebServlet中配置的初始化参数
③ 服务阶段(service)
publicvoidservice(ServletRequestreq,ServletResponseres)throwsServletException,IOException{// 处理请求的核心方法}
  • 特点:每次请求都会执行
  • 过程
    1. 容器为每个请求创建ServletRequestServletResponse对象
    2. 调用service()方法处理请求
    3. service()方法根据请求类型(GET/POST/PUT/DELETE 等)分派到doGet()doPost()等方法
  • 线程模型:Servlet 采用单例多线程模式,多个请求共享同一个 Servlet 实例,每个请求由独立线程处理
④ 销毁阶段(destroy)
publicvoiddestroy(){// 释放资源:关闭数据库连接、清理临时文件等super.destroy();}
  • 触发时机:服务器关闭、Web 应用被移除、Servlet 被卸载
  • 特点:只执行一次
  • 用途:释放 Servlet 占用的资源,如关闭数据库连接、清理临时文件等

4.3 生命周期执行流程图


五、Servlet 的配置方式

Servlet 的配置有两种方式:web.xml 配置注解配置

5.1 web.xml 配置方式(传统方式)

WEB-INF/web.xml文件中进行配置:

<?xml version="1.0" encoding="UTF-8"?><web-appxmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><!-- Servlet 配置 --><servlet><!-- Servlet 名称,自定义 --><servlet-name>HelloServlet</servlet-name><!-- Servlet 类的全限定名 --><servlet-class>com.example.servlet.HelloServlet</servlet-class><!-- 初始化参数 --><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><!-- 启动时加载顺序,数字越小优先级越高 --><load-on-startup>1</load-on-startup></servlet><!-- Servlet 映射配置 --><servlet-mapping><servlet-name>HelloServlet</servlet-name><!-- 访问路径 --><url-pattern>/hello</url-pattern></servlet-mapping></web-app>

5.2 注解配置方式(推荐)

使用@WebServlet注解,无需 web.xml 配置:

packagecom.example.servlet;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.PrintWriter;// 使用 @WebServlet 注解配置@WebServlet(name="HelloServlet",// Servlet 名称urlPatterns="/hello",// URL 映射路径initParams={// 初始化参数@WebInitParam(name="encoding",value="UTF-8")},loadOnStartup=1// 启动加载顺序)publicclassHelloServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{// 设置响应内容类型和编码resp.setContentType("text/html;charset=UTF-8");// 获取输出流PrintWriterout=resp.getWriter();out.println("<html>");out.println("<head><title>Hello Servlet</title></head>");out.println("<body>");out.println("<h1>Hello, Servlet!</h1>");out.println("<p>这是我的第一个 Servlet 程序</p>");out.println("</body>");out.println("</html>");}@OverrideprotectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{// 设置请求编码,解决中文乱码req.setCharacterEncoding("UTF-8");// 获取表单参数Stringusername=req.getParameter("username");Stringpassword=req.getParameter("password");// 处理业务逻辑...// 转发到结果页面req.setAttribute("msg","登录成功!");req.getRequestDispatcher("/result.jsp").forward(req,resp);}}

5.3 @WebServlet 注解属性详解

属性名类型功能描述等价于 web.xml
nameString指定 Servlet 的名称<servlet-name>
valueString[]等价于urlPatterns<url-pattern>
urlPatternsString[]指定 URL 匹配模式<url-pattern>
loadOnStartupint指定启动加载顺序<load-on-startup>
initParamsWebInitParam[]指定初始化参数<init-param>
asyncSupportedboolean是否支持异步操作<async-supported>
descriptionStringServlet 描述信息<description>
displayNameStringServlet 显示名称<display-name>

六、Servlet 处理请求的流程

6.1 请求处理完整流程

6.2 HttpServlet 常用方法

方法功能描述
doGet(HttpServletRequest, HttpServletResponse)处理 GET 请求
doPost(HttpServletRequest, HttpServletResponse)处理 POST 请求
doPut(HttpServletRequest, HttpServletResponse)处理 PUT 请求
doDelete(HttpServletRequest, HttpServletResponse)处理 DELETE 请求
doHead(HttpServletRequest, HttpServletResponse)处理 HEAD 请求
doOptions(HttpServletRequest, HttpServletResponse)处理 OPTIONS 请求

七、Servlet 线程安全问题

由于 Servlet 采用单例多线程模型,多个请求共享同一个实例,因此需要注意线程安全问题。

7.1 线程安全问题示例

@WebServlet("/unsafe")publicclassUnsafeServletextendsHttpServlet{// ❌ 错误:实例变量被多线程共享,存在线程安全问题privateintcount=0;@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{count++;// 多个线程同时操作,结果不可预期resp.getWriter().println("Count: "+count);}}

7.2 解决方案

@WebServlet("/safe")publicclassSafeServletextendsHttpServlet{// ✅ 正确:使用局部变量,每个线程独立@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{intcount=0;// 局部变量,线程安全count++;resp.getWriter().println("Count: "+count);}// ✅ 正确:使用同步代码块(但会影响性能)privatefinalObjectlock=newObject();privateintsharedCount=0;protectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{synchronized(lock){sharedCount++;resp.getWriter().println("Shared Count: "+sharedCount);}}}

7.3 线程安全最佳实践

建议说明
避免使用实例变量尽量使用局部变量存储请求相关数据
使用线程安全类ConcurrentHashMapAtomicInteger
同步关键代码必要时使用synchronizedLock
使用请求域存储数据通过request.setAttribute()传递数据

八、ServletContext 与 ServletConfig

8.1 ServletConfig(单个 Servlet 配置)

ServletConfig对象代表当前 Servlet 的配置信息,每个 Servlet 独享一个ServletConfig

@Overridepublicvoidinit(ServletConfigconfig)throwsServletException{super.init(config);// 获取初始化参数Stringencoding=config.getInitParameter("encoding");System.out.println("编码配置:"+encoding);// 获取 Servlet 名称StringservletName=config.getServletName();System.out.println("Servlet 名称:"+servletName);}

8.2 ServletContext(全局应用配置)

ServletContext对象代表整个 Web 应用,所有 Servlet 共享同一个ServletContext

@WebServlet("/context")publicclassContextServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{// 获取 ServletContextServletContextcontext=getServletContext();// 获取全局初始化参数StringdbUrl=context.getInitParameter("dbUrl");// 存储全局属性(所有 Servlet 共享)context.setAttribute("appName","MyWebApp");// 获取真实路径StringrealPath=context.getRealPath("/upload");// 获取资源输入流InputStreamis=context.getResourceAsStream("/WEB-INF/config.properties");}}

8.3 两者对比

特性ServletConfigServletContext
作用范围单个 Servlet整个 Web 应用
获取方式getServletConfig()getServletContext()
参数配置<init-param><context-param>
数据共享不共享所有 Servlet 共享

九、请求转发与重定向

9.1 请求转发(Forward)

// 获取请求分发器RequestDispatcherdispatcher=req.getRequestDispatcher("/target.jsp");// 转发请求dispatcher.forward(req,resp);
  • 特点:服务器内部跳转,URL 不变,请求对象共享
  • 适用场景:MVC 模式中的控制器转发到视图

9.2 重定向(Redirect)

// 发送重定向响应resp.sendRedirect("/target.jsp");// 或重定向到外部网站resp.sendRedirect("https://www.example.com");
  • 特点:客户端重新发起请求,URL 改变,请求对象不共享
  • 适用场景:登录成功后跳转、跨域跳转

9.3 两者对比

特性请求转发(Forward)重定向(Redirect)
跳转方式服务器内部客户端重新请求
URL 变化不变改变
请求对象共享不共享
范围同一服务器内可跨域
效率低(两次请求)

十、完整示例:用户登录 Servlet

packagecom.example.servlet;importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.PrintWriter;/** * 用户登录 Servlet 示例 */@WebServlet(name="LoginServlet",urlPatterns="/login")publicclassLoginServletextendsHttpServlet{@Overridepublicvoidinit()throwsServletException{System.out.println("LoginServlet 初始化...");}@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{// 显示登录页面resp.setContentType("text/html;charset=UTF-8");PrintWriterout=resp.getWriter();out.println("<!DOCTYPE html>");out.println("<html><head><title>登录</title></head><body>");out.println("<h2>用户登录</h2>");out.println("<form action='/login' method='post'>");out.println("用户名:<input type='text' name='username'><br><br>");out.println("密&nbsp;&nbsp;码:<input type='password' name='password'><br><br>");out.println("<input type='submit' value='登录'>");out.println("</form></body></html>");}@OverrideprotectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{// 设置请求编码req.setCharacterEncoding("UTF-8");// 获取请求参数Stringusername=req.getParameter("username");Stringpassword=req.getParameter("password");// 简单的业务逻辑验证if("admin".equals(username)&&"123456".equals(password)){// 登录成功,重定向到首页req.getSession().setAttribute("user",username);resp.sendRedirect("/index.jsp");}else{// 登录失败,请求转发回登录页并显示错误信息req.setAttribute("error","用户名或密码错误!");req.getRequestDispatcher("/login.jsp").forward(req,resp);}}@Overridepublicvoiddestroy(){System.out.println("LoginServlet 销毁...");}}

总结

本文详细介绍了 JavaWeb 三大组件之一的Servlet,包括:

知识点核心内容
Servlet 定义运行在服务器端的 Java 程序,处理 HTTP 请求和响应
核心特点功能强大、可移植、性能高效、安全性高、可扩展
体系结构Servlet → GenericServlet → HttpServlet → 自定义 Servlet
生命周期加载实例化 → init() → service() → destroy()
配置方式web.xml 配置 和 @WebServlet 注解配置
线程安全单例多线程模型,注意避免实例变量的线程安全问题
核心对象ServletRequest、ServletResponse、ServletConfig、ServletContext
页面跳转请求转发(Forward)和重定向(Redirect)

如果本文对你有帮助,欢迎点赞 👍、收藏 ⭐、关注 💖,你的支持是我持续创作的动力!

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

相关文章:

  • pandas删列原理与性能优化:BlockManager、内存拷贝与CoW机制解析
  • 5分钟快速上手TEdit:泰拉瑞亚地图编辑器的终极指南
  • 深入 Linux 线程机制(三):线程互斥——竞争条件与互斥锁的本质
  • RAG 多路召回架构:向量 + 关键词 + 知识图谱的融合实战
  • 影刀RPA学习路线图:从小白到独立开发的完整学习路径
  • 《在飞机上偶遇前任后》穗雪|小说|txt下载|番外|免费阅读
  • PyTorch Dataset与DataLoader配置优化:4个关键参数对训练速度的影响实测
  • Python爬虫实战:多线程与异步IO并发爬虫——从10倍到100倍提速
  • MediaCrawler-new:多平台社交媒体数据采集技术框架与自动化解决方案
  • ICM-42688-P与PIC24HJ256GP610在运动控制中的高效协同
  • 城市违章搭建识别、棚户区语义分割与YOLO格式数据集:基于深度学习的城市治理AI实践全解析10693期
  • 《从 300 行到 30 万行代码,我学到的 5 个重构教训》
  • 5分钟找回QQ空间全部历史说说的终极指南:GetQzonehistory帮你永久保存青春记忆
  • 好用的区域教育一体化管理平台哪个供应商好
  • Google谷歌浏览器 Chrome浏览器安装包 官方正版离线安装包,支持中文界面!
  • 【Redis八股|第1篇】Redis 是什么?为什么它这么快?
  • 2026视频转文字工具选择指南:拆解免费付费坑点,看懂隐私、水印、收费套路避踩雷
  • AI生成图片文字模糊?AnyText技术原理与实战指南
  • 第48期 OpenAI的钱都花哪了?Capex支出全景调研
  • 一款AI笔记助手和远程同步的markdown笔记idea-note
  • 英雄联盟玩家必备:如何用LCU工具箱提升你的游戏效率3倍?
  • 质量管理必备的10个基本思维方式
  • 《夫人要和离》藤鹿山|小说|txt下载|番外|免费阅读
  • 38、<简单>编程输入10个正整数,然后自动按从大到小的顺序输出
  • 《被错认成道侣后》山野行月|小说|txt下载|全文
  • 【共创季稿事节】HarmonyOS 6.1 API23新特性实战:Marquee跑马灯组件进阶用法
  • 抖音无水印视频下载神器:从零开始掌握批量下载技巧
  • Grafana Dashboard JSON 文件详解
  • Java计算机毕设之基于前后端分离的病毒靶点药物关系数据采集系统的设计与实现 基于 SpringBoot 的生物医药多源数据采集与归档系统(完整前后端代码+说明文档+LW,调试定制等)
  • Driver Store Explorer:Windows驱动管理的终极解决方案,三步释放系统空间