免责声明


由于传播、利用本公众号绿洲安全所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号绿洲安全及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢
转自:https://blog.csdn.net/m0_73803300/article/details/150510975一
Servlet 概述
运行在服务器端:Servlet 由 Web 服务器(如 Tomcat)加载和管理 基于 HTTP 协议:专门处理 HTTP 请求和响应 多线程支持:一个 Servlet 实例可以同时处理多个客户端请求(注意线程安全) 组件化:遵循 Java EE 规范,可被 Web 容器管理 跨平台:基于 Java 语言,一次编写,到处运行
接收客户端请求数据(表单参数、URL 参数等) 处理业务逻辑(调用 Service 层、DAO 层等) 与数据库交互(查询、插入、更新数据等) 生成动态响应内容(HTML、JSON、XML 等) 实现页面跳转和重定向
二
Servlet 的生命周期
import javax.servlet.*;import javax.servlet.annotation.WebServlet;import java.io.IOException;("/lifeCycle")public class LifeCycleServlet implements Servlet {private ServletConfig config;// 1. 构造方法(实例化阶段)public LifeCycleServlet() {System.out.println("1. 构造方法被调用(实例化Servlet)");}// 2. 初始化方法public void init(ServletConfig config) throws ServletException {this.config = config;System.out.println("2. init()方法被调用(初始化Servlet)");// 可以获取初始化参数String param = config.getInitParameter("initParam");if (param != null) {System.out.println("初始化参数:" + param);}}// 3. 处理请求的核心方法public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {System.out.println("3. service()方法被调用(处理请求)");res.getWriter().write("Servlet Life Cycle Demo");}// 4. 销毁方法public void destroy() {System.out.println("4. destroy()方法被调用(销毁Servlet)");// 释放资源操作}public ServletConfig getServletConfig() {return config;}public String getServletInfo() {return "Life Cycle Servlet Demo";}}
1. 构造方法被调用(实例化Servlet)2. init()方法被调用(初始化Servlet)3. service()方法被调用(处理请求) // 第一次访问时3. service()方法被调用(处理请求) // 第二次访问时...
4. destroy()方法被调用(销毁Servlet)
三
编写 Servlet 的两种方式
方式 1:继承 GenericServlet 类
import javax.servlet.GenericServlet;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class MyGenericServlet extends GenericServlet {public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {// 设置响应内容类型res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();// 输出HTML内容out.println("<html>");out.println("<head><title>GenericServlet示例</title></head>");out.println("<body>");out.println("<h1>这是一个GenericServlet示例</h1>");out.println("请求方法:" + ((HttpServletRequest) req).getMethod());out.println("</body>");out.println("</html>");}}
GenericServlet的优势:
实现了Servlet和ServletConfig接口
提供了getServletContext()、getInitParameter()等便捷方法
无需重写所有Servlet接口方法,只需实现service()
方式 2:继承 HttpServlet 类(推荐)
HttpServlet是GenericServlet的子类,专门用于处理 HTTP 请求,是最常用的方式:
import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class MyHttpServlet extends HttpServlet {// 处理GET请求protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<h1>处理GET请求</h1>");out.println("用户名:" + req.getParameter("username")); // 获取URL参数}// 处理POST请求protected void doPost(HttpServletRequest req, HttpServletResponse res)throws IOException {// 设置请求编码(处理中文参数)req.setCharacterEncoding("UTF-8");res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<h1>处理POST请求</h1>");out.println("用户名:" + req.getParameter("username")); // 获取表单参数}}
HttpServlet的工作流程:
1.重写service()方法,将ServletRequest和ServletResponse转换为HttpServletRequest和HttpServletResponse
2.根据 HTTP 请求方法(GET、POST、PUT、DELETE 等)调用对应的doXxx()方法
3.开发者只需重写需要处理的doXxx()方法(通常是doGet()和doPost())
四
Servlet 配置方式
Servlet 有两种配置方式:注解配置(Servlet 3.0 + 推荐)和XML 配置(传统方式)。
1. 注解配置(@WebServlet)
@WebServlet注解用于配置 Servlet,常用属性:
| 说明 | |
value/urlPatterns | "/user"、"/*") |
@WebInitParam数组) | |
示例:
import javax.servlet.annotation.WebInitParam;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;name = "ConfigServlet",urlPatterns = {"/config", "/configServlet"}, // 多个URL映射loadOnStartup = 2, // 服务器启动时加载initParams = { // 初始化参数@WebInitParam(name = "version", value = "1.0"),@WebInitParam(name = "author", value = "JavaWeb")})public class ConfigServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<h1>Servlet配置示例</h1>");out.println("Servlet名称:" + getServletName() + "<br>");// 获取初始化参数out.println("版本:" + getInitParameter("version") + "<br>");out.println("作者:" + getInitParameter("author") + "<br>");// 获取所有初始化参数名称out.println("<h3>所有初始化参数:</h3>");getInitParameterNames().asIterator().forEachRemaining(name -> {out.println(name + ":" + getInitParameter((String) name) + "<br>");});}}
2. XML 配置(web.xml)
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><!-- 配置Servlet --><servlet><servlet-name>XmlConfigServlet</servlet-name><servlet-class>com.example.XmlConfigServlet</servlet-class><!-- 初始化参数 --><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><!-- 启动时加载(可选) --><load-on-startup>3</load-on-startup></servlet><!-- 配置Servlet映射 --><servlet-mapping><servlet-name>XmlConfigServlet</servlet-name><url-pattern>/xmlConfig</url-pattern></servlet-mapping><!-- 可以配置多个映射 --><servlet-mapping><servlet-name>XmlConfigServlet</servlet-name><url-pattern>/xmlConfigServlet</url-pattern></servlet-mapping></web-app>
对应的 Servlet 类:
package com.example;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class XmlConfigServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<h1>XML配置的Servlet</h1>");out.println("编码:" + getInitParameter("encoding"));}}
五
Servlet 核心对象:Request 与 Response
HttpServletRequest和HttpServletResponse是 Servlet 处理请求和响应的核心对象。
1. HttpServletRequest(请求对象)
封装了客户端发送的 HTTP 请求信息,常用方法:
| 方法 | 说明 |
|---|---|
| String getParameter(String name) | 获取请求参数(表单或 URL 参数) |
| String[] getParameterValues(String name) | 获取多值参数(如复选框) |
| Map<String, String[]> getParameterMap() | 获取所有参数的 Map 集合 |
| String getMethod() | 获取 HTTP 请求方法(GET/POST 等) |
| String getRequestURI() | 获取请求 URI(如 /myapp/user) |
| String getContextPath() | 获取 Web 应用上下文路径(如 /myapp) |
| String getRemoteAddr() | 获取客户端 IP 地址 |
| void setCharacterEncoding(String env) | 设置请求体编码(处理 POST 中文) |
| HttpSession getSession() | 获取会话对象 |
| void setAttribute(String name, Object o) | 设置请求域属性 |
| Object getAttribute(String name) | 获取请求域属性 |
示例:处理表单提交
import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.Arrays;import java.util.Map;public class FormHandlerServlet extends HttpServlet {protected void doPost(HttpServletRequest req, HttpServletResponse res)throws IOException {// 处理POST中文参数req.setCharacterEncoding("UTF-8");res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<h1>表单处理结果</h1>");// 获取单个参数String username = req.getParameter("username");String password = req.getParameter("password");out.println("用户名:" + username + "<br>");out.println("密码:" + password + "<br>");// 获取多值参数(复选框)String[] hobbies = req.getParameterValues("hobby");if (hobbies != null) {out.println("爱好:" + Arrays.toString(hobbies) + "<br>");}// 获取所有参数out.println("<h3>所有参数:</h3>");Map<String, String[]> paramMap = req.getParameterMap();for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {out.println(entry.getKey() + ":" + Arrays.toString(entry.getValue()) + "<br>");}// 获取请求信息out.println("<h3>请求信息:</h3>");out.println("请求方法:" + req.getMethod() + "<br>");out.println("请求URI:" + req.getRequestURI() + "<br>");out.println("客户端IP:" + req.getRemoteAddr() + "<br>");}// 支持GET请求访问protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.sendRedirect("form.html"); // 重定向到表单页面}}
对应的表单页面(form.html):
<html><head><meta charset="UTF-8"><title>用户表单</title></head><body><form action="/myapp/formHandler" method="post"><div>用户名:<input type="text" name="username"></div><div>密码:<input type="password" name="password"></div><div>爱好:<input type="checkbox" name="hobby" value="reading">阅读<input type="checkbox" name="hobby" value="sports">运动<input type="checkbox" name="hobby" value="music">音乐</div><div><input type="submit" value="提交"></div></form></body></html>
2. HttpServletResponse(响应对象)
| 方法 | 说明 |
|---|---|
| PrintWriter getWriter() | 获取字符输出流(输出文本) |
| ServletOutputStream getOutputStream() | 获取字节输出流(输出二进制数据) |
| void setContentType(String type) | 设置响应内容类型(如 text/html;charset=UTF-8) |
| void setCharacterEncoding(String charset) | 设置响应字符编码 |
| void sendRedirect(String location) | 重定向到新 URL(客户端跳转) |
| void setStatus(int sc) | 设置 HTTP 响应状态码 |
| void addHeader(String name, String value) | 添加响应头 |
示例:生成动态响应
import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.Date;public class ResponseDemoServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {// 1. 输出HTML响应res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<!DOCTYPE html>");out.println("<html>");out.println("<head>");out.println("<title>响应示例</title>");out.println("</head>");out.println("<body>");out.println("<h1>动态生成的HTML页面</h1>");out.println("<p>当前时间:" + new Date() + "</p>");out.println("<p>客户端IP:" + req.getRemoteAddr() + "</p>");out.println("</body>");out.println("</html>");// 2. 重定向示例(注释上面代码,测试重定向)// res.sendRedirect("https://www.example.com");// 3. 设置响应状态码示例// res.setStatus(404); // 未找到// res.setStatus(500); // 服务器错误}}
六
请求与转发
1. 请求转发(Forward)
// Servlet1:处理部分逻辑后转发到Servlet2public class Servlet1 extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException {System.out.println("Servlet1处理请求");// 设置请求域属性(可在转发目标中获取)req.setAttribute("message", "来自Servlet1的消息");// 获取请求调度器,转发到/servlet2RequestDispatcher dispatcher = req.getRequestDispatcher("/servlet2");dispatcher.forward(req, res); // 执行转发}}// Servlet2:接收转发的请求public class Servlet2 extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<h1>Servlet2处理结果</h1>");// 获取Servlet1设置的属性String message = (String) req.getAttribute("message");out.println("接收到的消息:" + message);}}
请求转发的特点:
客户端只发送一次请求
浏览器地址栏 URL 不变
共享同一个HttpServletRequest对象(可通过setAttribute()传递数据)
只能转发到当前 Web 应用内部的资源
2. 重定向(Redirect)
客户端跳转:服务器向客户端发送一个 302 响应和新的 URL,客户端自动向新 URL 发送请求,地址栏会改变。
public class RedirectServlet1 extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {System.out.println("RedirectServlet1处理请求");// 设置请求域属性(重定向中无法传递)req.setAttribute("message", "这个消息不会被收到");// 重定向到/redirect2// 方式1:使用sendRedirect()res.sendRedirect("/myapp/redirect2");// 方式2:手动设置状态码和响应头(等价于方式1)// res.setStatus(302);// res.setHeader("Location", "/myapp/redirect2");}}public class RedirectServlet2 extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();out.println("<h1>RedirectServlet2处理结果</h1>");// 尝试获取RedirectServlet1设置的属性(结果为null)String message = (String) req.getAttribute("message");out.println("接收到的消息:" + message); // null}}
客户端发送两次请求 浏览器地址栏 URL 改变 不共享 HttpServletRequest对象(无法通过setAttribute()传递数据)可以重定向到其他 Web 应用甚至其他网站的资源
请求转发与重定向的对比
| 特性 | 请求转发(Forward) | 重定向(Redirect) |
|---|---|---|
| 跳转方式 | 服务器端跳转 | 客户端跳转 |
| 浏览器地址 | 不变 | 改变 |
| 请求次数 | 1 次 | 2 次 |
| 数据共享 | 共享 Request 对象 | 不共享 |
| 跳转范围 | 仅限当前 Web 应用 | 任意 URL |
| 适用场景 | 同一应用内页面跳转,需传递数据 | 不同应用间跳转,或避免表单重复提交 |
ServletContext 对象
ServletContext(Servlet 上下文)代表整个 Web 应用,是所有 Servlet 共享的全局对象。
ServletContext 的作用
存储全局配置信息
在不同 Servlet 之间共享数据
获取 Web 应用的资源文件
获取 Web 应用的初始化参数
ServletContext 示例
import javax.servlet.ServletContext;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.util.Properties;public class ServletContextDemoServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();// 获取ServletContext对象ServletContext context = getServletContext();// 1. 获取Web应用初始化参数(在web.xml中配置)out.println("<h3>Web应用初始化参数:</h3>");String appName = context.getInitParameter("appName");String appVersion = context.getInitParameter("appVersion");out.println("应用名称:" + appName + "<br>");out.println("应用版本:" + appVersion + "<br>");// 2. 存储和获取全局共享数据context.setAttribute("onlineUsers", 100); // 设置全局属性int onlineUsers = (Integer) context.getAttribute("onlineUsers"); // 获取全局属性out.println("<h3>全局共享数据:</h3>");out.println("在线用户数:" + onlineUsers + "<br>");// 3. 获取Web应用资源out.println("<h3>Web应用资源:</h3>");// 获取资源在服务器上的真实路径String realPath = context.getRealPath("/WEB-INF/config.properties");out.println("config.properties真实路径:" + realPath + "<br>");// 读取资源文件内容InputStream is = context.getResourceAsStream("/WEB-INF/config.properties");Properties props = new Properties();props.load(is);out.println("数据库URL:" + props.getProperty("db.url") + "<br>");// 4. 获取Web应用信息out.println("<h3>Web应用信息:</h3>");out.println("上下文路径:" + context.getContextPath() + "<br>");out.println("服务器信息:" + context.getServerInfo() + "<br>");}}
对应的 web.xml 配置:
<!-- Web应用初始化参数 --><context-param><param-name>appName</param-name><param-value>MyJavaWebApp</param-value></context-param><context-param><param-name>appVersion</param-name><param-value>1.0.0</param-value></context-param>
七
线程安全问题
线程安全问题示例
public class UnsafeServlet extends HttpServlet {// 实例变量(多线程共享)private int count = 0;protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();// 自增操作(非原子操作,存在线程安全问题)count++;// 模拟耗时操作try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}out.println("访问次数:" + count);}}
解决线程安全问题的方法
public class SafeServlet extends HttpServlet {private int count = 0;protected void doGet(HttpServletRequest req, HttpServletResponse res)throws IOException {res.setContentType("text/html;charset=UTF-8");PrintWriter out = res.getWriter();// 同步代码块(使用当前Servlet实例作为锁)synchronized (this) {count++;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}out.println("访问次数:" + count);}}}
1.实现 SingleThreadModel 接口(不推荐):
1.让 Servlet 每次处理请求时创建新实例(破坏单例)
2.已被 Servlet 规范标记为过时(deprecated)
最佳实践:
尽量避免在 Servlet 中使用实例变量
必须使用共享变量时,通过synchronized保证线程安全
考虑使用线程安全的集合(如ConcurrentHashMap)
八
知识点回顾
1.Servlet 基础:运行在服务器端的 Java 程序,处理 HTTP 请求和响应
2.生命周期:实例化→初始化(init())→处理请求(service())→销毁(destroy())
3.编写方式:继承HttpServlet(推荐),重写doGet()、doPost()等方法
4.配置方式:@WebServlet注解(Servlet 3.0+)或web.xml
5.核心对象:
1.HttpServletRequest:获取请求信息、参数
2.HttpServletResponse:生成响应、页面跳转
3.ServletContext:全局上下文,共享数据
6.页面跳转:请求转发(服务器端)和重定向(客户端)
7.线程安全:Servlet 默认单例,需注意多线程并发问题