背景:面试中很基础的一个问题,所以有必要好好整理一番。
Servlet体系结构是建立在 Java 多线程机制上的,它的生命周期由 Web 容器负责。
当客户端第一次请求某个 Servlet 时,Servlet 容器将会根据 web.xml 的配置文件实例化这个 Servlet 类。当有新的客户端请求该 Servlet 时,一般不会再实例化该 Servlet 类。
当有多个请求时,Servlet 容器会起多个线程来访问同一个 Servlet 实例的 service() 方法,如果该 Servlet 实例中有共享的实例变量,需要注意多线程安全问题。
生命周期
Servlet 生命周期定义了 Servlet 从创建到毁灭的整个过程,总共分为四个步骤。
调用 init() 方法初始化
调用 service() 方法来处理客户端的请求
调用 destroy() 方法释放资源,标记自身为可回收
被垃圾回收器回收
init() 方法
init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,用于 Servlet的初始化,初始化的数据,可以在整个生命周期中使用。
在下列时刻Servlet容器装载Servlet,共三种情况:
1,Servlet容器启动时自动装载某些Servlet,实现它只需要在web.XML文件中的之间添加如下代码:
<loadon-startup>1loadon-startup>
说明:
在servlet的配置当中,5的含义是:
标记容器是否在启动的时候就加载这个servlet。
当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;
当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。
正数的值越小,启动该servlet的优先级越高。
2,在Servlet容器启动后,客户首次向Servlet发送请求
3,Servlet类文件被更新后,重新装载Servlet
Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期内,init()方法只被调用一次。
初始化阶段步骤:
Ø Servlet容器加载servlet类,把它的. Class文件中的数据读到内存中。
Ø Servlet容器创建servletConfig对象。servletConfig对象包含了servlet的初始化配置信息。此外servlet容器还会使得servletConfig对象与当前的web应用的servletContext对象关联。
Ø Servlet容器创建servlet对象。
Ø Servlet容器调用servlet对象的init(ServletConfig config)方法。
通过初始化步骤,创建了servlet对象和servletConfig对象,并且servlet对象与servletConfig对象关联,而servletConfig对象又与当前对象的servletContext对象关联。当servlet容器完成servlet后,servlet对象只要通过getServletContext()方法就能得到web应用的servletContext对象。
service() 方法
service() 方法是执行实际任务的主要方法。 Servlet 容器(Tomcat、Jetty等)调用 service() 方法来处理来自客户端(浏览器)的请求,并把相应结果返回给客户端。
每次 Servlet 容器接收到一个 Http 请求, Servlet 容器会产生一个新的线程并调用 Servlet实例的 service 方法。 service 方法会检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut、doDelete 方法。所以,在编码请求处理逻辑的时候,我们只需要关注 doGet()、或doPost()的具体实现即可。
对于Tomcat来说,它会将传递过来的参数放在一个Hashtable中,该Hashtable的定义是:
private Hashtable paramHashStringArray = new Hashtable();
这是一个String-->String[]的键值映射。
HashMap线程不安全的,Hashtable线程安全。
destroy() 方法
destroy() 方法也只会被调用一次,在 Servlet 生命周期结束时调用。destroy() 方法主要用来清扫“战场”,执行如关闭数据库连接、释放资源等行为。
调用 destroy 方法之后,servlet 对象被标记为垃圾回收,等待 JVM 的垃圾回收器进行处理。
引申
Servlet何时被创建:
1,默认情况下,当WEB客户第一次请求访问某个Servlet的时候,WEB容器将创建这个Servlet的实例。
2,当web.xml文件中如果元素中指定了子元素时,Servlet容器在启动web服务器时,将按照顺序创建并初始化Servlet对象。
注意:在web.xml文件中,某些Servlet只 有元素,没有元素,这样我们无法通过url的方式访问这些 Servlet,这种Servlet通常会在元素中配置一个子元素,让容 器在启动的时候自动加载这些Servlet并调用init()方法,完成一些全局性的初始化工作。
Web应用何时被启动:
1,当Servlet容器启动的时候,所有的Web应用都会被启动
2,控制器启动web应用
Servlet与JSP的比较:
有许多相似之处,都可以生成动态网页。
JSP的优点是擅长于网页制作,生成动态页面比较直观,缺点是不容易跟踪与排错。
Servlet是纯Java语言,擅长于处理流程和业务逻辑,缺点是生成动态网页不直观。
Servlet工作原理:
首先简单解释一下Servlet接收和响应客户请求的过程,首先客户发送一个请求,Servlet是调用service()方法对请求进行响应 的,通过源代码可见,service()方法中对请求的方式进行了匹配,选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层 的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet,doPost等等这些方法 的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或 doPost等这些方法。
每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到 Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不 特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此 HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。
Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以 HttpServlet中实现了service()方法,并将请求ServletRequest,ServletResponse强转为 HttpRequest和HttpResponse。
public void service(ServletRequest req,ServletResponse res)
throws ServletException,IOException
{
HttpRequest request;
HttpResponse response;
try
{
req = (HttpRequest)request;
res = (HttpResponse)response;
}catch(ClassCastException e)
{
throw new ServletException("non-HTTP request response");
}
service(request,response);
}
代码的最后调用了HTTPServlet自己的service(request,response)方法,然后根据请求去调用对应的doXXX方法,因为HttpServlet中的doXXX方法都是返回错误信息,
protected void doGet(HttpServletRequest res,HttpServletResponse resp)
throws ServletException,IOException
{
String protocol = req.getProtocol();
String msg = IStrings.getString("http.method_get_not_supported");
if(protocol.equals("1.1"))
{
resp.sendError(HttpServletResponse.SC.METHOD.NOT.ALLOWED,msg);
}
esle
{
resp.sendError(HttpServletResponse.SC_BAD_REQUEST,msg);
}
}