讲讲我对Servlet的了解,他是什么?有什么?

一、Servlet概述

1、 什么是 Servlet

​ Servlet 是基于 Jakarta 技术的 Web 组件,由容器管理,可生成动态内容。与其他基于 Jakarta 技术的组件一样,servlet 是独立于平台的 Java 类,它们被编译为与平台无关的字节码,这些字节码可以动态加载到支持 Jakarta 技术的 Web 服务器中并由其运行。容器,有时也称为 servlet 引擎,是提供 servlet 功能的 Web 服务器扩展。Servlet 通过 servlet 容器实现的请求/响应范式与 Web 客户端交互。

2、 什么是 Servlet 容器

​ Servlet 容器是 Web 服务器或应用程序服务器的一部分,它提供发送请求和响应的网络服务、解码基于 MIME 的请求以及格式化基于 MIME 的响应。Servlet 容器还通过其生命周期包含和管理 Servlet。

Servlet 容器可以内置到主机 Web 服务器中,也可以通过该服务器的本机扩展 API 作为附加组件安装到 Web 服务器。Servlet 容器也可以内置于或可能安装在支持 Web 的应用程序服务器中。

​ 所有 Servlet 容器都必须支持 HTTP 作为请求和响应的协议,但可以支持其他基于请求/响应的协议,例如 HTTPS(基于 SSL 的 HTTP)。容器必须实现的 HTTP 规范的必需版本是 HTTP/1.1 和 HTTP/2。

​ Java SE 8 是必须用来构建 Servlet 容器的底层 Java 平台的最低版本。

3、 一个例子

以下是一个典型的事件序列:

  1. 客户端(例如,Web 浏览器)访问 Web 服务器并发出 HTTP 请求。
  2. 请求由 Web 服务器接收并传递给 Servlet 容器。Servlet 容器可以在与主机 Web 服务器相同的进程中运行,也可以在同一主机上的不同进程中运行,或者在与其处理请求的 Web 服务器不同的主机上运行。
  3. Servlet 容器根据其Servlet 的配置确定调用哪个 Servlet,并使用代表请求和响应的对象调用它。
  4. Servlet 使用请求对象来找出远程用户是谁、POST作为此请求的一部分发送的HTTP参数以及其他相关数据。Servlet 执行它编程的任何逻辑,并生成数据发送回客户端。它通过响应对象将此数据发送回客户端。
  5. Servlet 处理完请求后,Servlet 容器会确保正确刷新响应,并将控制权返回给主机 Web 服务器。

二、Servlet核心技术

1、Servlet加载时机

​ 在默认情况下,当Web客户第一次请求访问某个Servlet时,Web容器会创建这个Servlet的实例。 当设置了web.xml中的子元素后,Servlet容器在启动Web应用时,将按照指定顺序创建并初始化这个Servlet。设置的数值大于0即可。例如:

xml 复制代码
<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.ydlclass.servlet.HelloServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

[#]2、Servlet的生命周期

先看与Servlet生命周期有关的三个方法:init(), service(), destroy(). Servlet生命周期可被定义为从创建直到毁灭的整个过程。以下是三个方法分别对应的Servlet过程:

  • init():Servlet进行初始化;
  • service():Servlet处理客户端的请求;
  • destroy():Servlet结束,释放资源;

在调用destroy()方法后,Servlet由JVM的垃圾回首器进行垃圾回收。

现在我们来详细讨论Servlet生命周期的方法:

init()方法:

​ Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化在Servlet生命周期中init()方法只被调用一次

​ 当用户调用一个Servlet时,Servlet容器就会创建一个Servlet实例,每一个用户请求都会产生一个新的线程,init()方法简单的创建或加载一些数据,这些数据将会被用在Servlet的整个生命周期。

init()方法的定义如下:

csharp 复制代码
public void init() throws ServletException {
  // 初始化代码...
}

service()方法:

​ service()方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service()方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。

​ 每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service()方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用doGet()、doPost()等方法。

service()的定义如下:

java 复制代码
public void service(ServletRequest request, ServletResponse response) 
    throws ServletException, IOException{
// service()代码...
}

destroy()方法:

​ destroy()方法只会被调用一次,在Servlet生命周期结束时被调用。destroy()方法可以让Servlet关闭数据库连接、停止后台、把cookie列表或点击计数器写入到磁盘,并执行其他类似的清理活动。 在调用destroy()方法之后,Servlet对象被标记为垃圾回收。

destroy()方法的定义如下所示:

csharp 复制代码
public void destroy() {
    // 终止化代码...
  }

总结:

  • 在首次访问某个Servlet时,init()方法会被执行,而且也会执行service()方法。
  • 再次访问时,只会执行service()方法,不再执行init()方法。
  • 在关闭Web容器时会调用destroy()方法。

3、实现一个Servlet

​ 当服务器接收到一个请求,就要有一个Servlet去处理这个请求,所以完成一个Servlet通常需要两步走。一方面要写一个java程序定义一个Servlet,另一方面要配置一下Servlet确定这个Servlet要处理哪一个请求。

(1)创建Servlet的三种方式

  • 实现javax.servlet.Servlet接口。
  • 继承javax.servlet.GenericServlet类。
  • 继承javax.servlet.http.HttpServlet类。

我们在日常开发中一般会使用第三种方法来进行Servlet的创建,前两种方法理解即可。

**注意:**创建Servlet文件后,需要在web.xml文件中完成Servlet配置,才可以使用。

通过实现Servlet接口,这个接口定义了servlet的生命周期,所有的方法需要我们实现。

java 复制代码
public class UserServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.getWriter().print("<h1>hello servlet</h1>");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

GenericServlet

csharp 复制代码
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;

    public GenericServlet() {
    }

    public void destroy() {
    }

    public String getServletInfo() {
        return "";
    }
    
    public void init() throws ServletException {
    }

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
    
    ....
}
scala 复制代码
public class UserServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.getWriter().print("<h1>hello servlet</h1>");
    }
}

Http只是会根据请求的类型进行特殊的调用

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.servlet.http;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javax.servlet.DispatcherType;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public abstract class HttpServlet extends GenericServlet {


    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

    protected long getLastModified(HttpServletRequest req) {
        return -1L;
    }



    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

	// 还是会调用它,只是会根据请求的类型进行特殊的调用
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }
    
    ...

}

HttpServletRequest和ServletRequest都是接口

HttpServletRequest继承自ServletRequest

HttpServletRequest比ServletRequest多了一些针对于Http协议的方法。 例如:

getHeader(), getMethod() , getSession()

三、Servlet的匹配规则

1、四种匹配规则

(1) 精确匹配

中配置的项必须与url完全精确匹配。

xml 复制代码
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/user/users.html</url-pattern>
    <url-pattern>/index.html</url-pattern>
    <url-pattern>/user/addUser</url-pattern>
</servlet-mapping>

当在浏览器中输入如下几种url时,都会被匹配到该servlet   http://localhost:8080/appDemo/user/users.htmlopen in new window   http://localhost:8080/appDemo/index.htmlopen in new window   http://localhost:8080/appDemo/user/addUseropen in new window

注意:

http://localhost:8080/appDemo/user/addUser?username=Tom&age=23open in new window 会被匹配到MyServlet。

#(2) 路径匹配

以"/"字符开头,并以"/*"结尾的字符串用于路径匹配

xml 复制代码
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/user/*</url-pattern>
</servlet-mapping>

路径以/user/开始,后面的路径可以任意。比如下面的url都会被匹配。

#(3)扩展名匹配

以"*."开头的字符串被用于扩展名匹配

xml 复制代码
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

则任何扩展名为jsp或action的url请求都会匹配,比如下面的url都会被匹配

#(4) 缺省匹配

xml 复制代码
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

#2、匹配顺序

  1. 精确匹配。

  2. 路径匹配,先最长路径匹配,再最短路径匹配。

  3. 扩展名匹配。

    注意:使用扩展名匹配,前面就不能有任何的路径。

  4. 缺省匹配,以上都找不到Servlet,就用默认的Servlet,配置为/

#3、需要注意的问题

路径匹配和扩展名匹配无法同时设置

​ 匹配方法只有三种,要么是路径匹配(以"/"字符开头,并以"/*"结尾 ),要么是扩展名匹配(以"*."开头),要么是精确匹配,三种匹配方法不能进行组合,不要想当然使用通配符。

  • 如/user/*.action是非法的
  • 另外注意:/aa//bb 是精确匹配,合法,这里的不是通配的含义

"/*"和"/"含义并不相同

  • "/"属于路径匹配,并且可以匹配所有request,由于路径匹配的优先级仅次于精确匹配,所以"/"会覆盖所有的扩展名匹配,很多404错误均由此引起,所以这是一种特别恶劣的匹配模式。
  • "/"是servlet中特殊的匹配模式,切该模式有且仅有一个实例,优先级最低,不会覆盖其他任何url-pattern,只是会替换servlet容器的内建default servlet ,该模式同样会匹配所有request。

Tomcat在%CATALINA_HOME%\conf\web.xml文件中配置了默认的Servlet,配置代码如下:

xml 复制代码
<servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
<servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
 </servlet>
<servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
</servlet-mapping>

    <!-- The mappings for the JSP servlet -->
<servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
</servlet-mapping>
  • "/*"和"/"均会拦截静态资源的加载,需要特别注意

#4、举例

映射的URL 对应的Servlet
/hello servlet1
/bbs/admin/* servlet2
/bbs/* servlet3
*.jsp servlet4
/ servlet5

实际请求映射的结果

去掉上下文路径的剩余路径 处理请求的Servlet
/hello servlet1
/bbs/admin/login servlet2
/bbs/admin/index.jsp servlet2
/bbs/display servlet3
/bbs/index.jsp servlet3
/bbs servlet3
/index.jsp servler4
/hello/index.jsp servlet4
/hello/index.html servlet5
/news servlet5
相关推荐
Estar.Lee2 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
2401_857610034 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
凌冰_5 小时前
IDEA2023 SpringBoot整合MyBatis(三)
spring boot·后端·mybatis
码农飞飞5 小时前
深入理解Rust的模式匹配
开发语言·后端·rust·模式匹配·解构·结构体和枚举
一个小坑货5 小时前
Rust 的简介
开发语言·后端·rust
monkey_meng5 小时前
【遵守孤儿规则的External trait pattern】
开发语言·后端·rust
Estar.Lee6 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
新知图书6 小时前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
盛夏绽放7 小时前
Node.js 和 Socket.IO 实现实时通信
前端·后端·websocket·node.js
Ares-Wang7 小时前
Asp.net Core Hosted Service(托管服务) Timer (定时任务)
后端·asp.net