板块一 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/465908.html

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

相关文章

读书笔记之《重塑大脑重塑人生》:大脑强大的可塑性

《重塑大脑重塑人生》作者是诺曼道伊奇&#xff0c;原作名: The Brain That Changes Itself: Stories of Personal Triumph from the Frontiers of Brain Science &#xff0c;于 2015-1-20出版。 诺曼•道伊奇&#xff08;Norman Doidge&#xff09;是医学博士&#xff0c;精…

C#,21根火柴棍问题(21 Matchticks Problem)的算法与源代码

一、21根火柴棍问题&#xff08;21 Matchticks Problem&#xff09; 21根火柴棍问题是西方经典游戏之一。 给定21根火柴&#xff0c;2个人A和B&#xff08;比如&#xff1a;分别是计算机和用户&#xff09;。 每个人一次可以挑选 1-- 4 根火柴。 被迫挑最后一根火柴的人输了…

【C语言】C的整理记录

前言 该笔记是建立在已经系统学习过C语言的基础上&#xff0c;笔者对C语言的知识和注意事项进行整理记录&#xff0c;便于后期查阅&#xff0c;反复琢磨。C语言是一种面向过程的编程语言。 原想在此阐述一下C语言的作用&#xff0c;然而发觉这些是编程语言所共通的作用&#…

轻薄型机型EM-T195,对移动作业so easy

由于移动工作环境特殊且不固定&#xff0c;不仅温差大&#xff0c;还会产生潮湿、干燥、灰尘等恶劣气候环境&#xff0c;使普通平板电脑无法适应移动工作。但三防平板电脑是设计制造的&#xff0c;材料特殊&#xff0c;可以平静面对上述恶劣环境&#xff0c;保证随时随地保持良…

Day 43 | 动态规划 1049. 最后一块石头的重量 II 、494. 目标和 、 474.一和零

1049. 最后一块石头的重量 II 题目 文章讲解 视频讲解 思路&#xff1a;dp[j] 表示容量为 j 的背包&#xff0c;最多可以背最大重量为dp[j]。 class Solution {public int lastStoneWeightII(int[] stones) {int sum 0;for (int i 0; i < stones.length; i) {sum stone…

利用路由懒加载和CDN分发策略对极客园项目进行性能优化

文章目录 前言1.配置路由懒加载2.项目资源打包3.包体积可视化分析4.cdn配置 总结 前言 极客园项目的完成之后&#xff0c;我们需要对项目进行打包以及性能优化&#xff0c;优化用户体验以及加快响应时间&#xff0c;本文只列举了路由懒加载和cdn分发的策略 1.配置路由懒加载 …

数学建模:EWM – TOPSIS 超强讲义! 原理、应用、代码

目录 一、综合评价指标预处理 1.定量指标的一致化处理&#xff08;正向化处理&#xff09; 2.定量指标的无量纲化处理 二、熵权法&#xff08;EWM&#xff09; 三、TOPSIS法 四、熵权法-TOPSIS的使用流程 案例&#xff1a;熵权法-TOPSIS的案例分析&#xff1a;水质评价 …

Spring Security实现权限认证与授权

一、Spring Security Spring Security作为Spring家族的安全框架&#xff0c;在安全方面的两个核心功能是认证&#xff08;Authentication&#xff09;和授权&#xff08;Authorization&#xff09;。 &#xff08;1&#xff09;用户认证指的是&#xff1a;验证某个用户是否为系…

Docker 一小时从入门到实战 —— Docker commands | Create your own image | vs VM ... 基本概念扫盲

Docker crash course 文章目录 Docker crash course1. What and Why of Docker?2.1 What2.2 What problem does it solve?2.2.1 before containers2.1.2 with containers 2. Docker vs Virtual Machines2.1 Difference2.2 Benefits 3. Install docker locally4. Images vs Co…

CentOS7下如何安装Nginx

一、Ngxin是什么 Nginx是一个开源的 Web 服务器&#xff0c;具有反向代理、负载均衡、缓存等功能。它可以作为 HTTP 服务器&#xff0c;将服务器上的静态文件&#xff08;如 HTML、图片&#xff09;通过 HTTP 协议展现给客户端&#xff0c;也可以实现动静分离&#xff0c;把动态…

寒假9-蓝桥杯训练

//轨道炮 #include<iostream> using namespace std; #include<algorithm> int logs[100010]; int main() {int n;cin >> n;for (int i 1;i < n;i){cin >> logs[i];}sort(logs 1, logs n 1);int ans 1000000000;for (int i 2;i < n;i){if (…

{}初始化和初始化列表

C98标准中允许使用花括号对数组和自定义类型的变量进行初始化&#xff0c;C11扩展了大括号的用途&#xff0c;允许使用花括号对所有的内置类型和自定义类型进行初始化&#xff0c;使用时&#xff0c;可以加号&#xff0c;也可以不加。 对于自定义类型&#xff0c;当花括号中的常…