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

2023年 10月 9日 49.3k 0

一、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、 一个例子

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

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

    1、Servlet加载时机

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

    
        HelloServlet
        com.ydlclass.servlet.HelloServlet
        2
    
    

    [#]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()方法的定义如下:

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

    service()方法:

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

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

    service()的定义如下:

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

    destroy()方法:

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

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

    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的生命周期,所有的方法需要我们实现。

    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("hello servlet");
        }
    
        @Override
        public String getServletInfo() {
            return null;
        }
    
        @Override
        public void destroy() {
    
        }
    }
    

    GenericServlet

    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();
        }
        
        ....
    }
    
    public class UserServlet extends GenericServlet {
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            servletResponse.getWriter().print("hello servlet");
        }
    }
    

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

    //
    // 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完全精确匹配。

    
        MyServlet
        /user/users.html
        /index.html
        /user/addUser
    
    

    当在浏览器中输入如下几种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) 路径匹配

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

    
        MyServlet
        /user/*
    
    

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

    • http://localhost:8080/appDemo/user/users.htmlopen in new window
    • http://localhost:8080/appDemo/user/addUseropen in new window
    • http://localhost:8080/appDemo/user/bb//sdf/sdf/sdf/updateUseropen in new window

    #(3)扩展名匹配

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

    
        MyServlet
        *.jsp
        *.do
    
    

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

    • http://localhost:8080/appDemo/user/users.jspopen in new window
    • http://localhost:8080/appDemo/toHome.actionopen in new window

    #(4) 缺省匹配

    
        MyServlet
        /
    
    

    #2、匹配顺序

  • 精确匹配。

  • 路径匹配,先最长路径匹配,再最短路径匹配。

  • 扩展名匹配。

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

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

  • #3、需要注意的问题

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

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

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

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

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

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

    
            default
            org.apache.catalina.servlets.DefaultServlet
            
                debug
                0
            
            
                listings
                false
            
            1
        
    
            jsp
            org.apache.jasper.servlet.JspServlet
            
                fork
                false
            
            
                xpoweredBy
                false
            
            3
     
    
            default
            /
    
    
        
    
            jsp
            *.jsp
            *.jspx
    
    
    • “/*”和“/”均会拦截静态资源的加载,需要特别注意

    #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

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论