第五章 Servlet

一 Servlet简介

1.1 静态资源和动态资源
静态资源

无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源. 例如:html css js img ,音频文件和视频文件

动态资源

需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,运行时动态生成,例如Servlet,Thymeleaf

生活举例

去蛋糕店买蛋糕

  • 直接买柜台上已经做好的 : 静态资源

  • 和柜员说要求后现场制作 : 动态资源

1.2 Servlet简介
Servlet介绍

Servlet (server applet) 是运行在服务端(tomcat)的Java小程序,是sun公司提供一套定义动态资源规范; 从代码层面上来讲Servlet就是一个接口

  • 在整个Web应用中,Servlet主要负责 接收处理请求协同调度功能以及响应数据。我们可以把Servlet称为Web应用中的控制器
  • 不是所有的JAVA类都能用于处理客户端请求,能处理客户端请求并做出响应的一套技术标准就是Servlet

  • Servlet是运行在服务端的,所以 Servlet必须在WEB项目中开发且在 Tomcat 这样的服务容器中运行

请求 响应与HttpServletRequest和HttpServletResponse之间的对应关系

二 Servlet开发流程

2.1 目标

校验注册时,用户名是否被占用. 通过客户端向一个Servlet发送请求,携带username,如果用户名是已经注册过的 '已注册username' ,则向客户端响应 NO,表示已注册过。如果是其他未注册的,响应YES。

2.2 开发过程
  • 步骤1 开发一个web类型的module
  • 步骤2 开发一个UserServlet

public class UserServlet  extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取请求中的参数
        String username = req.getParameter("username");
        if("zhangsan".equals(username)){
            //通过响应对象响应信息
            resp.getWriter().write("NO");
        }else{
            resp.getWriter().write("YES");
        }

    }
}

注意事项

  • 自定义一个类,要继承HttpServlet类

  • 重写service方法,该方法主要就是用于处理用户请求的服务方法

  • HttpServletRequest 代表请求对象,是有请求报文经过tomcat转换而来的,通过该对象可以获取请求中的信息

  • HttpServletResponse 代表响应对象,该对象会被tomcat转换为响应的报文,通过该对象可以设置响应中的信息

  • Servlet对象的生命周期(创建,初始化,处理服务,销毁)是由tomcat管理的,无需我们自己new

  • HttpServletRequest HttpServletResponse 两个对象也是有tomcat负责转换,在调用service方法时传入给我们用的

  • 步骤3 在web.xml为UseServlet配置请求的映射路径

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <servlet>
        <!--给UserServlet起一个别名-->
        <servlet-name>userServlet</servlet-name>
        <servlet-class>com.alice.servlet.UserServlet</servlet-class>
    </servlet>


    <servlet-mapping>
        <!--关联别名和映射路径-->
        <servlet-name>userServlet</servlet-name>
        <!--可以为一个Servlet匹配多个不同的映射路径,但是不同的Servlet不能使用相同的url-pattern-->
        <url-pattern>/userServlet</url-pattern>
       <!-- <url-pattern>/userServlet2</url-pattern>-->
        <!--
            /        表示通配所有资源,不包括jsp文件
            /*       表示通配所有资源,包括jsp文件
            /a/*     匹配所有以a前缀的映射路径
            *.action 匹配所有以action为后缀的映射路径
        -->
       <!-- <url-pattern>/*</url-pattern>-->
    </servlet-mapping>

</web-app>

  • Servlet并不是文件系统中实际存在的文件或者目录,所以为了能够请求到该资源,我们需要为其配置映射路径

  • servlet的请求映射路径配置在web.xml中

  • servlet-name作为servlet的别名,可以自己随意定义,见名知意就好

  • url-pattern标签用于定义Servlet的请求映射路径

  • 一个servlet可以对应多个不同的url-pattern

  • 多个servlet不能使用相同的url-pattern

  • url-pattern中可以使用一些通配写法

    • / 表示通配所有资源,不包括jsp文件

    • /* 表示通配所有资源,包括jsp文件

    • /a/* 匹配所有以a前缀的映射路径

    • *.action 匹配所有以action为后缀的映射路径

  • 步骤4 开发一个form表单,向servlet发送一个get请求并携带username参数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="userServlet">
        请输入用户名:<input type="text" name="username" /> <br>
        <input type="submit" value="校验">
    </form>
</body>
</html>

  • 启动项目,访问index.html ,提交表单测试

映射关系图

三 Servlet注解方式配置

3.1 @WebServlet注解源码

3.2 @WebServlet注解使用
使用@WebServlet注解替换Servlet配置

@WebServlet(
        name = "userServlet",
        //value = "/user",
        urlPatterns = {"/userServlet1","/userServlet2","/userServlet"},
        initParams = {@WebInitParam(name = "encoding",value = "UTF-8")},
        loadOnStartup = 6
)
public class UserServlet  extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取请求中的参数
        String username = req.getParameter("username");
        if("zhangsan".equals(username)){
            //通过响应对象响应信息
            resp.getWriter().write("NO");
        }else{
            resp.getWriter().write("YES");
        }
    }
}

四 Servlet生命周期

4.1 生命周期简介
  • A- 什么是Servlet的生命周期
  • 应用程序中的对象不仅在空间上有层次结构的关系,在时间上也会因为处于程序运行过程中的不同阶段而表现出不同状态和不同行为——这就是对象的生命周期

  • 简单的叙述生命周期就是对象在容器中从 开始创建 到 销毁 的过程。

  • B- Servlet容器

Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。这一点和我们之前所编写的代码有很大不同。在今后的学习中我们会看到,越来越多的对象交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。

  • C- Servlet主要的生命周期执行特点

4.2 生命周期测试
  • A- 开发servlet代码
  • B- 配置Servlet
  • C- 请求Servlet测试

4.3 生命周期总结
  1. 通过生命周期测试我们发现Servlet对象在容器中是单例

  2. 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程

  3. 多个线程可能会使用相同的Servlet对象,所以在Servlet中,我们不要轻易定义一些容易经常发生修改的成员变量

  4. load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复

  5. Tomcat容器中,已经定义了一些随系统启动实例化的servlet,我们自定义的servlet的load-on-startup尽量不要占用数字1-5

五 Servlet继承结构

5.1 Servlet 接口
  • A- 源码及功能解释
  • B- 接口及方法说明

Servlet 规范接口,所有的Servlet必须实现

  • public void init(ServletConfig config) throws ServletException;

    • 初始化方法,容器在构造servlet对象后,自动调用的方法,容器负责实例化一个ServletConfig对象,并在调用该方法时传入

    • ServletConfig对象可以为Servlet 提供初始化参数

  • public ServletConfig getServletConfig();

    • 获取ServletConfig对象的方法,后续可以通过该对象获取Servlet初始化参数

  • public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

    • 处理请求并做出响应的服务方法,每次请求产生时由容器调用

    • 容器创建一个 ServletRequest对象 和 ServletResponse对象 ,容器在调用service方法时,传入这两个对象

  • public String getServletInfo();

    • 获取ServletInfo信息的方法

  • public void destroy();

    • Servlet实例在销毁之前调用的方法

5.2 GenericServlet 抽象类
  • A- 源码
  • B- 源码解释

GenericServlet 抽象类是对Servlet接口一些固定功能的粗糙实现,以及对service方法的再次抽象声明,并定义了一些其他相关功能方法

  • private transient ServletConfig config;

    • 初始化配置对象作为属性

  • public GenericServlet() { }

    • 构造器,为了满足继承而准备

  • public void destroy() { }

    • 销毁方法的平庸实现

  • public String getInitParameter(String name)

    • 获取初始参数的快捷方法

  • public Enumeration<String> getInitParameterNames()

    • 返回所有初始化参数名的方法

  • public ServletConfig getServletConfig()

    • 获取初始Servlet初始配置对象ServletConfig的方法

  • public ServletContext getServletContext()

    • 获取上下文对象ServletContext的方法

  • public String getServletInfo()

    • 获取Servlet信息的平庸实现

  • public void init(ServletConfig config) throws ServletException()

    • 初始化方法的实现,并在此调用了init的重载方法

  • public void init() throws ServletException

    • 重载init方法,为了让我们自己定义初始化功能的方法

  • public void log(String msg)

  • public void log(String message, Throwable t)

    • 打印日志的方法及重载

  • public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

    • 服务方法再次声明

  • public String getServletName()

    • 获取ServletName的方法

5.3 HttpServlet 抽象类
  • A- 源码
  • B- 解释

abstract class HttpServlet extends GenericServlet HttpServlet抽象类,除了基本的实现以外,增加了更多的基础功能

  • private static final String METHOD_DELETE = "DELETE";

  • private static final String METHOD_HEAD = "HEAD";

  • private static final String METHOD_GET = "GET";

  • private static final String METHOD_OPTIONS = "OPTIONS";

  • private static final String METHOD_POST = "POST";

  • private static final String METHOD_PUT = "PUT";

  • private static final String METHOD_TRACE = "TRACE";

    • 上述属性用于定义常见请求方式名常量值

  • public HttpServlet() {}

    • 构造器,用于处理继承

  • public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException

    • 对服务方法的实现

    • 在该方法中,将请求和响应对象转换成对应HTTP协议的HttpServletRequest HttpServletResponse对象

    • 调用重载的service方法

  • public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException

    • 重载的service方法,被重写的service方法所调用

    • 在该方法中,通过请求方式判断,调用具体的do***方法完成请求的处理

  • protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

  • protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

  • protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

  • protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

  • protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

  • protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

  • protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

    • 对应不同请求方式的处理方法

    • 除了doOptions和doTrace方法,其他的do*** 方法都在故意响应错误信息

5.4 自定义Servlet
  • A- 继承关系图解

  • B- 自定义Servlet中,必须要对处理请求的方法进行重写
  • 要么重写service方法

  • 要么重写doGet/doPost方法

六 ServletConfig和ServletContext

6.1 ServletConfig的使用
  • A- ServletConfig是什么
  • 为Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象

  • 容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性

  • B- ServletConfig是一个接口,定义了如下API

package jakarta.servlet;
import java.util.Enumeration;
public interface ServletConfig {
    String getServletName();
    ServletContext getServletContext();
    String getInitParameter(String var1);
    Enumeration<String> getInitParameterNames();
}

  • C- ServletConfig怎么用,测试代码
定义Servlet
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletConfig servletConfig = this.getServletConfig();
        // 根据参数名获取单个参数
        String value = servletConfig.getInitParameter("param1");
        System.out.println("param1: " + value);

        // 获取所有参数名
        Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
        while (parameterNames.hasMoreElements()){
            String parameterName = parameterNames.nextElement();
            System.out.println(parameterName+" : "+servletConfig.getInitParameter(parameterName));
        }
    }
}
配置Servlet
<servlet>
    <servlet-name>servletA</servlet-name>
    <servlet-class>com.alice.servlet.ServletA</servlet-class>
    <!--配置ServletA的初始参数-->
    <init-param>
        <param-name>param1</param-name>
        <param-value>value</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>servletA</servlet-name>
    <url-pattern>/servletA</url-pattern>
</servlet-mapping>
请求Servlet测试


6.2 ServletContext的使用
  • A- ServletContext是什么
  • ServletContext对象 有称呼为 上下文对象 , 或者叫应用域对象(后面统一讲解域对象)

  • 容器会为每个app创建一个独立的唯一的ServletContext对象

  • ServletContext对象为所有的Servlet所共享

  • ServletContext可以为所有的Servlet提供初始配置参数

  • B- ServletContext怎么用
配置ServletContext参数
<context-param>
    <param-name>paramA</param-name>
    <param-value>valueA</param-value>
</context-param>
<context-param>
    <param-name>paramB</param-name>
    <param-value>valueB</param-value>
</context-param>
在Servlet中获取ServletContext并获取参数
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //TODO ServletConfig
       /*
        ServletConfig servletConfig = this.getServletConfig();
        // 根据参数名获取单个参数
        String value = servletConfig.getInitParameter("param1");
        System.out.println("param1: " + value);

        // 获取所有参数名
        Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
        while (parameterNames.hasMoreElements()){
            String parameterName = parameterNames.nextElement();
            System.out.println(parameterName+" : "+servletConfig.getInitParameter(parameterName));
        }
        */

        //TODO ServletContext
        //从ServletContext中获取为所有的Servlet准备的参数
        ServletContext servletContext = this.getServletContext();
        String valueA = servletContext.getInitParameter("paramA");
        System.out.println("paramA: " + valueA);
        // 获取所有参数名
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        // 迭代并获取参数名
        while (initParameterNames.hasMoreElements()){
            String parameterName = initParameterNames.nextElement();
            System.out.println(parameterName+" : "+servletContext.getInitParameter(parameterName));
        }
    }
}
6.3 ServletContext其他重要API
  • A- 获取资源的真实路径

String realPath = servletContext.getRealPath("资源在web目录中的路径");

例如我们的目标是需要获取项目中某个静态资源的路径,不是工程目录中的路径,而是部署目录中的路径;我们如果直接拷贝其在我们电脑中的完整路径的话其实是有问题的,因为如果该项目以后部署到公司服务器上的话,路径肯定是会发生改变的,所以我们需要使用代码动态获取资源的真实路径。只要使用了servletContext动态获取资源的真实路径,那么无论项目的部署路径发生什么变化,都会动态获取项目运行时候的实际路径,所以就不会发生由于写死真实路径而导致项目部署位置改变引发的路径错误问题

  • B- 获取项目的上下文路径

String contextPath = servletContext.getContextPath();

项目的部署名称,也叫项目的上下文路径,在部署进入tomcat时所使用的路径,该路径是可能发生变化的,通过该API动态获取项目真实的上下文路径,可以帮助我们解决一些后端页面渲染技术或者请求转发和响应重定向中的路径问题

  • C- 域对象的相关API
  • 域对象: 一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同

  • ServletContext代表应用,所以ServletContext域也叫作应用域,是webapp中最大的域,可以在本应用内实现数据的共享和传递

  • webapp中的三大域对象,分别是应用域,会话域,请求域

三大域对象都具有的API如下

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐