servlet没有main方法,他受一个叫做容器的java应用(比如Tomcat)控制。当web server应用(比如Apache)收到容器访问servlet请求,server不是先把请求直接给servlet,而是先给部署这个servlet的容器,容器“看到”找的是servlet,创建两个对象:HttpServletResponse和HttpServletRequest。容器根据URL找到servlet类,为这个请求创建或从池里分配线程,把request和response对象传给该线程,servlet的生命周期就开始了。
容器启动的时候这步就发生了,一启动容器就开始找部署的web项目。第一寻找其servlet类,第二就开始加载这个类。A servlet moves from does not exist to initialized (which really means ready to service client requests), beginning with a constructor. But the constructor makes only an object, not a servlet. To be a servlet, the object needs to be granted servletness. 所以在构造函数和init()之间的状态是一种“半死半活状态”,这时候不能接触很多作为servlet能接触的资源(like getting web app configuration info, or looking up a reference to another part of the application),所以init()之前都不要做什么操作,不要在构造函数中放东西。在创建servlet实例之后在为任何客户端请求服务之前,容器就调用init方法,你可以在处理请求之前在里面初始化你的servlet(重写此方法可以建立数据库连接,用其他对象注册自己等)。接着调用service方法(重写此方法的可能性不大),接着doGet/doPost(至少重写其中一个方法)。service结束后,线程就消亡或者放回池里。但第二个请求来,容器再次创建或从池里分配线程,继续service->doGet/doPost 序列。因此,在一个时间段内,有多少客户请求就有多少运行的线程,当然也受容器的策略、配置或资源限制(比如指定容器的最大并发线程数,当请求的客户数超过的时候,客户就必须等待)。容器运行多个线程来处理多个对一个servlet的请求(不管是不是同一个客户发出请求,一个请求一个线程),对于分布式web应用,一个JVM只对应一个servlet实例
成为一个servlet可以给你什么:一个ServletConfig对象,一个ServletContext。
ServletConfig对象:每个servlet有一个;Use it to pass deploy-time information to the servlet (a database or enterprise bean lookup name, for example)that you don’t want to hard-code into the servlet (servlet init parameters);访问ServletContext;Parameters are configured in the Deployment Descriptor(如<init-param>方式配置).我们可以通过servletconfig获得初始化参数(getServletConfig().getInitParameter(“foo”);),然后通过setAttribute dispatch给某个jsp页面,但是针对一个servlet的初始化参数很有局限性,所以引入针对应用的<context-param>,这时候通过servletContext获得初始化参数(getServletContext().getInitParameter(“foo”)),这个初始化参数可以被应用的任意servlet,jsp访问。注意初始化参数是一个deploy-time constants,不能在runtime改变他们,只能通过重新部署。
ServletContext :每个web app有一个(严格说来如果是分布式的话,应该是每个JVM一个);Use it to access web app parameters (also confi gured in the Deployment Descriptor) ;类似application bulletin-board功能,让其他应用也能访问;用来获得服务器信息,比如容器名字和版本,以及支持的API版本。ServletContextListener 可以监听ServletContext 的初始化(从ServletContext中获得初始化参数,用参数连接数据库,把连接作为attribute存起来以便所有应用都可以访问)和销毁阶段(关闭连接)。为了使context attributes 线程安全,可以对context进行同步锁的方式:
synchronized(getServletContext()) {
getServletContext().setAttribute(“foo”, “22”);
getServletContext().setAttribute(“bar”, “42”);
out.println(getServletContext().getAttribute(“foo”));
out.println(getServletContext().getAttribute(“bar”));
}
ServletResponse接口有两种流可供输出:ServletOutputStream for bytes, or a PrintWriter for character data.
例:1 PrintWriter writer = response.getWriter();
writer.println(“some text and HTML”);
2 ServletOutputStream out = response.getOutputStream();
out.write(aByteArray);
如果不想自己处理,可通过redirect给一个全新的URL(注意如果response已提交后不能再redirect),或者dispatch the request 给你应用中的某个组件(如jsp)。redirect会先返回给客户,客户再重新发请求,而dispatch 的话,服务器会直接转给某个组件。这样两种方式的区别就很明显了。
servlet->容器调用service()->doGet()/doPost() 产生动态页面并把页面放入response对象,记住此时容器仍然有引用指向该response对象!线程完成,容器把response对象转换为HTTP对象,送回客户端,然后删除request和response对象。