Servlet的框架是由两个java包组成的:javax.servlet和javax.servlet.http.
javax.servlet包含了支持普通协议无关的Servlet的类。
javax.servlet.http包括了对HTTP协议的特别支持。
servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必须实现这一接口。在Servlet接口中定义了5个方法,其中有3个方法代表了Servlet的生命周期:
(1)init方法,负责初始化Servlet对象
(2)service方法,负责初始响应客户的请求
(3)destroy方法,当Servlet对象退出生命周期时,负责释放占用的资源
由上图可知,GenericServlet实现了Servlet接口,HttpServlet扩展了GenericServlet。用户开发自己的Servlet类时,扩展HttpServlet类即可。如果Servlet类扩展了HttpServlet类,通常不必实现service方法,因为HttpServlet类已经实现了service方法。在HttpServlet的service方法中,首先从HttpServletRequest对象中获取HTTP请求方式的信息,然后再根据请求方式调用相应的方法。
该方法的声明形式如下:
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException
在HttpServlet的service方法中,有两个参数:HttpServletRequest和HttpServletResponse,这两个类分别扩展了javax.servlet.ServletRequest和javax.servlet.ServletResponse接口。
ServletRequest接口:ServletRequest接口封装了客户请求信息,如客户请求方式、参数名和参数值、客户端正在使用的协议,以及发出客户请求的远程主机信息等。ServletRequest接口还为Servlet提供了直接以二进制数方式读取客户请求数据流的ServletInputStream。ServletRequest的子类可以为Servlet提供更多的与特定协议相关的数据
ServletRequest接口:ServletRequest接口为Servlet提供了返回响应结果的方法。它允许Servlet设置返回数据的长度和MIME类型,并且提供了输出流ServletOutputStream。ServletResponse子类可以提供更多和特定协议相关的方法。
HttpServlet能够根据客户发出的HTTP请求,生成相应的HTTP响应结果。HttpServlet首先必须读取HTTP请求的内容。Servlet容器负责创建HttpRequest对象,并把HTTP请求信息封装到HttpRequest对象中,这样通过调用HttpServletRequest的相关方法,就可以方便地读取HTTP请求中的任何一部分消息。
Servlet生命周期
阶段一:Servlet实现类的加载
(1)所有的web-server加载Servlet实现类的时候,都将搜索web-app根目录下WEB-INF目录的子目录lib和classes,在lib目录中搜索*.jar文件,在classes目录中搜索*.class文件;它将遍历WEB-INF目录下的web.xml配置文件,根据文件中的Servlet标签下Servlet-class子标签的内容加载相应的class文件。
(2)tomcat安装的根目录下的common目录的子目录lib中存放webapps目录下所有web-app的公共jar包,当web-app使用jar包时首先搜索本app中的lib,如果没有找到需要的jar包,将在common目录的lib中找jar包;
(3)web服务器启动时加载Servlet实现类的方法(配置WEB-INF目录下的web.xml)
在web.xml文件中与Servlet实现类相对应的Servlet标签中添加子标签,如:
标签内部的文本应是正整数,当多个Servlet实现类相对应的Servlet标签都添加了子标签时,根据该文本对应整数之的大小加载相应的Servlet实现类;整数值越小,加载顺序越靠前;值相等时,加载顺序由web-server决定。
阶段二:Servlet对象的创建与初始化
(1)大多数web-server会在客户端第一次访问服务器时加载Servlet实现类,创建对象并执行初始化方法,(如果设置服务器启动时加载,则服务器启动时创建对象并执行初始化方法,客户端第一次访问服务器时,直接进入service阶段执行service方法);以后每个客户端在发送请求就不会再调用init方法,也就是说它只能被调用一次。一个Servlet对象的初始化方法只执行一次;
(2)为Servlet对象设置初始化参数(配置WEB-INF目录下的web.xml)
1> 在web.xml文件中与Servlet实现类相对应的Servlet标签中添加子标签,如:
每对init-param标签只能设置一个初始化参数,如果要设置多个初始化参数,需重复以上四行代码;参数名一般都不能改,因为我们要在java程序中用到;参数值可以修改。
2> Servlet实现类中覆盖无参的init()方法,方法内部使用getInitParameter("参数名")获得相应的参数值,该方法在javax.Servlet.ServletConfig,javax.Servlet.GenericServlet两个类中都有定义,一般使用后者(即在Servlet实现类中直接使用getInitParameter("参数名")获得相应的参数值);
3> HttpServlet直接继承了GenericServlet类中init(ServletConfig)方法,GenericServlet类的init()方法有两种形式:常规初始化、由初始化参数控制的初始化
public void init()throws ServletException{} //可以调用不带参数的方法
public void init(ServletConfig config)throws ServletException{
this.config=config;//初始化成员变量config
init(); //这里直接调用了不带参数的init方法。
}
常规初始化:创建、载入在servlet生命周期内用到的一些数据,如为即将处理的请求建立数据库连接共享、将数据文件载入HashMap等
由初始化参数控制的初始化
1. 通过向web.xml的servlet元素添加init-param子元素,可以指定初始化参数的名称和值
2. 在servlet的init方法中,调用getServletConfig,获取ServletConfig对象的引用
3. 以init方法的参数名称为参数,调用ServletConfig的getInitParameter方法,返回值就是init参数的值,或null——如果在web.xml文件中没有找到这个init参数。
init(ServletConfig)方法中第一行初始化了config成员变量,GenericServlet类中getInitParameter("参数名")方法返回的是getConfig().getInitParameter("参数名");当Servlet实现类中直接使用 getInitParameter("参数名"),由于没有初始化成员变量config,getConfig()将返回null;故会产生空指针异常,所以应该总是在第一行加上super.init(con)。
阶段三:Servlet对象根据客户端的请求提供service
(1)HttpServlet类继承自Servlet的实现类javax.Servlet.GenericServlet,HttpServlet类中覆盖的service()方法完成了由一个service方法向多个方法(doGet、doPost、doDelete等方法)转换的过程,细化了service方法的功能,doGet、doPost分别表示当客户端"method”属性设置为get和post时调用;HttpServlet的子类中只需要覆盖其中的一个功能方法;
(2)Service方法会在服务器被访问时调用,Servlet对象的生命周期中service方法可能被多次调用,由于web-server启动后,服务器中公开的部分资源将处于网络中,当网络中的不同主机(客户端)并发访问服务器中的同一资源,服务器将开设多个线程处理不同的请求,多线程同时处理同一对象时,有可能出现数据并发访问的错误。多线程同时处理同一变量时(如:对同一文件进行写操作),且有读写操作时,必须考虑是否加上同步(synchronized),同步添加时,不要添加范围过大,有可能使程序变为纯粹的单线程,大大削弱了系统性能;只需要做到多个线程安全的访问相同的对象就可以了;
在servlet中需要同步的有:成员变量,网络资源,文件,静态变量,数据库连接等,用到这些一定要考虑线程同步,安全的问题。
阶段四:Servlet对象的销毁(destroy()方法)
当程序中的Servlet对象不再使用时,web-server将有可能销毁该对象,不同的web-server销毁时机不同;一个Servlet对象的销毁(destroy()方法)只执行一次;
我们可以在这个方法里做关闭数据库,关闭流,终止后台线程等收尾工作。注意:不要把持久化的对象存入内存中,程序结束时统一转移到外部存储设备;因为在web-server异常退出(断电等因素引起)的时候,未能来得及转移数据,这样的话会造成数据丢失。
从另一个角度看:
Servlet的生命周期:
在每个Servlet实例的生命中有三种类型事件,这三种事件分别对应于由Servlet引擎所唤醒的三个方法。
(1) init()
当Servlet第一次被装载时,Servlet引擎调用这个Servlet的init()方法,只调用一次。如果某个Servlet需要特殊的初始化需要,那么Servlet编写人员可以重写该方法来执行初始化任务。这是个可选的方法。如果某个Servlet不需要初始化,那么默认情况下将调用它的父类的init方法。系统保证,在init方法完成前,是不会调用Servlet去处理任何请求的。
(2)service()
这是Servlet最重要的方法,是真正处理请求的地方。对于每个请求,Servlet引擎将调用Servlet的service方法,并把Servlet请求对象和Servlet响应对象作为参数传递给它。
(3)destroy()
这是相对于init的可选方法,当Servlet即将被卸载时由Servlet引擎来调用,这个方法用来清除并释放在init方法中分配的资源。
Servlet的生命周期可以分以下几步:
1 装载Servlet,这一项操作一般是动态执行的。然而,Servlet通常会提供一个管理的选项,用于在Servlet启动时强制转载和初始化特定的Servlet。
2 服务器创建一个Servlet实例。
3 服务器调用Servlet的init方法。
4 一个客户端请求到达服务器端。
5 服务器创建一个请求对象。
6 服务器创建一个响应对象。
7 服务器激活Servlet的service()方法,传递请求对象和响应对象作为参数。
8 service()方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息。
9 service()方法使用响应对象的方法。将响应传回服务器,最终到达客户端。service()方法可以激活其他方法以处理请求。如doGet(),doPost()或者程序员自己开发的其他方法。
10 对于更多的客户端请求,服务器创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传给它,如此重复以上的循环,但无需再次调用init()方法,Servlet一般只初始化一次。
11 当服务器不再需要Servlet时,比如当服务器要关闭时,服务器调用Servlet的destroy()方法。
Servlet的工作过程:
Servlet的主要功能在于交互式的浏览和修改数据,生成动态Web内容。这个基本工作过程如下:
(1) 客户端发送请求至服务器端。
(2) 服务器上的Web容器装入Servlet,并为Servlet进程创建线程。请注意,Servlet是在出现第一个请求时装入的,在服务器关闭之前不会卸载它。Servlet也可以配置为Web应用程序启动时自动装载。
(3)Web容器将请求信息发送至Servlet。
(4)Servlet生成响应内容并将其返回给Web容器。响应内容动态生成,通常取决于客户端的请求。
(5)Web容器将响应返回给客户端。
(6)服务器关闭或者Servlet空闲时间超过一定限度时,调用destroy()方法退出
Servlet容器响应Web客户请求的步骤
SERVLET API中forward() 与redirect()的区别?
答:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用sendRedirect()方法。