Servlet

Servlet是Sun公司开发的一种动态Web技术

Servlet是一系列API中的一个,实现该接口并部署到Web服务器即可

实现了Servlet接口的程序叫Servlet应用

模块创建

Idea->新建->模块->MavenArchetype

选项名称位置JDK父项目录Archetype
含义 项目名称,即工件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

访问demo20会触发session监听器的创建事件,输出结果是1,但由于session有效期在web.xml设置为1分钟(见前文),因此1分钟后会自动触发session监听器的销毁事件

如果想使结果为2,请新建无痕模式窗口(或使用另一浏览器)访问demo20

#java##javaweb##servlet#
JavaWeb 文章被收录于专栏

此专栏由于更新观看不便,不会保持及时更新,最新更新见计算机合集专栏https://www.nowcoder.com/creation/manager/columnDetail/04yp33

全部评论

相关推荐

流浪的神仙:无恶意,算法一般好像都得9硕才能干算法太卷啦
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务