板块一 Servlet编程:第一节 Servlet的实现与生命周期 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程:第一节 Servlet的实现与生命周期

  • 一、Servlet相关概念
    • Serlvet的本质
  • 二、中Web项目中实现Servlet规范
    • (1)在普通的Java类中继承HttpServlet类
    • (2)重写service方法
      • 编辑项目对外访问路径
  • 二、Servlet工作流程
  • 三、其它实现Servlet规范的方式
    • (1)继承自GenericServlet 类
    • (2)实现Servlet接口
    • (3)在HttpServlet中直接重写doGet()和doPost()方法
  • 四、Servlet生命周期

在上一节的内容中,我们已经系统的学习了HTTP的相关概念、知道了GET和POST请求在服务器上的运行原理、请求响应在服务器中究竟是怎样运行的,从这一节开始我们将系统的学习实现Servlet的完整过程

一、Servlet相关概念

Serlvet的本质

  • 当编写Java程序想要在网上实现聊天、发帖、这样一些的交互功能,普通Java技术是非常复杂的,试想要从底层搭建出一整个服务层的代码有多复杂?并且每个人搭建的底层还不一样,为了解决这个问题,sun公司就提供了Serlvet这种技术供我们使用。Servlet是Server与Applet的缩写,是服务端小程序的意思,本质上就是一个遵循Servlet开发的Java类,由服务器调用,在服务器端运行,它的创建、使用、销毁都由Servlet容器进行管理,其常见容器有很多,如Tomcat,Jetty,WebLogic Server,WebSphere,JBoss等等(在Tomcat的集成这一节中我们已经详尽的学习了Tomcat),更奇怪的是Servlet没有main()方法,本质上我们可以想象Servlet通过某种注入回调方法与Servlet容器取得联系,从而代替我们在Servlet中书写main()函数
    在这里插入图片描述
  • Servlet与HTTP紧密联系,可以处理与HTTP协议相关的所有内容,这是Servlet应用广泛的原因之一
  • 实际上在运行JSP时,服务器底层将JSP编译成一个Java类,这个类就是Servlet,因此可以说JSP就是Servlet(详见JSP追根溯源小节)
  • Servlet在 JAVA WEB项目中的位置,它就是我们常说的后端

在这里插入图片描述

  • 编程学习越往后越是如此,我们能做的其实很有限。大部分工作框架都已经帮我们做了。只
    要我们实现xx接口,它会帮我们创建实例,然后搬运(接口注入)到它合适的位置,然后一套既定
    的流程下来执行到创建我们需要的实例

二、中Web项目中实现Servlet规范

我们已经在板块零的第二节中创建了Java Web项目;又在板块零的第三节中成功把Tomcat集成到IDEA中,现在我们就在这个Web项目中实现Servlet规范

(1)在普通的Java类中继承HttpServlet类

在www.caijiyuan包中创建一个普通的Java类
helloServlet.java
在这里插入图片描述
此类继承HttpServlet类,还记得继承怎么写吗:extends
在这里插入图片描述

(2)重写service方法

在上一节中我们详细学习了请求响应在服务器中究竟是怎样运行的,而在Servlet中请求响应就是通过HttpServlet类中的service方法实现的,service方法有两个形参(Request,Rrsponse)分别对应请求、响应
在HttpServlet类中,IDEA重写方法的快捷键是Ctrl+O在这里插入图片描述
但我们发现有两个service,它们的区别是什么?
先来看第一个,HttpServlet的service()方法

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取http request的method参数,其实就是html的form标签  //中method属性对应的字符串 String method = req.getMethod();long errMsg;//判断请求方式if(method.equals("GET")) {//获取最后被修改时间 errMsg = this.getLastModified(req);if(errMsg == -1L) {/**如果servlet不支持http request header的if-modified-since属性 * 则继续处理 **/  this.doGet(req, resp);} else {//如果支持这个属性 long ifModifiedSince;try {ifModifiedSince = req.getDateHeader("If-Modified-Since");} catch (IllegalArgumentException var9) {ifModifiedSince = -1L;}/** * 如果客户端的文件最后修改时间和服务器端的文件最后修改时间一致则返回304不需要修改状态 * 这样服务器就不返回html,浏览器读取本地缓存文件,否则重新获取服务器端的对应html文件 **/  if(ifModifiedSince < errMsg / 1000L * 1000L) {this.maybeSetLastModified(resp, errMsg);this.doGet(req, resp);} else {resp.setStatus(304);}}} else if(method.equals("HEAD")) {errMsg = this.getLastModified(req);this.maybeSetLastModified(resp, errMsg);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 {//如果请求不是以上的所有请求方式,该方法就会响应501错误,也就是不支持这种请求String errMsg1 = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[]{method};errMsg1 = MessageFormat.format(errMsg1, errArgs);resp.sendError(501, errMsg1);}}

这是第二个ServletRequest

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;try {request = (HttpServletRequest)req;response = (HttpServletResponse)res;} catch (ClassCastException var6) {throw new ServletException("non-HTTP request or response");}this.service(request, response);
}

原来只有第二个 ServletRequest service方法是由Tomcat自动调用,它将接收的客户端请求转交给HttpServlet中的第一个HttpServletRequest protected service方法,此保护类型的service方法再把将请求分发给doPost()doGet()方法进行下一步处理
因此这里就Request/Response俩个形参而言,重写调用第一个或第二个service方法的效果应该是一样的,此处我们直接重写第一个,也就是 HttpServletRequest protected service
在这里插入图片描述

编辑项目对外访问路径

我们还应将项目对外访问路径(就是在服务器中此项目的站点名)更改为自己想要的样子
在这里插入图片描述此处我更改为与包名一致/www.caijiyuan
在这里插入图片描述

注意:我们还应在类前设置注解@WebServlet("/helloServlet"),告诉服务器当前资源在站点下的真实路径

helloServlet.java中写入测试内容

package www.caijiyuan;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("/helloServlet")
public class helloServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.service(req, resp);//打印内容在控制台System.out.println("Hello Servlet with terminal");//通过流输出数据到浏览器resp.getWriter().write( "Hello Servlet with brower");}
}

启动服务器在浏览器中访问得

在这里插入图片描述
在控制台得
在这里插入图片描述

二、Servlet工作流程

那么HttpServletRequest request请求是在服务器中是怎样接受到的呢?
即是上一节中详解过的请求头的功劳

在这里插入图片描述
当请求进入服务器时

  1. 服务器会通过请求头键值对中的Host中的localhost找到主机本机
  2. 通过8080端口确定本机中占用端口的程序Tomcat
  3. 通过请求行中的"/www.caijiyuan"确定是Tomcat下的哪个站点
  4. 通过"/helloServlet"确定是当前站点下的那个资源路径

找到资源路径后,如果服务器是第一次被访问就会创建一个Servlet否则就会调用service方法生成request对象来处理会话中的请求;接受到请求后经过设定的代码生成response对象保存响应内容返回给客户端(浏览器),这就是Servlet工作的流程

三、其它实现Servlet规范的方式

(1)继承自GenericServlet 类

对于一个 Servlet 类,我们日常最常用的方法是继承自 HttpServlet 类,实际上,HttpServlet是扩展了GenericServlet 类。GenericServlet 类实现了Servlet,ServletConfig和Serializable接口。它主要完成了这些任务:

  • 将 init() 中的 ServletConfig 赋给一个类级变量,可以由 getServletConfig 获得;
  • 为 Servlet 所有方法提供默认实现的接口(除了service方法);
  • 可以直接调用 ServletConfig 中的方法;
    它的基本结构如下:
abstract class GenericServlet implements Servlet,ServletConfig{//GenericServlet通过将ServletConfig赋给类级变量private trServletConfig servletConfig;public void init(ServletConfig servletConfig) throws ServletException {this.servletConfig=servletConfig;/*自定义init()的原因是:如果子类要初始化必须覆盖父类的init() 而使它无效 这样this.servletConfig=servletConfig不起作用 这样就会导致空指针异常 这样如果子类要初始化,可以直接覆盖不带参数的init()方法 */this.init();}//自定义的init()方法,可以由子类覆盖  //init()不是生命周期方法public void init(){}//实现service()空方法,并且声明为抽象方法,强制子类必须实现service()方法 public abstract void service(ServletRequest request,ServletResponse response) throws ServletException,java.io.IOException{}//实现空的destroy方法public void destroy(){ }
}

可以看到如果继承这个类的话,我们必须重写 service() 方法来对处理请求
继承自GenericServlet 类实现Servlet

package www.caijiyuan;import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;@WebServlet("/Servlet_Gener")
public class Servlet_Gener extends GenericServlet {@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {//打印内容在控制台System.out.println("Hello Servlet with terminal");//通过流输出数据到浏览器servletResponse.getWriter().write( "Hello Servlet with brower");}
}

启动服务器后在浏览器中访问得

在这里插入图片描述

(2)实现Servlet接口

除了两个继承抽象类实现Servlet的接口,还有一个通过实现interface Servlet来实现Servlet规范的方法

在这里插入图片描述

实例
创建类Servlet_imp.java ,使其实现Servlet接口
在这里插入图片描述
IDEA中使用快捷键Alt+Shift+Enter实现方法,在service()方法中添加测试代码,并且在类前添上@WebServlet()注释

package www.caijiyuan;import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;@WebServlet("/Servlet_imp")
public class Servlet_imp implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {//通过流输出数据到浏览器servletResponse.getWriter().write( "Hello Servlet with brower");}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {}
}

访问浏览器同样可以得到
在这里插入图片描述

(3)在HttpServlet中直接重写doGet()和doPost()方法

我们在调用HttpServlet中service方法时,底层实际上是判断GET请求还是POST请求从而分别调用doGet()和doPost()方法

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);}}

实例
创建类Servlet_do.java使其继承自HttpServlet,然后重写doGet()和doPost()方法
在这里插入图片描述
通过上一节我们已经知道了浏览器访问地址是使用GET方法,所以我们在doGet()中添加测试代码,并且在类前添上@WebServlet()注释

package www.caijiyuan;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("/Servlet_do")
public class Servlet_do extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doGet(req, resp);//通过流输出数据到浏览器resp.getWriter().write( "Hello Servlet with brower");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doPost(req, resp);}
}

访问浏览器同样可以得到
在这里插入图片描述

以上三种方式都可以实现Servlet规范,使得普通Java类升级成Servlet类,但最简便的方式还是第一种,也就是继承自HttpServlet类

四、Servlet生命周期

有了以上的知识储备,我们就可以梳理一下Servlet的整个生命周期了:由于Servlet没有main()方法,不能独立运行,它的运行完全由Servlet引擎来控制和调度,所谓生命周期,指的就是Servlet容器何时创建 Servlet实例、何时调用其方法进行请求的处理、何时并销毁其实例的整个过程。

  • 实例和初始化时机:当请求到达容器时,容器查找该 Servlet对象是否存在,如果不存在,则会创建实例并进行初始化,如果存在则会直接调用service()方法
  • 就绪/调用/服务阶段:当有请求到达容器,容器调用Servlet对象的 service()方法,此方法在整个生命周期中可以被多次调用;HttpServlet的 service()方法则会依据请求方式来调用doGet()或者doPost()方法。但是,这两个do方法默认情况下,会抛出异常,需要子类去override
  • 销毁时机:当容器关闭时(应用程序停止时),会将程序中的Servlet实例进行销毁。上述的生命周期可以通过Servlet 中的生命周期方法来观察。在Servlet 中有三个生命周期方法,不由用户手动调用,而是在特定的时机由容器自动调用

观察这三个生命周期方法即可观察到Servlet的生命周期

init方法,在Servlet 实例创建之后执行(证明该Servlet有实例创建了)

public void init() throws ServletException {System.out.println("创建实例调用");
}

service 方法,每次有请求到达某个Servlet 方法时执行,用来处理请求(证明该Servlet 进行服务了)

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("service方法调用了");
}

destroy 方法,Servlet实例销毁时执行(证明该Servlet的实例被销毁了)

public void destroy() {System.out.println("实例被销毁了");
}

实例

启动服务器在浏览器中访问项目得
在这里插入图片描述
在控制台输出,即是在浏览器访问项目时调用了init()方法(只会被调用一次init()),并且紧接着接受请求调用了service方法
在这里插入图片描述
接着关闭Tomcat服务器,在控制台输出
在这里插入图片描述
Servlet的生命周期,可以更详细的分为四步:Servlet类加载 – >实例化 – >服务 – >销毁
下面我们描述一下Tomcat与Servlet是如何工作的,看看下面的时序图:
在这里插入图片描述

  1. Web Client 向Servlet容器(Tomcat)发出Http请求
  2. Servlet 容器接收Web Client的请求
  3. Servlet 容器创建一个HttpServletRequest对象,将Web Client请求的信息封装到这个对象中
  4. Servlet 容器创建一个HttpServletResponse 对象
  5. Servlet 容器调HttpServlet对象service 方法,把Request与Response作为参数,传给HttpServlet
  6. HttpServlet 调用HttpServletRequest对象的有关方法,获取Http请求信息
  7. HttpServlet 调用HttpServletResponse对象的有关方法,生成响应数据
  8. Servlet 容器把HttpServlet的响应结果传给Web Client

以上就是此小节的所有内容,我们系统的学习了Servlet的概念、实现Servlet规范的多个方法、Servlet的工作流程、生命周期等,为之后Servlet具体对象学习打下了坚实的基础。从下一节开始我们将学习service方法两个形参之一:HttpServletRequest实例的具体操作

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

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

相关文章

《乱弹篇(十三)明朝事儿》

2024年农历除夕夜&#xff0c;因追剧收看电视连续剧《后宫》而放弃了收看一年一度的《春晚》&#xff0c;至到春节&#xff08;农历正月初一&#xff09;晚才看完了《后宫》。 社交网站“必应”图片《后宫》 电视连续剧《后宫》&#xff0c; 讲的是明朝英宗末年的历史故事&…

Python操作MySQL基础

除了使用图形化工具以外&#xff0c;我们也可以使用编程语言来执行SQL从而操作数据库。在Python中&#xff0c;使用第三方库: pymysql来完成对MySQL数据库的操作。 安装第三方库pymysql 使用命令行,进入cmd&#xff0c;输入命令pip install pymysql. 创建到MySQL的数据库连接…

93 log4j-slf4j-impl 搭配上 log4j-to-slf4j 导致的 StackOverflow

前言 呵呵 最近想要 做一个 mongo 低版本的客户端读取高版本的服务端传递过来的数据造成的一个错误的时候, 出现了这样的问题 引入了 mongo-java-driver 之后, 使用相关 api 的时候会触发 com.mongo.internal.connection.BaseCluser 的初始化, 其依赖的 Loggers 间接的依赖…

Python中使用multiprocessing模块创建进程

在计算机编程领域&#xff0c;多进程编程是一种常见的并发编程技术&#xff0c;特别适用于利用多核处理器来提高程序性能和并行处理任务。Python作为一种功能强大的编程语言&#xff0c;提供了多种方法来实现多进程编程。其中&#xff0c;multiprocessing模块为我们提供了一种简…

【Spring MVC篇】参数的传递及json数据传参

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Spring MVC】 本专栏旨在分享学习Spring MVC的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、普通参数的传…

Nodejs基于vue的网上药店药品销售管理系统bxm50

该系统开发是基于B/S模式的&#xff0c;使用nodejs编程语言和js技术&#xff0c;框架使用vue框架&#xff0c;数据库采用MySQL数据库进行存储数据&#xff0c;使用HTML,CSS,等技术进行页面的布局和设计。系统编码使用vscode做开发工具&#xff0c; 系统设计包括几个主要的功能…

VitePress-12-markdown中使用vue的语法

前言 VitePress 中&#xff0c;markdown文档最终都会转换成为 html文件&#xff0c;我们在访问的时候&#xff0c;也是直接访问的 xxx.html 文件。而且&#xff0c;markdown文档会被作为 [vue单文件] 进行处理&#xff0c;因此&#xff0c;我们我们可以在文档中使用 vue 语法&…

MySQL索引怎么提高查询的速度?

目录 一、MySQL介绍 二、什么是索引 三、为什么要用索引 四、索引如何提高查询速度 一、MySQL介绍 MySQL是一个开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它是目前最流行和广泛使用的数据库之一。MySQL由瑞典MySQL AB公司开发&#xff0c;并在…

Python爬虫实战 | 京东平台电商API接口采集京东商品京东工业商品详情数据

item_get-获得JD商品详情API测试 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cac…

甘肃旅游服务平台:技术驱动的创新实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

《统计学简易速速上手小册》第1章:统计学基础(2024 最新版)

文章目录 1.1 数据类型和数据收集1.1.1 基础知识1.1.2 主要案例&#xff1a;顾客满意度调查1.1.3 拓展案例 1&#xff1a;产品销售分析1.1.4 拓展案例 2&#xff1a;员工绩效评估 1.2 描述性统计学1.2.1 基础知识1.2.2 主要案例&#xff1a;销售数据分析1.2.3 拓展案例 1&#…

Maven进阶

一、分模块开发与设计 1. 分模块开发的意义 问题导入 分模块开发对工程有什么好处&#xff1f; 模块拆分原则 目的&#xff1a;项目的扩展性变强了&#xff0c;方便其他项目引用相同的功能。 将原始模块按照功能拆分成若干个子模块&#xff0c;方便模块间的相互调用&#…