JavaWeb之Servlet接口

Servlet接口

什么是Servlet?

Servlet是一种基于Java技术的Web组件,用于生成动态内容,由容器管理,是平台无关的Java类组成,并且由Java Web服务器加载执行,是Web容器的最基本组成单元

什么是Servlet容器?

Servlet容器作为Web服务器或应用服务器的一部分,通过请求和响应提供Web客户端与Servlets交互的能力,容器管理Servlet实例以及它们的生命周期(创建、初始化、提供服务、销毁等)

在java web中不管是使用J2EE原生的servlet/jsp还是使用springmvc/springboot,在web服务器看来只是对外暴露出来的Servlet,而这个Servlet是javax.servlet.Servlet接口,该接口定义了Servlet引擎与Servlet程序之间通信的协议约定。

// Servlet的加载和实例化可以发生在容器启动时,也可以延迟初始化直到有请求需要处理时
public interface Servlet {
   // 负责初始化Servlet对象,容器创建好Servlet对象后由容器调用调用,只执行一次
   // 当load-on-startup设置为负数或者不设置时会在Servlet第一次用到时才被调用
    void init(ServletConfig config) throws ServletException;
  // 获取该Servlet的初始化参数信息
    ServletConfig getServletConfig();
  // 负责响应客户端的请求,当容器接收到客户端要求访问特定Servlet对象的请求时,会调用该Servlet对象的service()方法,每次请求都会执行
    void service(ServletRequest req, ServletResponse res)
 throws ServletException, IOException
;
  // 返回Servlet信息,包含创建者、版本、版权等信息
    String getServletInfo();
  // Servlet结束生命周期时调用,释放Servlet对象占用的资源
    void destroy();
}

而为了简化开发,jdk中提供了一个实现Servlet接口的简单的Servlet类,javax.servlet.GenericServlet,该类实现了Servlet的基本功能,对init(ServletConfig config)、service(ServletRequest req, ServletResponse res)和destroy()方法提供了默认实现

jdk针对HTTP协议专门提供了一个Servlet类,javax.servlet.http.HttpServlet,该类继承于GenericServlet类,在其基础上针对HTTP的特点进行扩充,一般编写程序时继承HttpServlet即可,这样只需要重写doGet()和doPost()方法即可

Servlet继承关系
Servlet继承关系

Servlet中涉及的主要对象

  • 请求对象 ServletRequest、HttpServletRequest
  • 响应对象 ServletResponse、HttpServletResponse
  • Servlet配置对象 ServletConfig
  • Servlet上下文对象 ServletConfig

Servlet注册与运行

Servlet编写好之后需要在web.xml中进行注册和映射才能被Servlet容器加载从而被外界访问

<!-- 注意servlet和servlet-mapping都是成对出现的 --> 
<!-- 注册Servlet -->    
 <servlet>
        <servlet-name>HW</servlet-name>
        <servlet-class>com.zhanghe.study.servlet.HelloWorldServlet</servlet-class>
     <!-- 配置servlet初始化init时中ServletConfig参数-->
        <init-param>
          <param-name>name</param-name>
          <param-value>john</param-value>
        </init-param>
        <!-- servlet加载时机,若为负数,则在第一次请求时被创建,若为0或者正数,在web应用被Servlet容器加载时创建实例,值越小越早被启动  -->
        <load-on-startup>1</load-on-startup>
    </servlet>
  <!-- 映射Servlet -->
    <servlet-mapping>
      <!-- 对应servlet标签中的servlet-name值 -->
        <servlet-name>HW</servlet-name>
       <!-- 开头的/表示web用用程序的根目录 -->
        <url-pattern>/HelloWorld</url-pattern>
    </servlet-mapping>

tomcat中的web.xml包含有一个缺省的Servlet

    <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-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

ServletConfig

对于每个Servlet可能在启动时都需要一些初始化参数,而所有的Servlet是交由Servlet引擎去实例化的,那么也就是需要将每个Servlet的初始化参数也都配置到web.xml中,Servlet引擎将Servlet容器对象和Servlet的配置信息封装到ServletConfig中,并在Servlet初始化时将ServletConfig传递给该Servlet。javax.servlet.ServletConfig接口的作用就是用来定义ServletConfig对象所需要对外提供的方法

public interface ServletConfig {
  // 获取web.xml中定义的servlet-name
    String getServletName();
 // ServletContext表示的是应用本身
    ServletContext getServletContext();
  // 获取init-param配置的参数
    String getInitParameter(String name);
  // 获取init-param配置的所有参数
    Enumeration<String> getInitParameterNames();
}

Servlet引擎装载并创建一个Servlet对象后,会调用该对象的init(ServletConfig config)方法,Servlet中的ServletConfig getServletConfig()方法会返回init传入的ServletConfig对象

    <servlet>
        <servlet-name>HW</servlet-name>
        <servlet-class>com.zhanghe.study.servlet.HelloWorldServlet</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>zhanghe</param-value>
        </init-param>
    </servlet>
// 获取指定的属性
config.getInitParameter("name")
// 获取所有的属性
config.getInitParameters()

ServletContext

每个Web应用程序在启动时都会创建一个ServletContext对象,每个Web应用程序都有一个唯一的ServletContext对象,javax.servlet.ServletContext接口定义了ServletContext需要对外提供的方法,Servlet通过这些方法来和ServletContext容器进行通信

  • 该对象代表当前WEB应用,可以获取到web应用的信息,一个Web应用只有一个ServletContext对象
  • 可以使用ServletConfig的getServletContext()获取到ServletContext
  • 可以获取web应用的初始化参数,这是全局的方法,在web.xml中配置
  • 获取web应用某个文件的绝对路径(在服务器上的路径,不是部署前的方法) getRealPath
  • 获取当前应用的名称 getContextPath
  • 获取当前web应用的某一个文件对应的输入流 getResourceAsStream() path是相对于当前web应用的根目录
    <context-param>
        <param-name>email</param-name>
        <param-value>master@163.com</param-value>
    </context-param>

servlet生命周期

生命周期相关方法,servlet生命周期中的方法全是由servlet容器来调用的

  • 构造器 web容器调用Servlet的无参构造器,默认是在第一次请求时被加载,可以通过load-on-startup标签来进行设置什么时候加载
  • init方法
  • service方法
  • destory方法
init方法--初始化

init方法在第一次创建servlet时被调用,在后续每次请求时都不会被调用。

当用户调用servlet的时候,该servlet的一个实例就会被创建,并且为每一个用户产生一个新的线程,init()用于进行一些初始化数据的加载和处理,这些数据会被用于servlet的整个生命周期

void init(ServletConfig config) throws ServletException;

为了防止重写该方法时开发者忘记将入参config赋值给成员变量config,故而提供了GenericServlet类进行了一次封装

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

在进行Servlet重写时只需要重写不带参数的init方法即可

public void init() throws ServletException

该方法是由servlet容器调用的

什么时候触发初始化

有两种情况会进行Servlet的初始化

  • Servlet被客户端首次请求访问时触发初始化方法

  • 如果配置了load-on-startup元素,则在Servlet容器启动该Servlet所属Web应用时就会初始化该Servlet

     <servlet>
         <servlet-name>dispatcherServlet</servlet-name>
         <servlet-class>
             org.springframework.web.servlet.DispatcherServlet
         </servlet-class>
         <load-on-startup>1</load-on-startup>
     </servlet>
重写init方法

GenericServlet实现了Servlet和ServletConfig,是一个抽象类,并对init(ServletConfig var1)方法进行了一层封装,有一个ServletConfig成员变量,在init()方法中进行了初始化,使得可以直接在GenericServlet中直接使用ServletConfig方法

而我们平时写Servlet大多是继承于HttpServlet类的,在对init方法进行重写时,重写不带参的init()方法即可

//GenericServlet类

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

public void init() throws ServletException {
}

该方法由GenericServlet的调用,如果需要使用到ServletConfig则调用getServletConfig()方法来获取

service方法

service方法是实际处理请求的方法,servlet容器调用service方法来处理请求,并将响应写回到客户端,每次服务器接收到一个新的servlet请求时,服务器会产生一个新的线程来调用服务

void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
处理请求逻辑

HttpServlet继承了GenericServlet,重写了service方法,将ServletRequest和ServletResponse转换为HttpServletRequest和HttpServletResponse,并根据不同的请求方式进行分发,doGet/doPost/doHead等

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;
    // 如果请求类型不相符,则抛出异常
    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }
  // 转换成Http的request和response
    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
  // 进行http的处理方法
    service(request, response);
}

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    
{
    // 获取请求类型
        String method = req.getMethod();
   // 根据不同的请求类型调用不同的方法
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

servlet可以在任何协议下访问 ,写的Servlet必须实现Servlet接口,在http协议下可以使用HttpServlet ,用来给Web访问的

在HttpServlet类中对于service()方法进行了处理,根据请求方式的不同,将请求分发到了不同的方法,而我们一般情况下写Servlet也是继承自HttpServlet的,所以在写请求处理逻辑时,只需要重写doGet()和doPost()方法即可

// HttpServlet类

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String msg = lStrings.getString("http.method_get_not_supported");
    this.sendMethodNotAllowed(req, resp, msg);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_post_not_supported");
        this.sendMethodNotAllowed(req, resp, msg);
}
GET方法

GET方法时浏览器向web服务器传递信息的默认方法,会在地址栏上产生很长的字符串,且GET方法有大小限制,请求字符串中最多只能有1024个字符

POST方法

POST方法不将请求信息放到地址中

destroy方法

destory()方法只在servlet生命周期结束时被调用一次。可以在destory()方法中进行一些资源的清理,如关闭数据库连接、停止后台线程等

https://zhhll.icu/2021/javaweb/基础/1.Servlet接口/

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/472293.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在电脑和 SD 卡上恢复已删除 MOV等视频文件

MOV 是 Apple 创建的多媒体容器。您可能已经意识到&#xff0c;用 macOS QuickTime Player 录制的视频是以 MOV 格式保存的&#xff0c;而且 MOV 在 Windows 上也兼容。我们可能已经保存了很多 MOV 格式的视频。但是&#xff0c;如果这些 MOV 文件丢失或被意外删除怎么办&#…

14-k8s控制器资源-rs控制器replicasets

replicaset副本控制器&#xff0c;简称&#xff1a;rs控制器&#xff1b; 用法&#xff1a;与rc控制器“几乎”相同&#xff1b; 能力&#xff1a;可以指定pod的副本始终存活&#xff0c;相比于rc控制器&#xff1b;支持标签匹配&#xff0c;也支持标签表达式 注意&#xff1a;…

备战蓝桥杯---数据结构之好题分享1

最近几天在刷学校的题单时&#xff0c;发现了几道十分巧妙又有启发性的题&#xff0c;借此来记录分享一下。 看题&#xff1a; 从整体上看似乎没有什么规律&#xff0c;于是我们从小地方入手&#xff0c;下面是图解&#xff1a; 因此&#xff0c;我们用栈的数据结构实现即可&a…

云计算基础-虚拟化概述

虚拟化概述 虚拟化是一种资源管理技术&#xff0c;能够将计算机的各种实体资源&#xff08;如CPU、内存、磁盘空间、网络适配器等&#xff09;予以抽象、转换后呈现出来并可供分割、组合为一个或多个逻辑上的资源。这种技术通过在计算机硬件上创建一个抽象层&#xff0c;将单台…

【HarmonyOS】ArkUI 组件(四)

List 列表&#xff08;List&#xff09;是一种复杂容器&#xff0c;具备下列特点&#xff1a; 列表项&#xff08;ListItem&#xff09;数量过多超出屏幕后&#xff0c;会自动提供滚动功能。列表项&#xff08;ListItem&#xff09;既可以纵向排列&#xff0c;也可以横向排列…

Oracle19c PDB的简介与创建

多租用户环境&#xff08;Multitenant Environment&#xff09;&#xff0c;允许一个数据库容器&#xff08;CDB&#xff09;承载多个可插拔数据库&#xff08;PDB&#xff09;。 首先&#xff0c;毋庸置疑的是pdb只能在cdb root下创建&#xff0c;每个cdb里面都有一个pdb$seed…

springboot194基于springboot的医药管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的医药管理系统 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考。 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **…

Mysql Day06

sql优化 插入数据 大批量插入数据 主键顺序插入性能高于乱序插入 load data local infile /root/load_user_100w_sort.sql into table tb_user fields terminated by , lines terminated by \n ; 主键优化 这个黄色的都是一个一个Page 主键乱序插入之后会变成1-3-2&#x…

[BUUCTF]-PWN:ciscn_2019_es_1解析(tcachebin duf)

查看保护 再查看ida 大致为alloc创建堆块&#xff0c;free释放堆块&#xff0c;show输出堆块内容 但是要注意一点free没有清空堆块指针 完整exp&#xff1a; from pwn import* from LibcSearcher import* pprocess(./es1) premote(node5.buuoj.cn,26841)def alloc(size,cont…

SQL中的各种连接的区别总结

前言 今天主要的内容是要讲解SQL中关于Join、Inner Join、Left Join、Right Join、Full Join、On、 Where区别和用法&#xff0c;不用我说其实前面的这些基本SQL语法各位攻城狮基本上都用过。但是往往我们可能用的比较多的也就是左右连接和内连接了&#xff0c;而且对于许多初学…

CSP-201903-2-二十四点

CSP-201903-2-二十四点 一、中缀表达式转后缀表达式 中缀表达式是一种常见的数学表达式书写方式&#xff0c;其中操作符位于相关的操作数之间&#xff0c;如 A B。而后缀表达式&#xff08;逆波兰表示法&#xff09;则是一种没有括号&#xff0c;操作符跟随操作数之后的表示…

使用Taro开发鸿蒙原生应用——快速上手,鸿蒙应用开发指南

导读 本指南为开发者提供了使用 Taro 框架开发鸿蒙原生应用的快速入门方法。Taro&#xff0c;作为一个多端统一开发框架&#xff0c;让开发者能够使用一套代码同时适配多个平台&#xff0c;包括鸿蒙系统。文章将详细介绍如何配置开发环境&#xff0c;以及如何利用 Taro 的特性…