在上一篇文章中,我们演示也证明了Servlet 是一种动态web资源开发的技术,即我可以在浏览器中输入URL,然后就可以在浏览器中看到我们编写的Servlet资源。

那当我们在浏览器上一起一个HTTP请求之后,具体的流程是怎么样的呢?借用LinkinStar博文中的图:

上面这副图讲解了整个HTTP请求到相应的过程,在这里进行下解释:

当在浏览器中输入URL后,会通过hosts文件/dns服务器解析为IP地址,进而找对应的服务器来提供服务,当服务器端收到请求后:

1)分析当前的这个请求访问的是当前服务器中的那个WEB应用

这个可以从请求行中的请求资源部分来分析出来。

2)分析当前请求要访问的这个WEB应用的那个资源:

从上述请求行的资源部分可以分析出请求的是什么资源

3)查找web.xml文件,查看有没有对应的虚拟路径,如果有则用这个虚拟路径对应的资源做出相应。

4)服务器从response对象中获取之前写入的数据,然后组成响应发送到浏览器中。

一个HTTP响应代表服务器向客户端回送的数据,它包括:一个状态行、若干消息头、以及实体内容。

二、Servlet的运行过程及生命周期

Servlet程序由WEB服务器调用,web服务器收到客户端的Servlet请求后,会首先检查是否已经装载并创建了该Servlet的实例对象;如果是,则直接跳到第四步,否则,执行第二步。

1)装载并创建该Servlet实例对象【加载:容器通过类家长群使用Servlet类对应的文件加载Servlet,创建:通过调用servlet构造函数创建一个Servlet对象】

2)调用Servlet实例对象的init方法

3)处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求,用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletRespons对象,然后调用Servlet的service方法并将请求和响应对象作为参数传递进去。

4)卸载:调用destroy方法让servlet自己释放其占用的资源。

因此Servlet的生命周期可以分为5个阶段:加载、创建、初始化、处理客户请求、卸载。

(1)通常情况下,服务器会再Servlet第一次调用时候加载并创建此Servlet的实例对象,同时调用init方法做初始化操作。

(2)一旦此Servlet实例创建出来后,该实例就驻留在内存中,为后续对这个Servlet的请求做出相应的服务,每次对这个Servlet的访问都会导致Servlet中service方法执行;

(3)当web应用被移除容器或者关闭服务器的时候,随着web应用的销毁,Servlet也会被销毁,在销毁之前服务器会调用Servlet的destroy方法做一些善后的工作。

下面3个方法可以基本代表Servlet的生命周期:

*init方法,负责初始化Servlet对象

*service方法,负责响应客户的请求(调用doGET 或 doPost 方法)

*destory方法,当Servlet对象退出生命周期的时候,负责释放占用的资源。

PS:在Servlet的整个生命周期内,Servlet的init方法只有在Servlet被创建的时候被调用一次,每次对这个Servlet的访问都会导致Servlet中的service方法执行。

例如:现在浏览器连续访问Servlet 5次,内存中只有一个Sevlet对象。Servlet对象由服务器创建(创建一次),request和response由Servlet容器创建(创建5次),会调用5次service方法。

发送一次请求:http://localhost:8000/Servlet_Demo01/servlet/ServletDemo01

我们看下控制台的输出:

可见,正常Servlet是在被第一次访问的时候加载并创建,同时开始调用service方法。

那我们再发送5次请求看下结果如何:

可见没有打印Servlet 初始化,但发送了请求,可见service方法就被调用了,紧接着我们停止容器看下,是否会调用destory方法。

可见当停止web服务器的时候,servlet被销毁了。

总结:可以看到,Servlet只会初始化一次,之后的话,我们多次访问的是同一个Servlet对象,此时,即使关掉网页,Servlet也不会销毁,只有tomcat服务器才会销毁servlet.

那么我们有没有办法,启动web服务器的时候同时加载并创建servlet呢?方法是有的,需要在web.xml中配置:<load-on-startup>1</load-on-startup>

 <servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>ServletDemo01</servlet-name>
<servlet-class>mdj.servlet.study.ServletDemo01</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>ServletDemo01</servlet-name>
<url-pattern>/servlet/ServletDemo01</url-pattern>
</servlet-mapping>

然后我们看下现象:

可见启动的时候,就进行了创建和加载,那么我们再次访问URL请求的时候,是否还会创建呢,答案应该是不会,看下面结果。

三、Servlet的结构

上面我们讲解了Servlet的运行过程和生命周期,下面我们看看其结构:

1)Servlet接口有五个方法

init初始化,就是把servlet装载到内存中,只会被调用一次

getServletConfig获取servletConfig对象

service主要的服务方法,放业务逻辑,每次都会被调用

getServletInfo得到servlet配置信息

destroy销毁该servlet,从内存中清除掉

2)继承GenericServlet

GenericServlet实现了servlet接口

然后只有一个抽象方法需要你自己去重写

那就是service方法,所以相比来说init别的方法他都帮你实现好了,只要你写service方法就可以了。

至少看起来继承GenericServlet比直接实现servlet接口要方便

3)继承HttpServlet

因为后来发现servlet主要是为了服务于http请求的,而且发现GenericServlet对于http来说还不够好

所以有了HttpServlet,首先它是继承自GenericServlet

然后它有很多http相关的方法,post,get,put等待

用户可以根据自己需要来实现这些方法

每个过来的请求都会调用service方法,最后service会根据不用的请求分发到不同的地方去做。

相比较而言,HttpServlet覆写了GenericServlet,service方法体内的代码会自动判断用户的请求方式,如为GET的请求则调用doGet方法,如为Post请求,则调用doPost方法,因此开发人员在编写servlet的时候,通常指需要继承HttpServlet,然后覆写doGet和doPost方法,不要去覆写service方法。

我们来看一下源码:

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 / 1000 * 1000)) {
// 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);
}
}

这个就是HttpServlet中service方法中的源码;

首先我们看到的就是String method = req.getMethod();这个就是通过request获取方法,然后根据方法判断调用哪一个方法,要说明的是前面它已经定义好了这些字符串

private static final String METHOD_DELETE = "DELETE"; 
   private static final String METHOD_HEAD = "HEAD"; 
   private static final String METHOD_GET = "GET"; 
   private static final String METHOD_OPTIONS = "OPTIONS"; 
   private static final String METHOD_POST = "POST"; 
   private static final String METHOD_PUT = "PUT"; 
   private static final String METHOD_TRACE = "TRACE"; 
需要指出的是,前面说过 HttpServlet是继承自GenericServlet,而GenericServlet需要用户实现一个service,刚才我们看到的是HttpServlet自己的。

在GenericServlet中也有一个service方法:

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

源码是这样的,它会把原来的ServletRequest 请求直接强转换成HttpServletRequest然后再去调用它正真的service方法。

这点是需要指出的,HttpServletRequest比ServletRequest进行了进一步的封装,方法更适合http。

四、Servlet映射匹配问题

由于客户端是通过URL的地址找到对应web服务器中的资源,所以Servlet程序如果想要被外界访问,那么必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中<servlet>和<servlet-mapping>元素组成。

<servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。

一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。

需要注意的是:

1.一个servlet可以对应多个servlet-mapping,从而一个servlet可以有多个路径访问。

2.url-pattern中的路径也可以使用*通配符,但是只有两种固定的格式:一种格式“*.扩展名”,另一种是正斜杠/ 开头并以/* 结尾。

通常匹配的优先级是哪个精确找哪个,*.后缀的格式永远匹配级最低。

五、线程安全问题

Servlet引擎采用多线程模式运行,它为并发的每个访问请求都使用一个独立的线程来进行响应。

但是由于默认情况下Servlet在内存中只有实例存在,因此当多个浏览器并发访问Servlet的时候,就会有可能产生线程的安全问题。

Servlet线程不安全,至始至终,之维护一个实例对象,当同一个资源被多个线程同时访问操作的时候,就可能会互相干扰。

解决的方法是:

1、SingleThreadModel接口(标记接口,单线程模型接口):不能真的防止线程安全问题(已过时)

Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用Servlet的service方法。对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用独立的一个Servlet实例对象

注:此接口在API 2.4中就已经过时,虽然解决了线程安全问题,但是消耗了大量性能(不同的客户端同时访问会创建不同的Servlet实例),所以此方法建议不用,实际开发中也不用。

2、使用同步代码块,但效率低。在Servlet中尽量少用类变量(成员变量),如果一定要用类变量则用锁来防止线程安全问题,但是要注意锁住内容应该是造成线程安全问题的核心代码,尽量的少锁主内容,减少等待时间提高servlet的响应速度。


几个小题目:

下面有关servlet service描述错误的是?

1、不管是post还是get方法提交过来的连接,都会在service中处理

答:每次请求都会调用service方法,最终都会在service中处理,正确;

2、doGet/doPost 则是在 javax.servlet.GenericServlet 中实现的

答:GenericServlet只是继承了Servlet的接口,实现了它其中的5个方法,其中需要用户重写的是service方法,而doGet/doPost是因为之后出现了HttpServlet才有的,是针对http请求才有了这个类,才有了doPost和doGet,所以是错误的。

3、service()是在javax.servlet.Servlet接口中定义的

答:Servlet接口一共定义了5个方法,其中就有service(),正确;

4、service判断请求类型,决定是调用doGet还是doPost方法

答:正确,原因见上面。

今天就复习到此,下一节讲解Servlet中比较常用到的ServletConfig 和ServletContext 对象。

web开发之Servlet 二的更多相关文章

  1. web开发之Servlet 一

    因为最近在研究公司一套新的框架,发现这套框架的底层是对Struts2,Spring 封装后的WEB应用框架,而我发现如果仅仅是利用这个框架开发,确实很容易快速上手,做业务来说是没有问题的,但我觉得如果 ...

  2. Java Web开发之Servlet、JSP基础

    有好多年不搞Java Web开发了,这几天正好国庆放假,放松之余也有兴趣回头看看Java Web开发技术的基础. 我们都知道,Servlet是Java Web开发的重要基础,但是由于Servlet开发 ...

  3. web开发之Servlet 三

    昨天我们学习了Servlet的运行过程和生命周期,那么今天我们学习下Servlet中非常重要的两个类:ServletConfig  ServletContext 我们可以看到,与顶层Servlet主动 ...

  4. Web开发之Servlet

    当一个请求到达服务端,服务器怎么处理? 当一个请求到达服务端时,由服务端的引擎来进行分析.它根据工程名找到工程, 然后拿到URL的资源地址和web.XML文件的所有的进行对比,和哪一个对比上就找到了具 ...

  5. Java Web开发之Servlet获取ckeditor内容

    js: <!-- ckeditor插件 --> <script type="text/javascript" src="js/ckeditor/cked ...

  6. Web开发之Tomcat&Servlet

    <!doctype html>01 - JavaEE - Tomcat&Servlet figure:first-child { margin-top: -20px; } #wri ...

  7. 移动web开发之rem适配布局

    移动web开发之rem适配布局 方案: 页面布局文字能否随着屏幕大小变化而变化 流式布局和flex布局主要针对于宽度布局,那高度如何布局? 怎样让屏幕发生变化的时候元素高度和宽度等比例缩放? 1. r ...

  8. Vim下的Web开发之html,CSS,javascript插件

    Vim下的Web开发之html,CSS,javascript插件   HTML 下载HTML.zip 解压HTML.zip,然后将里面的所有文件copy到C:\Program Files\Vim\vi ...

  9. JavaWeb开发之Servlet

    1. Servlet有关概念和前置知识 1.1 什么是动态网页 如果浏览器在不同时刻或不同条件下访问web服务器上的某个页面,浏览器所获得的页面内容可以发生变化,那么这个页面就称之为动态页面. 动态网 ...

随机推荐

  1. 软工实践第八次作业(课堂实战)- 项目UML设计(第五组)

    本次作业博客 团队信息 队名:起床一起肝活队 原组长: 白晨曦(101) 原组员: 李麒 (123) 陈德斌(104) 何裕捷(214) 黄培鑫(217) 王焕仁(233) 林志华(128) 乐忠豪( ...

  2. Maya脚本——重命名物体的名称

    该脚本用于将图1中的命名变更为图2中的,把maya中使用相同名称的物体都重命名为不同的名称. 重命名的规则是:组名_原名称_序号 查阅了maya的官方手册:http://download.autode ...

  3. jQuery之回到顶部

    实现回到顶部的功能,根据学了元素滚动实现,温习知识点. 做之前先理清一下步骤和思路: 1.获得页面的滚动长度 var $page = $("html,body"); var dis ...

  4. Web服务器负载均衡的几种方案 : DNS轮询

    本篇主要讲一下最简单的方案——DNS轮询. DNS轮询 大多域名注册商都支持多条A记录 的解析,其实这就是DNS轮询 ,DNS 服务器 将解析请求按照A记录 的顺序,逐一分配到不同的IP上,这样就完成 ...

  5. collection 多态 会自动转型为子类 继承多态需要显示转型

  6. iOS 通过网络请求获取图片的下载歌曲

    1.导入代理<NSURLConnectionDataDelegate> @interface ViewController ()<NSURLConnectionDataDelegat ...

  7. BZOJ3637 Query on a tree VI(树链剖分+线段树)

    考虑对于每一个点维护子树内与其连通的点的信息.为了换色需要,记录每个点黑白两种情况下子树内连通块的大小. 查询时,找到深度最浅的同色祖先即可,这可以比较简单的树剖+线段树乱搞一下(似乎就是qtree3 ...

  8. 题解 P1200 【[USACO1.1]你的飞碟在这儿Your Ride Is He…】

    cin其中有很多小众的函数与其他重叠 不妨拿来用用(作死不止) 划重点!!! 1.cin.get(),相当于c里面的getchar(),可以往里面输入字符 2.cin.getline(),相当于str ...

  9. [CQOI2013]新Nim游戏 线性基

    题面 题面 题解 首先我们知道nim游戏先手必败当且仅当所有石堆异或和为0,因此我们的目标就是要使对手拿石堆的时候,无论如何都不能使剩下的石堆异或和为0. 对于一个局面,如果我们可以选取一些可以凑出0 ...

  10. Android 解决ScrollView嵌入ListView | GridView | ScrollView显示问题

    一.ScrollView中嵌套ListView ScrollView和ListView都是滚动结构,很明显如果在ScrollView中加入ListView,可以预见性的知道,肯定会有显示/滚动的问题, ...