Servlet
Servlet是Sun公司开发的一种动态Web技术
Servlet是一系列API中的一个,实现该接口并部署到Web服务器即可
实现了Servlet接口的程序叫Servlet应用
模块创建
Idea->新建->模块->MavenArchetype
含义 |
项目名称,即工件ID | 保存路径 | JDK版本 | 模块所属项目 | 模板位置 | 模板 |
内容 |
自填 | 自选 | 1.8 | JavaWeb | 内部 | maven-archetype-webapp |
示例 |
servlet | D:\Code\Java | jdk1.8.0_202 | 之前创建的项目 | 内部 | maven-archetype-webapp |
载入依赖
serlet模块下的pom.xml文件(载入后记得右键->Maven->同步项目),后续操作均在模块内
<!-- servlet 依赖库 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<!--
tomcat 等 servlet 容器已经内置了该依赖库
因此设置 provided, 声明其由容器提供
-->
<scope>provided</scope>
</dependency>
新建Java目录
JavaWeb/servlet/src/main->右键->新建目录java
如果其不是java源代码根目录则右键该目录->将目录标记为->源代码根目录
运行调试配置->编辑配置->Tomcat 服务器 -> 本地
含义 |
配置名称 | Tomcat服务器 | 设置控制台输出编码格式,避免乱码 | 启动Tomcat时要部署的模块 |
内容 |
自填 | 配置->新建->配置可百度 | -Dfile.encoding=UTF-8 | servlet |
示例 |
servlet | Tomcat 9.0.100 | -Dfile.encoding=UTF-8 | 之前创建的模块 |
非Ultra版限制较多(建议试用Ultra,可试用1个月),不支持Tomcat配置,需安装SmartTomcat插件
入门
Demo01
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Demo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// req.getRequestURL().toString() 用于获取请求的URL路径的字符串
System.out.println("demo01: " + "这是第一个 Servlet, 证明你正式开始学习! " + "当前访问路径为: " + req.getRequestURL().toString());
}
}
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/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<!-- demo01 -->
<!-- Servlet 注册 -->
<servlet>
<servlet-name>demo01</servlet-name>
<servlet-class>xyz.ssydx.servlet.Demo01</servlet-class>
</servlet>
<!--
Servlet 映射
映射是自上而下匹配的,一旦找到匹配项就不再继续进行下去
-->
<!-- 将 Servlet 和单个路径映射 -->
<servlet-mapping>
<servlet-name>demo01</servlet-name>
<url-pattern>/demo01</url-pattern>
</servlet-mapping>
<!-- 将 Servlet 和多个路径映射 -->
<servlet-mapping>
<servlet-name>demo01</servlet-name>
<url-pattern>/demo01_1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>demo01</servlet-name>
<url-pattern>/demo01_2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>demo01</servlet-name>
<url-pattern>/demo01_3</url-pattern>
</servlet-mapping>
<!-- 将 Servlet 和通用路径映射 -->
<servlet-mapping>
<servlet-name>demo01</servlet-name>
<url-pattern>/demo01/*</url-pattern>
</servlet-mapping>
<!--
将 Servlet 和指定后缀路径映射
不能*suffix
不能 /*.suffix这种形式
也不能/your_path/*.suffix这种形式
-->
<servlet-mapping>
<servlet-name>demo01</servlet-name>
<url-pattern>*.demo01</url-pattern>
</servlet-mapping>
<!--
将 Servlet 和所有路径映射, 基本不会使用,会覆盖所有路径
初期可以用来把所有未映射路径统一处理映射到指定的 Servlet 上, 避免404错误
注意: 如果采用此策略必须确保该映射位于末尾
-->
<!-- <servlet-mapping>-->
<!-- <servlet-name>demo01</servlet-name>-->
<!-- <url-pattern>/*</url-pattern>-->
<!-- </servlet-mapping>-->
</web-app>
访问验证
# 启动应用
# 在浏览器中依次访问:
http://localhost:your_port/app_name/demo01
http://localhost:your_port/app_name/demo01_1
http://localhost:your_port/app_name/demo01/01
http://localhost:your_port/app_name/01.demo01
# IDE终端依次输出
demo01: 这是第一个 Servlet, 证明你正式开始学习! 当前访问路径为: http://localhost:your_port/app_name/demo01
demo01: 这是第一个 Servlet, 证明你正式开始学习! 当前访问路径为: http://localhost:your_port/app_name/demo01_1
demo01: 这是第一个 Servlet, 证明你正式开始学习! 当前访问路径为: http://localhost:your_port/app_name/demo01/01
demo01: 这是第一个 Servlet, 证明你正式开始学习! 当前访问路径为: http://localhost:your_port/app_name/01.demo01
映射总结
精确路径 | /demo01 |
/demo01 |
只要能匹配就立即使用 |
多路径绑定 | /demo01_1 |
/demo01_1 |
实际仍是精确匹配 |
通配符前缀 | /demo01/* |
/demo01/01 , /demo01/abc |
路径前缀匹配 |
后缀匹配 | *.demo01 |
01.demo01 , hello.world.demo01 |
后缀匹配 |
任意路径 | /* |
所有路径 | 最宽泛的匹配,不推荐使用 |
ServletContext
初始化参数
demo02
package xyz.ssydx.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Demo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 ServletContext 对象, 这是当前 Web 应用的上下文对象
ServletContext context = this.getServletContext();
// 也可以通过代码设置应用的初始化参数, 但通常不会生效, Servlet 容器会阻止该操作
// context.setInitParameter("datasource_url", "jdbc:mysql:/localhost:3306/java_web");
// 获取指定的初始化参数
String datasourceUrl = context.getInitParameter("datasource_url");
System.out.println("demo02: " + "datasource_url: " + datasourceUrl);
// 获取 ServletConfig 对象, 这是当前 Servlet 的配置对象
ServletConfig config = this.getServletConfig();
// 不支持代码设置
// 获取指定的初始化参数, 此处 redisUrl 为 null, 因为当前 Servlet 并未定义这个初始化参数
String redisUrl = config.getInitParameter("redis_url");
System.out.println("demo02: " + "redis_url: " + redisUrl);
}
}
demo03
package xyz.ssydx.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Demo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 ServletContext 对象, 这是当前 Web 应用的上下文对象
ServletContext context = this.getServletContext();
// 也可以通过代码设置应用的初始化参数, 但通常不会生效, Servlet 容器会阻止该操作
// context.setInitParameter("datasource_url", "jdbc:mysql:/localhost:3306/java_web");
// 获取指定的初始化参数
String datasourceUrl = context.getInitParameter("datasource_url");
System.out.println("demo03: " + "datasource_url: " + datasourceUrl);
// 获取 ServletConfig 对象, 这是当前 Servlet 的配置对象
ServletConfig config = this.getServletConfig();
// 不支持代码设置
// 获取指定的初始化参数, 此处 redisUrl 为 null, 因为当前 Servlet 并未定义这个初始化参数
String redisUrl = config.getInitParameter("redis_url");
System.out.println("demo03: " + "redis_url: " + redisUrl);
}
}
web.xml
后续若无特殊情况,web.xml中的注册及映射不再单独列出
<!-- demo02 demo03 -->
<!-- 设置当前 Web 应用的初始化参数, 应用内任何 Servlet 均可通过 ServletContext 进行访问 -->
<context-param>
<param-name>datasource_url</param-name>
<param-value>jdbc:mysql:/localhost:3306/java_web</param-value>
</context-param>
<!-- demo02 -->
<servlet>
<servlet-name>demo02</servlet-name>
<servlet-class>xyz.ssydx.servlet.Demo02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo02</servlet-name>
<url-pattern>/demo02</url-pattern>
</servlet-mapping>
<!-- demo03 -->
<servlet>
<servlet-name>demo03</servlet-name>
<servlet-class>xyz.ssydx.servlet.Demo03</servlet-class>
<!-- 设置 demo03 这个 Servlet 的初始化参数, 只有 demo03 可通过 ServletConfig 进行访问 -->
<init-param>
<param-name>redis_url</param-name>
<param-value>localhost:6379</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>demo03</servlet-name>
<url-pattern>/demo03</url-pattern>
</servlet-mapping>
访问验证
# 启动应用
# 在浏览器中依次访问:
http://localhost:your_port/app_name/demo02
http://localhost:your_port/app_name/demo03
# IDE终端依次输出
demo02: datasource_url: jdbc:mysql:/localhost:3306/java_web
demo02: redis_url: null
demo03: datasource_url: jdbc:mysql:/localhost:3306/java_web
demo03: redis_url: localhost:6379
初始化参数总结
应用级初始化参数 | <context-param> |
ServletContext |
getInitParameter() |
整个 Web 应用 | 是 |
Servlet 级初始化参数 | <init-param> |
ServletConfig |
getInitParameter() |
当前 Servlet | 否 |
请求转发
demo04
package xyz.ssydx.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Demo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 获取请求转发对象, 参数为要转发到的路径
RequestDispatcher dispatcher = context.getRequestDispatcher("/demo01");
// 进行转发
dispatcher.forward(req, resp);
// 拓展知识
ServletConfig config = this.getServletConfig();
String servletName = config.getServletName();
System.out.println("demo04: " + "servletName: " + servletName);
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo04
# IDE终端输出
demo01: 这是第一个 Servlet, 证明你正式开始学习! 当前访问路径为: http://localhost:8099/servlet_war/demo01
demo04: servletName: demo04
重定向见后文
资源读取
demo05
package xyz.ssydx.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
public class Demo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 测试1
try (
// 获取资源的字节输入流, 资源虽然被放到src/resources目录下, 但构建后会放到/WEB-INF/classes下
InputStream is = context.getResourceAsStream("/WEB-INF/classes/test.txt");
// 以 UTF-8 格式转为字符输入流
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
// 创建缓冲区字符输入流以提供读取效率(此处也可不考虑, 毕竟内容很少)
BufferedReader br = new BufferedReader(isr)
) {
// 按行读取
String line;
while ((line = br.readLine()) != null) {
System.out.println("demo05: " + line);
}
}
// 测试2
try (InputStream is = context.getResourceAsStream("/WEB-INF/classes/datasource.properties")) {
// 创建属性对象
Properties properties = new Properties();
// 从指定输入流中载入属性
properties.load(is);
// 获取属性, 第二个参数可指定属性不存在的默认值
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String url = properties.getProperty("url", "jdbc:mysql://localhost:3306/java_web");
System.out.println("demo05: " + "username: " + username);
System.out.println("demo05: " + "password: " + password);
System.out.println("demo05: " + "url: " + url);
}
}
}
test.txt
放到src/resources目录
hello 😀
该文档仅用于资源载入的测试!
datasource.properties
放到src/resources目录
username=ssydx
password=123456
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo05
# IDE终端输出
demo05: hello 😀
demo05: 该文档仅用于资源载入的测试!
demo05: username: ssydx
demo05: password: 123456
demo05: url: jdbc:mysql://localhost:3306/java_web
数据共享
demo06
package xyz.ssydx.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class Demo06 extends HttpServlet {
// 内部类
public class People {
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "People [name=" + name + ", age=" + age + "]";
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 设置属性
context.setAttribute("msg", "Hello World");
List<String> list = new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
context.setAttribute("list", list);
context.setAttribute("people", new People("ssydx", 18));
System.out.println("demo06: " + "属性设置成功");
}
}
demo07
package xyz.ssydx.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
public class Demo07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 获取属性
String msg = (String)context.getAttribute("msg");
System.out.println("demo07: " + msg);
List<String> list = (List<String>)context.getAttribute("list");
System.out.println("demo07: " + list);
Demo06.People people = (Demo06.People)context.getAttribute("people");
System.out.println("demo07: " + people);
}
}
访问验证
# 启动应用
# 在浏览器中依次访问:
http://localhost:your_port/app_name/demo06
http://localhost:your_port/app_name/demo07
# IDE终端依次输出
demo06: 属性设置成功
demo07: Hello World
demo07: [zhangsan, lisi, wangwu]
demo07: People [name=ssydx, age=18]
HttpServletResponse
编解码设置
demo08
package xyz.ssydx.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class Demo08 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 仅指定响应的输出流的编码方式是不够的, 虽然编码正确但客户端(浏览器)不知道如何解码, 也不知道如何显示
// resp.setCharacterEncoding("UTF-8");
/*
仅指定响应的内容类型(显示为 plain 文档, 按 UTF-8 进行解码)是可行的
虽然未指定响应输出流的编码方式, 但 Servlet 容器(Tomcat)会隐式根据响应内容类型中的解码进行响应输出流的编码
*/
resp.setContentType("text/plain;charset=UTF-8");
resp.getWriter().write("你好 🌏");
System.out.println("demo08: " + "编解码设置成功");
}
}
访问验证
后续若无特殊情况,访问验证还需关注浏览器渲染结果(注意观察标头),不再单独强调
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo08
# 浏览器页面显示
你好 🌏
# IDE终端输出
demo08: 编解码设置成功
重定向
demo09
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Demo09 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 进行重定向
resp.sendRedirect("demo08");
System.out.println("demo09: " + "重定向成功");
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo09
# IDE终端输出
demo09: 重定向成功
demo08: 编解码设置成功
请求转发见前文
文件下载1
demo10
package xyz.ssydx.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class Demo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 设置响应头,强制浏览器下载该文件
resp.setHeader("Content-Disposition", "attachment; filename=test.txt");
// 获取资源的输入流和响应的输出流
try (
InputStream is = context.getResourceAsStream("/WEB-INF/classes/test.txt");
OutputStream os = resp.getOutputStream()
) {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
System.out.println("demo10: " + "文件下载成功");
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo10
# IDE终端输出
demo10: 文件下载成功
test.txt文件见前文
文件下载2
demo11
package xyz.ssydx.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
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.OutputStream;
import java.net.URLEncoder;
public class Demo11 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 设置响应头,强制浏览器下载该文件
resp.setHeader(
"Content-Disposition",
// 中文字符无法直接出现在响应头中, 必须进行编码处理
"attachment; filename=" + URLEncoder.encode("测试.png", "UTF-8")
);
// 获取资源的输入流和响应的输出流
try (
InputStream is = context.getResourceAsStream("/WEB-INF/classes/测试.png");
OutputStream os = resp.getOutputStream()
) {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
System.out.println("demo11: " + "文件下载成功");
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo11
# IDE终端输出
demo11: 文件下载成功
测试.png自行准备并放到src/resouces目录下
验证码
demo12
package xyz.ssydx.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Random;
public class Demo12 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("image/jpeg");
// 设置刷新间隔, 每30秒自动刷新一次
resp.setHeader("refresh", "30");
// 不缓存响应结果, HTTP/1.1
resp.setHeader("Cache-Control", "no-cache");
// 不缓存响应结果, HTTP/1.0
// resp.setDateHeader("expires", -1);
// resp.setHeader("Pragma", "no-cache");
// 生成验证码图片
BufferedImage bufferedImage = new BufferedImage(80,20, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D)bufferedImage.getGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0,0,80,20);
g2d.setColor(Color.BLACK);
g2d.setFont(new Font(null, Font.BOLD, 20));
String captcha = getRand();
g2d.drawString(captcha, 5,20);
// 写入响应的输出流
ImageIO.write(bufferedImage, "jpg", resp.getOutputStream());
System.out.println("demo12: " + "验证码内容: " + captcha + " 获取时间为: " + new Date(System.currentTimeMillis()));
}
/**
* 本方法用于生成随机的6位数字字符串
* @return 返回值为6位的数字字符串
*/
private String getRand() {
Random random = new Random();
String str = random.nextInt(999999) + "";
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 6 - str.length(); i++) {
stringBuilder.append("0");
}
str += stringBuilder.toString();
return str;
}
}
访问验证
# 启动应用
# 在浏览器中访问并等待30秒以上:
http://localhost:your_port/app_name/demo12
# 浏览器页面显示
验证码图片, 每隔30秒自动刷新一次
# IDE终端输出(仅作示例)
demo12: 验证码内容: 998899 获取时间为: Sun May 25 13:35:02 CST 2025
demo12: 验证码内容: 863927 获取时间为: Sun May 25 13:35:32 CST 2025
HttpServletRequest
获取参数
demo13
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class Demo13 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobbies");
System.out.println("demo13: " + "username=" + username + ", password=" + password + ", hobbies=" + Arrays.toString(hobbies));
// 写入响应的输入流
resp.setContentType("text/plain;charset=utf-8");
resp.getWriter().write("username=" + username + ", password=" + password + ", hobbies=" + Arrays.toString(hobbies) + "\n");
// 拓展(请求包含)
this.getServletContext().getRequestDispatcher("/demo08").include(req, resp);
}
}
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="demo13" method="get">
用户名:<input type="text" name="username" id="username"> <br>
密码:<input type="password" name="password" id="password"> <br>
爱好:
<input type="checkbox" name="hobbies" value="小说">小说
<input type="checkbox" name="hobbies" value="电影">电影
<input type="checkbox" name="hobbies" value="manga">漫画
<input type="checkbox" name="hobbies" value="music">音乐
<br>
<button type="submit">提交</button>
</form>
</body>
</html>
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/login.html
# 填写表单并提交
# 浏览器跳转到 http://localhost:your_port/app_name/demo13?username=ssydx&password=123456&hobbies=电影&hobbies=manga
# 浏览器页面显示:
username=ssydx, password=123456, hobbies=[电影, manga]
你好 🌏
# IDE终端输出(仅作示例)
demo13: username=ssydx, password=123456, hobbies=[电影, manga]
demo08: 编解码设置成功
demo08 见前文
获取Cookie
demo14
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
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.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class Demo14 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/plain;charset=utf-8");
PrintWriter writer = resp.getWriter();
// 获取 cookie
Cookie[] cookies = req.getCookies();
Long lastTime = null;
// 遍历是否有指定 cookie
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("lastTime")) {
lastTime = Long.parseLong(cookie.getValue());
break;
}
}
}
// 无则认为第一次访问当前页面, 否则返回 cookie 值
if (lastTime == null) {
System.out.println("demo14: " + "尚未设置 lastTime");
writer.write("首次访问");
} else {
System.out.println("demo14: " + "上次设置 lastTime: " + lastTime);
writer.write("lastTime=" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(lastTime)));
}
// 设置 cookie
Cookie newLastTime = new Cookie("lastTime", System.currentTimeMillis() + "");
// 设置 30 秒后自动过期
newLastTime.setMaxAge(30);
resp.addCookie(newLastTime);
System.out.println("demo14: " + "设置 cookie 成功");
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo14
# 浏览器页面显示:
username=ssydx, password=123456, hobbies=[电影, manga]
首次访问
# 立刻刷新页面:
lastTime=2025-05-26 05:28:51
# 等待30秒以上再次刷新页面:
首次访问
# IDE终端依次输出(仅作示例)
demo14: 尚未设置 lastTime
demo14: 设置 cookie 成功
demo14: 上次设置 lastTime: 1748208531322
demo14: 设置 cookie 成功
demo14: 尚未设置 lastTime
demo14: 设置 cookie 成功
获取Session
web.xml
<!-- demo15 demo16 -->
<!-- 设置 session 的过期时间为1分钟 -->
<session-config>
<session-timeout>1</session-timeout>
</session-config>
<!-- demo15 -->
<servlet>
<servlet-name>demo15</servlet-name>
<servlet-class>xyz.ssydx.servlet.Demo15</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo15</servlet-name>
<url-pattern>/demo15</url-pattern>
</servlet-mapping>
<!-- demo16 -->
<servlet>
<servlet-name>demo16</servlet-name>
<servlet-class>xyz.ssydx.servlet.Demo16</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo16</servlet-name>
<url-pattern>/demo16</url-pattern>
</servlet-mapping>
demo15
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo15 extends HttpServlet {
// 内部类
public class People {
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "People [name=" + name + ", age=" + age + "]";
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/plain;charset=utf-8");
// 获取 session, 策略是无则新建
HttpSession session = req.getSession();
// 在 session 中存入数据
session.setAttribute("name", "ssydx");
session.setAttribute("people", new People("zhangsan", 18));
String id = session.getId();
// 如果是新创建的 session, 输出新建, 否则输出已有
if (session.isNew()) {
System.out.println("demo15: " + "新建session: " + id);
} else {
System.out.println("demo15: " + "已有session: " + id);
}
}
}
demo16
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class Demo16 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/plain;charset=utf-8");
// 获取session并获得其中的值, 无也不新建
HttpSession session = req.getSession(false);
if (session != null) {
String name = (String)session.getAttribute("name");
Demo15.People people = (Demo15.People)session.getAttribute("people");
// 强制使 session 非法(过期)
session.invalidate();
System.out.println("demo16: " + "name=" + name + ", people=" + people);
} else {
System.out.println("demo16: session 不存在");
}
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo15
# 立刻刷新页面
# 随即访问:
http://localhost:your_port/app_name/demo15
# 立刻刷新页面
# IDE终端依次输出(仅作示例)
demo15: 新建session: 5FC33AA983AFB0C18E7B964EA4C2ADAD
demo15: 已有session: 5FC33AA983AFB0C18E7B964EA4C2ADAD
demo16: name=ssydx, people=People [name=zhangsan, age=18]
demo16: session 不存在
# 不妨尝试等待1分钟以上再进行访问, 不难发现 session 也会新建(demo15)或不存在(demo16)
如果启动应用后先访问了调用过 getSession() 的 Servlet(包括任意的jsp页面,例如index.jsp),此时访问demo15会发现session已经存在,因为getSession方法在无参或参数为true时会无则新建,而jsp页面均隐式调用了该方法
不难发现在浏览器的cookie列表中往往存在一个名为jsessionid的cookie,这就是维持会话的标识,它跟会话是同步新建(jsessionid本质是session的id)
WebServlet
后续若无特殊情况,Servlet 均采用注解形式进行注册映射
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/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
<!-- metadata-complete="false" 开启注解扫描 -->
</web-app>
demo17
package xyz.ssydx.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
// 注册并映射, 配置方式类同web.xml
@WebServlet(name = "demo17", urlPatterns = {"/demo17"})
public class Demo17 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig config = this.getServletConfig();
String servletName = config.getServletName();
System.out.println("demo17: " + "servletName: " + servletName);
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo17
# IDE终端输出
demo17: servletName: demo17
Filter
过滤器常用于对 Servlet 进行统一处理,例如:编码设置、权限拦截
web.xml
<!-- filter01 -->
<filter>
<filter-name>filter01</filter-name>
<filter-class>xyz.ssydx.filter.Filter01</filter-class>
</filter>
<filter-mapping>
<filter-name>filter01</filter-name>
<url-pattern>/demo18</url-pattern>
</filter-mapping>
filter01
package xyz.ssydx.filter;
import javax.servlet.*;
import java.io.IOException;
public class Filter01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 应用启动时
System.out.println("Filter01 init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 设置请求的编码格式和响应的内容类型
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/plain;charset=utf-8");
System.out.println("Filter01 doFilter begin");
// 继续执行其他过滤器
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter01 doFilter finish");
}
@Override
public void destroy() {
// 应用关闭时
System.out.println("Filter01 destroy");
}
}
demo18
package xyz.ssydx.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 注册并映射, 配置方式类同web.xml
@WebServlet(name = "demo18", urlPatterns = {"/demo18", "/demo/demo18"})
public class Demo18 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello World 😀");
System.out.println("demo18: " + "响应完成");
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo18
# 浏览器页面显示:
Hello World 😀
# IDE终端输出
Filter01 doFilter begin
demo18: 响应完成
Filter01 doFilter finish
# 在浏览器中访问:
http://localhost:your_port/app_name/demo/demo18
# 浏览器页面显示:
Hello World ?
# IDE终端输出
demo18: 响应完成
单个过滤器按照filter-mapping映射顺序进行映射过滤
多个过滤器同样按照filter-mapping映射顺序进行过滤(不是过滤器注册顺序,而是过滤器映射顺序)
如果同时存在 web.xml注册映射的过滤器 和 注解配置的过滤器,前者总是优先于后者执行
WebFilter
同样支持注解配置
不支持顺序指定,即两个注解配置的过滤器的执行顺序是未知的,如需要按既定顺序执行过滤器则还是采用web.xml进行配置
filter02
package xyz.ssydx.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// 注解配置, 名称为filter02, 按 Servlet 的名称过滤(也可以按路径)
@WebFilter(filterName = "filter02", servletNames = "demo19")
public class Filter02 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 应用启动时
System.out.println("Filter02 init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter02 doFilter begin");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter02 doFilter finish");
}
@Override
public void destroy() {
// 应用关闭时
System.out.println("Filter02 destroy");
}
}
demo19
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 注册并映射, 配置方式类同web.xml
@WebServlet(name = "demo19", urlPatterns = "/demo19")
public class Demo19 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo19: " + "响应完成");
}
}
访问验证
# 启动应用
# 在浏览器中访问:
http://localhost:your_port/app_name/demo19
# IDE终端输出
Filter02 doFilter begin
demo19: 响应完成
Filter02 doFilter finish
Listener
web.xml
<!-- listener01 -->
<!-- <listener>-->
<!-- <listener-class>xyz.ssydx.listener.Listener01</listener-class>-->
<!-- </listener>-->
listener01
package xyz.ssydx.listener;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
// 也可在web.xml中进行注册
@WebListener
public class Listener01 implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("sessionCreated");
ServletContext context = se.getSession().getServletContext();
Integer loginCount = (Integer)context.getAttribute("loginCount");
if (loginCount == null) {
loginCount = 1;
} else {
loginCount++;
}
context.setAttribute("loginCount", loginCount);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("sessionDestroyed");
ServletContext context = se.getSession().getServletContext();
Integer loginCount = (Integer)context.getAttribute("loginCount");
if (loginCount == null) {
loginCount = 0;
} else {
loginCount--;
}
context.setAttribute("loginCount", loginCount);
}
}
demo20
package xyz.ssydx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "demo20", urlPatterns = "/demo20")
public class Demo20 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 触发一次 session 创建
req.getSession();
Integer loginCount = (Integer)this.getServletContext().getAttribute("loginCount");
resp.getWriter().println(loginCount);
System.out.println("demo20: " + "loginCount = " + loginCount);
}
}
访问验证
# 启动应用
# 在浏览器中访问并等待1分钟以上:
http://localhost:your_port/app_name/demo20
# IDE终端输出
sessionCreated
demo20: loginCount = 1
sessionDestroyed
#java##javaweb##servlet#访问demo20会触发session监听器的创建事件,输出结果是1,但由于session有效期在web.xml设置为1分钟(见前文),因此1分钟后会自动触发session监听器的销毁事件
如果想使结果为2,请新建无痕模式窗口(或使用另一浏览器)访问demo20
此专栏由于更新观看不便,不会保持及时更新,最新更新见计算机合集专栏https://www.nowcoder.com/creation/manager/columnDetail/04yp33