一.Servlet
(从Tomcat处理用户请求,我们可以清晰的看到容器Servlet的生命周期管理过程:)
1、客户发出请求—>Web 服务器转发到Web容器Tomcat;
2、Tomcat主线程对用户的请求做出响应,创建两个对象:HttpServletRequest和HttpServletResponse;
3、从请求中的URL中找到正确Servlet,Tomcat为其创建或者分配一个线程,同时把2创建的两个对象传递给该线程;
4、分配的线程调用Servlet的servic()方法,根据请求参数的不同调用doGet()或者doPost()方法;
5、假设是HTTP GET请求,doGet()方法生成静态页面,并组合到响应对象里;
6、Servlet线程结束,Tomcat将响应对象转换为HTTP响应发回给客户,同时删除请求和响应对象。
从该过程中,我们可以理解Servlet的生命周期:Servlet类加载(对应3步);Servlet实例化(对应3步);调用init方法(对应3步);调用service()方法(对应4、5步);调用destroy()方法(servlet容器服务终止)。
简单测试:
package com.servlet;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 解析servlet的执行过程和生命周期
* @author shi
*/
public class ServletParse extends HttpServlet{
private int count = 0;//测试servlet的生命过程中是否只有一个servlet实例 ?
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("调用doPost()方法!");
}
//tomcat调用servlet实例的service方法,service调用doget或者dopost处理请求
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
count++;
System.out.println("这是--" + count + "--用户访问!");
doPost(req, resp);
}
//servlet销毁的方法,整个生命周期只会调用一次 ?
public void destroy() {
System.out.println("servlet is destory!");
}
//servlet的初始化方法,只有在第一次加载的时候调用 ?
public void init(ServletConfig config) throws ServletException {
System.out.println("servlet is init!");
}
}
测试:启动两个IE,请求这个servlet
结果:
servlet is init!
这是--1--用户访问!
调用doPost()方法!
这是--2--用户访问!
调用doPost()方法!
关闭tomcat:
servlet is destory
说明 : 容器中,每个servlet定义(servlet definition)只有一个实例
深入说明servlet多实例问题:
Servlet容器负责servlet的载入和实例化。这项工作可以在servlet引擎启动的时候进行,也可以延迟到当容器检测到需要servlet来处理某个请求的时候进行。
首先,servlet容器得能找到servlet对应的class文件。容器可以根据需要选择使用java类装载器(class loader)从本地文件系统、远程文件系统或者网络服务中加载这个类。
在这个类被加载之后,容器就创建该类的一个实例。
需要重点说明的是,在servlet容器中,一个servlet类可能会有多个实例。例如,多个servlet定义使用同一个类,但定义了不同的初始化参数。另外,如果servlet实现了SingleThreadModel接口,容器会创建一个实例池。
在servlet对象载入和实例化之后,容器必须在servlet处理客户端请求之前对它进行初始化。在初始化过程中,servlet可以读取持久化的配置数据,初始化昂贵(costly)的资源,例如jdbc数据库连接,并执行其他一次性的操作。容器通过调用Servlet接口的init来初始化servlet,对于每个servlet定义,容器将创建一个唯一的、实现了ServletConfig接口的对象,并将其作为参数传递给init方法。通过这个配置信息对象,servlet可以name-value的方式获取初始化参数。这个配置信息对象还包含了一个实现了ServletContext接口的对象,描述了servlet的运行环境信息。
然后调用servlet实例的service () 方法处理请求。
在处理客户端情况过程中,容器有可能并发访问servlet的service方法,以处理来自客户端的并发访问。开发人员必须注意这一点,保证servlet在并发情况下正确运行。
开发人员可以通过让servlet实现SingleThreadModel接口来避免这种缺省行为。通过实现这个接口可以保证同一时刻只允许一个请求线程调用service方法。需要重点说明的是,上述保证只限于对servlet实例的访问。对于能被多个servlet实例访问的对象,例如HttpSession的实例对象,还是能够被多个servlet实例同时访问的,不管servlet是否实现SingleThreadModel接口。Servlet容器可以多种方式实现这个要求,可以让访问一个servlet的请求排队,也可以提供servlet实例池。如果servlet属于可分布应用,容器可以在应用所分布的每个虚拟机中保持一个servlet实例池。
如果service方法使用了synchronized修饰符(或者是HttpServlet的doGet、doPost等通过service方法分发调用的方法),servlet容器将保证对该方法的排队访问,并且不能为其创建实例池。我们强烈建议不要同步service方法和HttpServlet的doGet、doPost这些服务方法。
请求request和响应response对象是不保证线程安全的。这意味着它们只应当在请求处理线程范围内被使用。请求对象和响应对象的引用不应当被传递给在其他线程中失效的对象,否则会产生无法预料的结果。
参考:http://blog.csdn.net/oldcrane/archive/2008/07/26/2713534.aspx
http://www.javaresearch.org/article/423.htm
二.Jsp
Jsp1.2规范,jsp经过两个阶段:翻译和请求
执行过程:当页面第一次被请求,jsp页面被翻译为一个servlet,并由该servlet为请求服务,这是翻译过程;随后对该页面的请求就直接请求jsp翻译为的servlet,交由servlet进一步处理,这是请求阶段!
问题 : jsp再第一次访问的时候,必须被编译为java代码,这意味着第一次访问的用户要等待一个延迟,而后面的访问则不需要等待!当jsp页面改变时,则需重新编译。
Jsp文件中Scriptlet的作用域:
1.<% %> 或者 <%= %> ,编译为servlet后,是存在与servlet的service方法中,所以是局部的,不可以被多请求共享!
2.<%! %> ,便以为servlet后,则成为servlet类的一个属性或者方法,当多线程访问的时候,它是被不同的请求所共享。
推出 :jsp的执行其实也就是一个servlet的执行!