java-Web基础之Servlet

努力有时候也战胜不了天分,但至少能让别人看得起你。

——《七龙珠》

手动创建一个Maven项目

由于要学习Java中的Servlet,Filter,Listener等组件需要下载依赖,所以我们需要在构建工具中进行演示和学习。本文使用Maven项目作为示例。

创建文件夹结构

1
2
mkdir -p myapp/src/main/java	//创建文件结构
cd myapp //进入项目

手动创建一个简单的 pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 文件: pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>myapp</artifactId>
<version>1.0-SNAPSHOT</version>
</project>

这样我们就创建好了一个最简单maven项目。

Servlet

Servlet 是JavaWeb技术的核心组件,本质上就是运行在服务器端的小程序,用于处理客户端(如浏览器)的请求,生成动态响应。Servlet主要用于处理Http请求,实现动态网页内容生成。

Servlet 生命周期

Servlet的生命周期由Servlet容器(如Tomcat)管理,主要有以下几个阶段:

Servlet容器:Web服务器,用于接收HTTP请求并调用你写的Servlet 代码处理请求。

初始化 init() –只调用一次

  • 在Servlet第一次被访问时,容器会创建一个Servlet实例,然后调用它的 init()方法。
  • 通常用来做资源的初始化操作、比如数据库连接、读取配置文件等。
1
2
3
4
@Override
public void init() throws ServletException {
System.out.println("Servlet 初始化:init()");
}

注:只有一个实例,init()只调用一次

请求处理 service()–每次请求都会调用

  • 每次有客户端请求Servlet,Tomcat就会调用Servlet实例的 service() 方法
  • service() 实例会自动判断请求类型(GET/POST),然后分发给对应的doGet() 或 doPost()
1
2
3
4
5
6
7
8
9
10
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("处理 GET 请求");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("处理 POST 请求");
}

注:此处我们先暂时了解大概流程,无需关心代码实际实现

销毁 destroy()–只调用一次

  • 当Tomcat关闭、或Servlet被卸载(如热更新时),容器会调用 destroy()方法
  • 通常用来释放资源,如关闭数据库连接,停止线程等。
1
2
3
4
@Override
public void destroy() {
System.out.println("Servlet 被销毁:destroy()");
}

补充说明

  1. 一个Servlet类只被实例化一次(单例),多个请求公用一个实例,线程不安全。
  2. Servlet生命周期由容器自动管理,你无需手动调用这些方法
  3. SpringMVC的低层DispatchServlet也是一个Servlet,生命周期和你写的一样。

部署Tomcat

Apache 基金会开发的免费、开源、轻量级 Java Web 容器 ,支持 Servlet 和 JSP 规范

  • SpringBoot + SpringMVC 中内置Tomcat,无需配置容器,直接Main方法运行。

首先我们在官网下载安装TomCat:Apache Tomcat® - Welcome!

安装后记住安装路径,后面要用。

然后我们在VScode中安装扩展:

1747657005375

这个扩展帮助我们管理和启动Tomcat。

安装完成后,我们在VScode的侧边栏中的SERVICE中找到这个扩展(注意在资源管理器栏的最下面):

1747657081823

我们右键这个服务选择:Creat New Server

现在我们查看VScode的最上面,询问我们是否需要下载server,由于我们之前已经手动下载过Tomcat,所以选择No。

然后我们在弹出的窗口中选择之前安装的Tomcat的文件夹,来到这个页面:

1747657319639

不用更改配置,直接滑倒最下面点击Finish即可。

现在我们看到扩展中已经托管了Tomcat:

1747657387304

现在我们创建项目结构如下:

1
2
3
4
5
6
7
servlet-lifecycle-demo/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/LifecycleServlet.java

pom.xml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>servlet-lifecycle-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<finalName>servlet-lifecycle-demo</finalName>
<plugins>
<!-- Maven war 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>

LifecycleServlet.java 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/life")
public class LifecycleServlet extends HttpServlet {

public LifecycleServlet() {
System.out.println("🚀 构造方法:Servlet 实例被创建");
}

@Override
public void init() throws ServletException {
System.out.println("🔧 init():Servlet 被初始化");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("📥 doGet():处理 GET 请求");
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("<h2>你好,这是 Servlet 生命周期演示</h2>");
}

@Override
public void destroy() {
System.out.println("🧹 destroy():Servlet 被销毁");
}
}

然后我们在终端中输入:

1
mvn clean package

好,现在我们只需要右键Tomcat选择:add Deployment

1747657850124

然后选择File:

1747657911662

选择我们刚刚编译好的wer文件:

1747658001528

最后选择No:

1747657966359

我们就将刚刚编写好的Servlet加入了Tomcat容器中。

右键Tomcat服务选择Start Server,服务开始运行。此时打印的信息只有Tomcat的启动信息,Servlet还没有被创建。

1747658502463

然后我们浏览器访问:http://localhost:8080/servlet-lifecycle-demo/life

看到servlet实例被创建,然后调用了一次GET请求,

1747658529418

之后再次访问页面,也不会在创建新的Servlet实例,而是一直调用同一个实例的doGet方法

1747658627861

最后我们Stop Server:

1747658665412

我们可以在日志中找到Servlet销毁函数调用是打印的日志。

Servlet Mapping

Servlet的作用是将客户端发送的请求URL路径与特定的Servlet 类关联起来。当接收到符合某个映射规则的请求时,它会将请求交给对应的Servlet来处理。

使用 @WebServlet注解配置映射

在我们上面的代码中,我们使用

1
2
@WebServlet("/life")
public class LifecycleServlet extends HttpServlet

进行注解配置。这里的”/life” 就是一个URL模式(URL pattern), 当用户访问路径 /life 时,Tomcat就会将这个请求交给 LifecycleServlet的实例来处理。

  • 多个URL模式:可以为一个Servlet映射多个URL模式

    1
    @WebServlet({"/life","/mylife"})

    这样当访问多个路径中的任意一个路径时,都会交给 LifecycleServlet实例进行处理

  • 指定 name :可以为Servlet指定一个名称

    1
    2
    3
    4
    @WebServlet(name = "lifecycleServlet", urlPatterns = {"/life"})
    public class LifecycleServlet extends HttpServlet {
    // ... your code ...
    }

    在注解映射时,name属性通常不是必须的,但是在web.xml中,servlet-name属性是必须的,所以这里介绍一下。

使用 web.xml配置映射(传统方式)

在web应用的 WEB-INF目录下,会有一个web.xml文件。我们也可以通过这个文件进行Servlet配置和Servlet映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<servlet>
<servlet-name>lifecycleServlet</servlet-name>
<servlet-class>com.example.LifecycleServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>lifecycleServlet</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>

</web-app>
  • <servlet> 用于声明一个Servlet,通过 <servlet-name>指定名称(必须),<servlet-class>指定Servlet类的完整限定名。
  • <servlet-mapping>元素用于将一个Servlet名称 <servlet-name>映射到一个URL模式 <url-pattern>

URL模式的类型

  • 精确匹配:以 /开头,例如 /life。只用当请求的路径与此路径完全匹配时,Servlet才会被调用。
  • 路径匹配:以 /开头,以 /*结尾,例如 /admin/*。任何以 /admin/ 开头的请求都会被匹配到这个Servlet。
  • 扩展名匹配:以 *. 开头,例如 *.do。任何以 .do结尾的请求都会被匹配到这个Servlet
  • 默认Servlet:只有一个 /作为URL模式,他会处理所有其他Servlet映射都无法匹配的请求。

Servlet特性

Servlet的线程安全性:

  • Servlet实例是单例的,但是对于每个请求,Servlet容器都会创建一个新的线程来执行Servlet的 service()方法(doGet()doPost())
  • Servlet需要考虑线程安全性。避免在Servlet的实例变量中存储共享的可变状态,或者采取同步措施来保护共享资源。

Servlet的配置(ServletConfig):

  • ServletConfig 对象代表Servlet的配置信息,每个servlet都有自己的ServletConfig对象
  • 我们可以在 web.xml (或使用 @WebInitParam 注解)为Servlet配置初始化参数,并通过 ServletConfiggetInitParameter(String name) 方法获取。

一个小DEMO

  • 使用ServletConfig获取初始化参数:
1
2
3
4
5
6
7
8
<servlet>
<servlet-name>helloConfig</servlet-name>
<servlet-class>com.example.HelloConfig</servlet-class>
<init-param>
<param-name>exampleParam</param-name>
<param-value>Hello from config!</param-value>
</init-param>
</servlet>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloConfig extends HttpServlet {

private String exampleParam;

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
exampleParam = config.getInitParameter("exampleParam");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("<h2>" + exampleParam + "</h2>");
}

}
  • 使用 @WebInitParam 注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebServlet(urlPatterns = "/hello")
@WebInitParam(name = "exampleParam", value = "Hello from annotation!")
public class HelloAnnotation extends HttpServlet {
private String exampleParam;

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
exampleParam= config.getInitParameter("exampleParam");
System.out.println("Greeting from annotation: " + exampleParam);
}

// ... doGet method ...
}

无论使用注解还是web.xml在Servlet类中获取初始化参数的方法都一样。只是注册参数的方法不同。

Servlet API详解

Servlet API 是构建Java Web 应用的核心。它定义了Servlet容器和你的Servlet之间的契约。

HttpServletRequest 接口

HttpServletRequest 对象代表了客户端发送给Servlet的HTTP请求。通过这个对象,你可以获取到请求的所有信息。

常用方法:

获取参数时,如果没有设置该参数,则会得到 null

  • getParameter(String name): 获取指定名称的请求参数的值(返回 String)。如果参数不存在或有多个同名参数,行为可能不确定。
  • getParameterValues(String name): 获取指定名称的所有请求参数的值(返回 String[])。
  • getParameterMap(): 获取所有请求参数的 Map<String, String[]>
  • getHeader(String name): 获取指定名称的请求头的值(返回 String)。
  • getHeaders(String name): 获取指定名称的所有请求头的值(返回 Enumeration<String>)。
  • getHeaderNames(): 获取所有请求头的名称(返回 Enumeration<String>)。
  • getMethod(): 获取请求的 HTTP 方法(例如:”GET”, “POST”)。
  • getRequestURI(): 获取请求的 URI。
  • getContextPath(): 获取 Web 应用的上下文路径。
  • getServletPath(): 获取 Servlet 的路径。
  • getQueryString(): 获取查询字符串(URL 中 ? 后面的部分)。
  • getRemoteAddr(): 获取发送请求的客户端的 IP 地址。
  • getRemoteHost(): 获取发送请求的客户端的主机名。
  • getSession(): 返回当前请求关联的 HttpSession,如果不存在则创建一个新的。
  • getSession(boolean create): 返回当前请求关联的 HttpSession,如果 createtrue 且不存在则创建一个新的,否则返回 null
  • setAttribute()HttpServletRequest 的属性只在当前请求的处理过程中有效,当该请求的响应发送回客户端后,这些属性就不再需要了
  • getAttribute():获取设置的属性。

练习1:获取get请求中的参数 name=ulna&age=20

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.example;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import javax.servlet.ServletConfig;

import java.util.Enumeration;
import java.util.Map;


@WebServlet(name = "Test1", urlPatterns = {"/test1"})
public class HttpServletRequestTest1 extends HttpServlet {


@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取请求参数的最佳实践

//直接获取参数
String name = req.getParameter("name");
String age = req.getParameter("age"); //缺少参数获取值null

// 通过Map方法获取
Map <String, String[]> parameterMap = req.getParameterMap();

String nameMap = parameterMap.get("name")[0];
//String ageMap = parameterMap.get("age")[0]; //如果少参数会报错
String ageMap = null; //使用Map在获取值之前需要判断是否存在
if (parameterMap.containsKey("age")) {
ageMap = parameterMap.get("age")[0];
}

// 获取请求头的最佳实践
// 获取所有请求头名称
Enumeration<String> headerNames = req.getHeaderNames(); //获取所有请求头名称


// 只获取想要的请求头
String userAgent = req.getHeader("user-agent"); //获取User-Agent请求头
System.out.println("User-Agent: " + userAgent);

// 设置响应内容类型
resp.setContentType("text/html;charset=UTF-8");

// 输出响应内容
resp.getWriter().println("<html><body>");
resp.getWriter().println("<h1>Request Method: " + req.getMethod() + "</h1>");
resp.getWriter().println("<h1>Received Parameters</h1>");
resp.getWriter().println("<p>Name: " + name + "</p>");
resp.getWriter().println("<p>Age: " + age + "</p>");
resp.getWriter().println("<h2>Using Map Method</h2>");
resp.getWriter().println("<p>Name from Map: " + nameMap + "</p>");
resp.getWriter().println("<p>Age from Map: " + ageMap + "</p>");
resp.getWriter().println("<h2>Request Headers</h2>");
while (headerNames.hasMoreElements()) { //像迭代器一样访问请求头
String headerName = headerNames.nextElement();
//可以在这里对请求头进行处理
String headerValue = req.getHeader(headerName);
resp.getWriter().println("<p>" + headerName + ": " + headerValue + "</p>");
}
// 输出上下文路径
resp.getWriter().println("<h1>Context Path: " + req.getContextPath() + "</h1>"); //当前应用的上下文路径
// 输出Servlet路径
resp.getWriter().println("<h1>Servlet Path: " + req.getServletPath() + "</h1>"); //当前Servlet实例的路径
// 实际访问的路径 = 上下文路径 + Servlet路径

resp.getWriter().println("</body></html>");
}

}

HttpServletResponse 接口

HttpServletResponse 对象代表了Servlet发送给客户端的Http响应。通过这个对象,我们可以设置响应的内容状态码响应头等信息。

常用方法:

  • getWriter(): 返回一个 PrintWriter 对象,你可以使用它向客户端发送文本数据。
  • getOutputStream(): 返回一个 ServletOutputStream 对象,你可以使用它向客户端发送二进制数据(例如:图片、文件)。
  • setContentType(String type): 设置响应的内容类型(例如:”text/html”, “application/json”)。同时也可以设置字符编码,例如 "text/html;charset=UTF-8"
  • setStatus(int sc): 设置响应的状态码(例如:200, 404, 500)。
  • sendError(int sc, String msg): 发送一个错误响应到客户端,并设置状态码和错误消息。
  • sendRedirect(String location): 发送一个临时的重定向响应,告诉客户端浏览器去访问另一个 URL。
  • setHeader(String name, String value): 设置指定的响应头。
  • addHeader(String name, String value): 添加指定的响应头(允许有多个同名 header)。
  • setDateHeader(String name, long date): 设置日期格式的响应头。
  • setIntHeader(String name, int value): 设置整数格式的响应头。
  • setContentLength(int len): 设置响应内容的长度。

练习2:创建一个Servlet,当通过访问特定的URL时如:/redirect-me , 他会使用sendRedirect() 方法将客户端重定向到另一个你指定的URL例如:https://www.google.com

1
2
3
4
5
6
7
8
9
10
@WebServlet(name = "Redirect", urlPatterns = {"/redirect-me"})
public class HttpServletRequestTest1 extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.sendRedirect("https://www.google.com");
}

}

ServletConfig 接口

每个Servlet都有一个对应的 ServletConfig 对象,Servlet 容器在构建Servlet时会创建这个对象,并传递给Servlet的 init(ServletConfig config)方法。ServletConfig 接口允许你访问Servlet的配置信息。

注册初始参数:

只有一个参数时:通常可以这样简化写

1
2
3
@WebServlet(name = "ConfigTest", urlPatterns = {"/configTest"})
@WebInitParam(name = "param1", value = "value1")
public class ConfigTest extends HttpServlet

多个参数时:规范写法

1
2
3
4
5
6
7
@WebServlet(name = "ConfigTest",
urlPatterns = {"/configTest"},
initParams = {
@WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2"),
})
public class ConfigTest extends HttpServlet

常用方法:

  • getServletName(): 返回 Servlet 的名称,通常是在 web.xml 或使用 @WebServlet 注解时指定的。
  • getInitParameter(String name): 返回 Servlet 初始化参数中指定名称的值(返回 String)。这些参数可以在 web.xml 中通过 <init-param> 标签配置,或者在使用注解时通过 @WebInitParam 配置。
  • getInitParameterNames(): 返回包含所有 Servlet 初始化参数名称的 Enumeration<String>.
  • getServletContext(): 返回当前 Web 应用的 ServletContext 对象。

练习3:创建一个Servlet,通过 @WebInitPara 注解配置至少两个初始化参数。在Servlet的 doGet() 方法中获取并显示这些初始化参数的值以及servlet的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@WebServlet(name = "ConfigTest",
urlPatterns = {"/configTest"},
initParams = {
@WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2"),
})
public class ConfigTest extends HttpServlet {
private String param1;
private String param2;

@Override
public void init(ServletConfig config) throws ServletException{
super.init(config);
param1 = config.getInitParameter("param1");
param2 = config.getInitParameter("param2");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

resp.setContentType("text/plain");
resp.getWriter().println("Servlet Name: " + getServletConfig().getServletName()); //servlet中任何地方可以使用getServletConfig()获取该Servlet的Config
resp.getWriter().println("Param1: " + param1);
resp.getWriter().println("Param2: " + param2);
}

}

ServletContext 接口

ServletContext 接口代表了当前的Web应用程序。它是整个Web应用共享的上下文,所有的Servlet、Filter、Listener 等都可以访问同一个 ServletContext对象。Servlet 容器在启动Web应用时创建 ServletContext对象。

常用方法:

  • getAttribute(String name): 返回指定名称的应用程序级别的属性值(返回 Object)。
  • setAttribute(String name, Object object): 设置应用程序级别的属性/对象
  • removeAttribute(String name): 移除指定名称的应用程序级别的属性。
  • getInitParameter(String name): 返回 Web 应用的上下文初始化参数中指定名称的值(返回 String)。这些参数在 web.xml<context-param> 标签中配置。
  • getInitParameterNames(): 返回包含所有 Web 应用上下文初始化参数名称的 Enumeration<String>.
  • getResource(String path): 返回指定路径的资源 URL。路径以 / 开头,相对于 Web 应用的根目录。
  • getResourceAsStream(String path): 返回指定路径的资源输入流。
  • getContextPath(): 返回 Web 应用的上下文路径。
  • getServletContextName(): 返回 Web 应用的名称(如果在部署描述符中指定了)。
  • log(String msg): 将消息写入 Servlet 容器的日志。

练习4:

  1. 在你的web应用的web.xml文件种配置至少一个 <context-param>
  2. 创建一个Servlet,该Servlet 获取并显示这个上下文参数的值。
  3. 在该Servlet中,设置一个ServletContext属性,并在同一个Servlet中获取并显示这个属性的值。

web.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">

<context-param>
<param-name>appName</param-name>
<param-value>LearnServlet</param-value>
</context-param>
<context-param>
<param-name>blogName</param-name>
<param-value>https://blog.ulna520.top</param-value>
</context-param>
</web-app>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@WebServlet(name = "ContextTest",
urlPatterns = {"/contextTest"}
)
public class ContextTest extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

ServletContext context = getServletContext();

String appName = context.getInitParameter("appName");
String blogName = context.getInitParameter("blogName");
String contextPath = context.getContextPath();
String ServletContextName = context.getServletContextName(); //未设置得到null

Map<String,Integer> map = new HashMap<>();
map.put("appName", 1);
map.put("blogName", 2);
map.put("contextPath", 3);
map.put("ServletContextName", 4);
context.setAttribute("ServletContext", map); //可以设置Object对象,以供全局访问一个对象


resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("<html><body>");
resp.getWriter().println("<h1>Context Parameters</h1>");
resp.getWriter().println("<p>Application Name: " + appName + "</p>");
resp.getWriter().println("<p>Blog Name: " + blogName + "</p>");
resp.getWriter().println("<p>Context Path: " + contextPath + "</p>");
resp.getWriter().println("<p>Servlet Context Name: " + ServletContextName + "</p>");
resp.getWriter().println("<h2>Context Attributes</h2>");
resp.getWriter().println("<ul>");
Map<String, Integer> contextMap = (Map<String, Integer>) context.getAttribute("ServletContext");
for (Map.Entry<String, Integer> entry : contextMap.entrySet()) {
resp.getWriter().println("<li>" + entry.getKey() + ": " + entry.getValue() + "</li>");
}
resp.getWriter().println("</ul>");
resp.getWriter().println("</body></html>");
}

}

Servlet 请求转发与重定向

请求转发

请求转发是指将当前请求的处理交给web服务器上的另一个资源来完成,这个过程发生在服务器内部,客户端浏览器只发送一次请求(与重定向的一个关键不同)

实现方式:

通过 RequestDispatcher 接口来实现请求转发。我们可以通过 HttpServletRequest 对象的 getRequestDispatcher(String path) 方法获取一个 RequestDispatcher对象。path 可以是相对于当前Servlet上下文路径

RequestDispatcher 接口提供了两个主要方法用于转发:

  • forword(ServletRequest request, ServletResponse response):将请求转发给另一个资源。
    • 注意:在转发后,forword()后的代码有可能会执行。但是在调用之后,当前Servlet不应该再向客户端发送任何响应。最佳实践是 forwrod()后就立即结束当前方法。
  • include(ServletRequest request, ServletResponse response):将另一个资源的内容包含到当前响应中。执行流程会回到当前Servlet继续处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet("/first")
public class FirstServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("message", "Hello from FirstServlet");
RequestDispatcher dispatcher = request.getRequestDispatcher("/second");
System.out.println("FirstServlet: Forwarding request to /second");
dispatcher.forward(request, response);
System.out.println("FirstServlet: After forwarding"); // 这行代码可能不会立即执行,取决于 SecondServlet 的处理
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebServlet("/second")
public class SecondServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String message = (String) request.getAttribute("message");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html><head><title>Second Servlet</title></head><body>");
out.println("<h1>Second Servlet 处理请求</h1>");
if (message != null) {
out.println("<p>Message from previous servlet: " + message + "</p>");
} else {
out.println("<p>No message passed.</p>");
}
out.println("</body></html>");
System.out.println("SecondServlet: Request processed.");
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

请求重定向

请求重定向是指服务器告诉客户端浏览器发送一个新的HTTP请求到另一个URL。这涉及到两次HTTP请求-响应周期。

实现方式:

通过 HttpServletResponse 对象的 sendRedirect(String location)方法来发送重定向响应。

location 是客户端浏览器需要重新访问的URL。

1
2
3
4
5
6
7
8
9
10
11
12
@WebServlet("/redirect-google")
public class RedirectServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("RedirectServlet: Received request, redirecting to Google.");
response.sendRedirect("https://www.google.com");
System.out.println("RedirectServlet: After sending redirect.");
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

java-Web基础之Servlet
http://blog.ulna520.com/2025/05/19/java-Web基础_20250519_165356/
Veröffentlicht am
May 19, 2025
Urheberrechtshinweis