servlet本质上是一个接口,它压根就不管也管不着哪些网络协议,如:http。
那servlet到底是干什么的呢?很简单,接口的作用是什么?规范呗!
servlet接口定义的是一套处理网络请求的规范,所有实现servlet的类,都需要实现它的那五个方法,其中最主要的是两个声明周期方法init()和destory(),还有一个处理请求的service()。也就是说,所有实现servlet接口的类,或者说,所有想要处理网络请求的类,都需要回答这三个问题:
- 你初始化时要做什么?
- 你销毁时要做什么?
- 你接收到请求时要做什么?
这是Java给的一种规范!就像阿西莫夫的机器人三大定律、行尸走肉里Rick的那三个问题一样,规范!
servlet是一个规范,那实现了servlet的类,就能处理请求了吗?
不能。你从来不会在servlet中写什么监听8080端口的代码,servlet不会直接和客户端打交道!
那请求是怎么来到servlet的呢?答案是servlet容器,比如我们最常用的Tomcat。servlet都是部署在一个容器中的,不然你的servlet根本不起作用。
Tomcat才是与客户端直接打交道的家伙,它监听了端口,请求过来后,根据URL等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,Tomcat再把这个respond返回给客户端。
Servlet本身在Tomcat中是“非常被动”的一个角色,处理的事情也很简单。网络请求与响应,不是它的主要职责,它其实更偏向于业务代码。所谓的request和respond是Tomcat传给它,用来处理请求和响应的工具,但它本身不处理这些。
Servlet的前世今生所谓Tomcat其实是Web服务器和Servlet容器的结合体。
(1) Web服务器的所做的工作本质上是:
将某个主机上的资源映射为一个URL供外界访问
(2) 那什么是servlet容器呢?
servlet容器,顾名思义里面存放的是servlet对象。
我们为什么能通过web服务器映射的URL访问到资源呢?肯定需要写程序处理请求,主要三个过程
接收请求(收到请求)
处理请求(处理请求)
响应请求(返回处理结果)
任何一个应用程序,必然包括这三个步骤。其中接收请求和响应请求是共性功能,而且没有差异性。于是,就可以把接收和响应两个步骤抽取成web服务器。
但处理请求的逻辑是不同的。没关系,抽取出来做成servlet,交给程序员自己编写
当然,随着互联网的发展,出现了三次架构,所以一些逻辑就从servlet抽取出来,分担到service和dao 但是servlet并不擅长往浏览器输出HTML页面,所以就出现了JSP。
等Spring家族出现后,Servlet开始退居幕后,取而代之的是SpringMVC。SpringMVC的核心组件DispacterServlet其实本质是一个Servlet,但是它已经自立门户,在原来HttpServlet的基础上,有封装了一层逻辑。
Servlet工作模式Servlet(Server Applet,服务端小程序,是服务端的一小部分),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
工作模式:
1、客户端请求该 Servlet;
2、Tomcat加载 Servlet 类到内存;
3、Tomcat实例化并调用init()方法初始化该 Servlet;(tomcat只认init()方法,所有需要继承父类的该方法或者创建该方法)
4、Tomcat调用service()(根据请求方法不同调用doGet() 或者 doPost(),此外还有doHead()、doPut()、doTrace()、doDelete()、doOptions());(tomcat只认xxx()方法,所有需要继承父类的该方法或者创建该方法)
5、destroy();
6、加载和实例化 Servlet。这项操作一般是动态执行的。然而,Server 通常会提供一个管理的选项,用于在 Server 启动时强制装载和初始化特定的 Servlet;
7、Server 创建一个 Servlet的实例;
8、第一个客户端的请求到达 Server;
9、Server 调用 Servlet 的 init() 方法(可配置为 Server 创建 Servlet 实例时调用,在 web.xml 中 标签下配置 标签,配置的值为整型,值越小 Servlet 的启动优先级越高);
10、一个客户端的请求到达 Server;
11、Server 创建一个请求对象,处理客户端请求;
12、Server 创建一个响应对象,响应客户端请求;
13、Server 激活 Servlet 的 service() 方法,传递请求和响应对象作为参数;
14、service() 方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息;
15、service() 方法使用响应对象的方法,将响应传回Server,最终到达客户端。service()方法可能激活其它方法以处理请求,如 doGet() 或 doPost() 或程序员自己开发的新的方法;
16、对于更多的客户端请求,Server 创建新的请求和响应对象,仍然激活此 Servlet 的 service() 方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用 init() 方法。一般 Servlet 只初始化一次(只有一个对象),当 Server 不再需要 Servlet 时(一般当 Server 关闭时),Server 调用 Servlet 的 destroy() 方法。
Servlet是J2EE 规范中的一种,主要是为了扩展java作为web服务的功能。J2EE 从92年的J2EE 1.2到现在J2EE8 从12个规范到现在20多个规范,越来越完善。他的作用就是为java程序提供一个统一的web应用的规范,方便程序员统一的使用这种规范来编写程序(开发人员按规范写程序),应用容器可以使用提供的规范来实现自己的特性(容器按规范调用程序)。比如tomcat的代码和jetty的代码就不一样是吧,但作为程序员你只需要了解servlet规范就可以从request中取值,你可以操作session等等。不用在意应用服务器底层的实现的差别而影响你的开发。
当然你也可以自己写一个http 服务器,自己定义一套API,比如你在底层接受到一个http请求后,你把这个http请求的header、cookie和param等封装成一个MyRequest.class 。然后你要得到,你在你的MyServlet中从MyRequest对象中拿到param请求参数,校验成功后需要返还给浏览器一个HTTP response。其中必须要有一个session,所以你往cookie中写了一个字段,LAOZIDESESSIONID=878361839QWQWEQEQE,同时把这个sessionid放在了自己的内存中。下一次浏览器再访问你就会带上这个LAOZIDESESSIONID这个cookie,你就知道他原来已经访问过了,而且上一次访问的数据你都有(在第一次保存在内存中)
但是有没有想过,如果每个程序员都写一个自己的HTTP服务器,该程序员离职了咋办。而且你用你的,我用我的,遇到问题都不能一起解决,你一会儿只支持http/1.0 ,别人都支持http/2.0了(虽然这个是在底层的实现了,和servlet没半毛钱关系,大家注意了,打个比方而已)。别人都支持注解了,你还在写配置呢!肯定不能啊,所有J2EE要出一个规范,要管住你们这群人,大家都要同步走。大家都用我这套规范,所有的请求都放在Request中,返回都放在response中。sessionID的名称也都可以自己设置,比如tomcat你可以
讲了这么多废话,总结来说Servlet就是一群人来制定java应用中使用web时的各种规范,统一接口,其他内部实现由厂商自己实现,tomcat jetty jboss等等应运而生。面向接口编程!!很熟悉吧
关于他如何工作的:一个http请求到来,容器将请求封装成servlet中的request对象,在request中你可以得到所有的http信息,然后你可以取出来操作,最后你再把数据封装成servlet的response对象,应用容器将respose对象解析之后封装成一个http response。
web服务器习惯处理静态页面,所以需要一个程序来帮忙处理动态请求(如当前时间)。Web服务器程序会将动态请求转发给帮助程序,帮助程序处理后,返回处理后的静态结果给web服务器程序。这样就避免了web服务器程序处理动态页面。Servlet的本质是一个帮助程序。如下图
Servlet工作流程分为三个阶段。init(初始化),service(运行),destroy(销毁)
Servlet没有main方法,所有行为由Container控制。Container就是一个java程序。
在加载Servlet的.class后,Servlet会由构造函数生成一个实例,然后Container调用init()方法完成参数的初始化,接着调用service()方法,service会根据网页的请求,调用doGet或者doPost方法,最后调用销毁方法。整个流程如下图:
查看接口方法: 五个方法,最难的地方在于形参,然而Tomcat会事先把形参对象封装好传给我们。除此之外,既不需要我们写TCP连接,也不需要我们解析HTTP请求,更不需要我们把结果转换成HTTP响应,request对象和response对象帮我们搞定了。
在Servlet里面主要写的代码都是业务逻辑,和原始的、底层的解析、连接等没有丝毫关系。最难的几个操作,人家已经给你封装成形参传进来了。
也就是说servlet虽然是个接口(接口保证了方法名的规范性和一致性,名字不一致,tomcat将无法通过名称进行调用),但是实现类只是个空壳,我们写点业务逻辑就好了
总的来说,Tomcat已经帮我们完成了底层的操作,并且传入了三个对象:ServletConfig、ServletRequest、ServletResponse。
ServletConfig即“servlet配置”,就是我们在web.xml中配置的servlet。它封装了servlet的一些参数信息。如果需要,我们可以从它获取。
Tomcat解析web.xml。通过找到,然后通过找到,然后通过java的反射机制将class实例化为对象。
Request/Response这是接收请求和发送请求的类,Tomcat已经处理并封装好了,不需要servlet操心。HTTP请求到达Tomcat之后,Tomcat通过字符串解析,把各个请求头(Header)、请求地址(URL)、请求参数等都封装进Request对象中。通过调用
等方法,就可以得到浏览器当初发送的请求信息。
至于Response,Tomcat传给Servlet时,它还是空的对象。Servlet逻辑处理后得到结果,最终通过response.write()方法,将结果写入response内部的缓冲区。Tomcat会在servlet处理结束后,拿到response,遍历里面的信息,组装成HTTP响应发给客户端。
Servlet接口5个方法,其中init、service、destory是声明周期方法。init和destory各自只执行一次,即servlet创建和销毁时。而service会在每次有新请求到来时被调用。也就是说,我们主要的业务代码需要写在service中。
但是,浏览器发送请求基本的有两种:GET/POST。于是我们必须这样写
那能不能简化呢?
于是我们发现了一个GenericServlet,是个抽象类。(Generic:通用的) 作用
- 提升了init方法中原本是形参的servletConfig对象的作用域,方便其他方法使用
- init方法中还调用了一个init空参方法,如果我们希望在servlet创建时做一些什么初始化操作,可以继承GenericServlet后,覆盖init空参方法
- 由于需要其他方法也可以使用servletConfig,于是写了一个getServletConext。
但是service()方法没有实现。
于是又引入了一个HttpServlet 它继承了GenericServlet。
GenericServlet本身是一个抽象类,有一个抽象方法service。实现如下: 也就是说,HttpServlet的service方法已经替我们完成了复杂的请求方法判断。
问题是HttpServlet为什么要声明成抽象类呢?它的文档中注释了: 一个类声明成抽象方法,一般有两个原因:
- 有抽象方法
- 没有抽象方法,但是不希望被实例化
HTTPServlet做成抽象类,仅仅是为了不让new
它为什么不希望被实例化,而且要求子类重写doGet、doPost等方法呢?
我们来看一下源码:
如果我们没有重写会怎么样? 浏览器页面会显示405(http.method_get_not_supported)
也就是说,HttpServlet虽然在service中帮我们写了请求方式的判断。但是针对每一种请求,业务逻辑代码都是不同的,HttpServlet无法知晓子类想干嘛,所以就抽象出了7个方法,并且提供了默认实现:报405、400错误,提示请求不支持。
但是这种实现本身非常。。。简单来说等于没有。所以不能让它被实例化,不然调用doXXX方法是无用功。
这就是模板方法模式:父类把能写的逻辑都写完,把不确定的业务代码抽象成一个方法,调用它。当子类重写该方法,整个业务代码就活了
子类继承父类的service方法,tomcat直接调用service方法。子类覆盖父类的doXxx方法,子类继承的service优先调用子类的doXxx方法。(java子类调用父类的方法的步骤:1、子类的对象调用方法时,会首先在子类中查找,如果子类中没有该方法,再到父类中查找;2、如果该方法中又调用了其他方法,那么还是按照之前的顺序,先在子类中查找,再在父类中查找。)
servlet首先是一个接口,GenerivServlet实现了这个接口。
他们都是属于javax.servlet.*;的这个包里面的
此外,在javax.servlet.http*;的这个包里面呢,其实也含有一个重要的servlet类叫httpServlet
首先我们看到ie浏览器,我们讲servlet也好,讲jsp也罢,首先我们要明确的是这是一个bs的体系结构,所以说他必然有浏览器的,那么浏览器要去访问Tomcat其中的某一个servlet或者说是jsp的时候呢,必须在ie浏览器当中输入
http://........
之类的命令敲了回车键之后浏览器就会向你指定的Tomcat发送http请求的(向指定的主机的指定端口发出http请求),这个请求会被Tomcat的Web服务器的这个模块接收,web服务器处理之后会转发给Tomcat的容器部分进行处理,他会帮助ie浏览器找到这个请求想要找到的servlet,这时在容器里面的行为要么是在容器内再向数据库发送操作数据库的命令,要么呢直接返回结果 ,其实就是静态的html页面,当页面被web服务器模块接收到之后,他会将静态页面返回给那个发送请求的那个ie浏览器,ie浏览器得到这个结果之后,就会自己显示出来,然后用户就能看到结果,所以可以将我们的结果分成三个部分,第一个部分是ie浏览器,第二个是Tomcat模块,第三个就是数据库模块。
其实开发servlet有三种方法,一种是实现servlet接口,一种是继承GernericServlet,一种是继承HttpServlet。 为什么会有三种方法呢?因为这个servlet这个技术并不是说一出现之后就是成熟的,他经历过这三个发展阶段,因此它具有这三种方式,其实在最先前是实现servlet接口方法来开发的,后面的时候技术人员发现这样写似乎不太方便,所以又发展出来一个叫做继承GenericServlet的方法来实现,后来又发现这个也不是很方便,所以又发展出来了继承 HttpServevlet方式来实现
为了使学习更加深刻,我会把三种方式都实现下。
Servlet开发是很简单的事情特别是用一些高级工具来开发的话,特别是像JBuilder来实现的话只要点一下就行了,但很遗憾的这样的高级工具他会隐藏太多的细节,这样的话就不利于我们的学习,特别是部署之类的他就给你全写了,那么你就不好学到他servlet的底层运行机制和原理,为了让大家理解的更为深刻,我还是先用JCreater来实现开发Servlet,后面当然会用到eclipse 或者JBuilder这些高级工具来开发。我们先过一点苦日子,然后再过一点好日子,这样的话知识更加扎实一点了。
下面我们就来真正的来开发Servlet,首先用什么方法呢?用实现接口的方法来实现
在这里会引入servlet的生命周期这个特点。这第一个servlet非常简单,就是写一个Hello World 在浏览器中输出。教程依照先简单,再难,再综合的顺序进行。
下面是Servlet的开发流程。
1:首先在Tomcat的主目录下的webapp下面建立一个WEB-INF文件夹
2:然后再WEB-INF文件夹下建立一个web.xml文件,记录网站的配置信息
建立classes子文件夹 存放你的servlet
当然这个操作你可以自己完成 也可以在root目录下拷贝一份
大家可以看到 这个地方传递过来了一个信息,什么信息呢?
对于Tomcat来讲,你所有的这些网站,页面对他来讲都是web应用,他看来就是web应用,就是在webapp下面建立我们的网站。
比如建立一个文件夹叫做mywebsite,接下来文件夹中放什么东西呢,我们要放的就是WEB-INF文件夹,在里面放置web.xml文件以及classes文件夹和lib文件夹。
WEB-INF这个要注意大小写,要注意大小写要一模一样才行的,名字都要一样才行的。(Tomcat只认这个名字)
在这个文件夹下进行上述两种操作,你可能会去问,为什么要这样做呢?这个倒是没有办法的事情 因为这就是规范。
classes当然是存servlet用的 ,那个lib文件夹用来做什么呢?
用来存放这个应用汇用到的一些jar包,比如数据库啦之类的,文件的下载要用的包都放在lib里面去,就是lib库。
//这是我的第一个Servlet 使用实现Servlet接口的方式来开发
package com.tsinghua;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class Hello implements Servlet
{
// 该函数用于初始化该servlet, 类似于我们的类的构造函数
// 该函数只是会被调用一次, 当用户第一次访问该servlet的时候被调用
public void init(ServletConfig parm1) throws ServletException
{
System.out.println("init it !");
}
// 用于得到servlet配置文件 与生命周期无关
public ServletConfig getServletConfig()
{
return null;
}
// service 函数用于处理业务逻辑
// 程序员应当把业务逻辑代码写在这里
// 该函数在用户每次访问servlet的时候都会被调用
// ServletRequest 对象用于获得客户端信息,ServletResponse 对象用于向客户端返回信息(客户端可以理解为浏览器)
// servelt jsp b/s
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
System.out.println("service it");
PrintWriter pw = res.getWriter();
pw.println("hello world");
}
public String getServletInfo()
{
return " ";
}
// 销毁servlet实例(释放内存)
// 1 reload 该servlet(webApp)
// 2 关闭Tomcat 或者说 关机之后 都会调用这个函数
public void destroy()
{
System.out.println("destory it");
}
}
以上就是实现servlet接口的方式来开发servlet方式来代码实现
其实以上方法都是回调函数 都是会在特定的时候特定的环境下自动调用的
其中init() 和 destroy() 都是只会调用一次的 但是 service 会在每一次都会被调用的
到现在还没有完成 因为如果你想让别人访问到你的wervlet 的话 你就要部署你servlet
下面讲授关于部署servlet的步骤(在web.xml 进行配置设置)
如果你要问为什么进行部署,那么还是一句话,规范。
hello_servlet
com.tsinghua.Hello
hello_servlet
/sp
用下面的控制台命令编译Hello.java之后,我们就可以启动Tomcat来进行访问了
启动Tomcat的bin目录下的startup.bat之后,
输入
http://127.0.0.1:8080/guowuxin/sp
多次访问之后就能体会到Servlet的生命周期
参考:
网络:Servlet的本质是什么?为什么要有Servlet?_OceanStar的学习笔记的博客-CSDN博客_servlet的本质是什么
https://blog.csdn.net/weixin_42635759/article/details/100171907