Servlet本身也可以说就是一个Java文件,主要是担当Web浏览器(客户请求)与Http服务器上数据库等的中间层。即,承接浏览器(网页)的操作内容以及返回数据库的被访问记录。Servlet运行于服务器内部,并为Web服务器容器加载。也即是,Servlet的创建、运行、销毁等都已交由服务器容器管理。
那,可以看一下Servlet的生命周期:
(1)装载Servlet。该操作一般是动态执行。然而,Server通常会提供一个管理的选项,用于在Server启动时强制装载和初始化特定的Servlet。
(2)Server创建一个Servlet的实例
(3)Server调用Servlet的init()方法
(4)一个客户端的请求到达Server
(5)Server创建一个请求对象
(6)Server创建一个响应对象
(7)Server激活Servlet的Service()方法,传递请求、响应对象作为参数
(8)service()方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息
(9)service()方法使用响应对象的方法,将响应传回Server,最终到达客户端。Service()方法可以激活其他方法以处理请求,如doGet()或doPost()或程序员自己开发的新的方法。
(10)对于更多的客户端请求,Server创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传递给该方法。如此重复以上的循环,但无需再调用init()方法。因为,一般Servlet只初始化一次(只有一个实例),而当Server不再需要Servlet时(如异常或Server关闭),Server将调用Servlet的destroy()方法。
这个生命周期是相当好理解的。唯一的一点,就是,为什么Servlet只有一个实例?
出于性能的考虑:特别的对于门户网站而言,每一个Servlet在每一秒内的并发访问量都可以是成千上万的。在一个面向模块化开发的现在,常常一个点击操作就被定义为一个Servlet的实现,而如果Servlet的每一次被访问,都创建一个新的实例的话,服务器的可用资源消耗量将是一个相当重要的问题。退一步,一般Servlet的访问是很快的,每一个实例被快速的创建,又被快速的回收,GC的回收速度也跟不上,频繁的内存操作也将可能带来次生的问题。所以,Servlet的“单一实例化”是一个很重要的策略。另外一点,JSP实质上也是一个Servlet文件。一个页面被请求时,应该如何返回被请求页面的内容?是把一个“页面对象”返回?一个页面既有的框架内容大都基本固定(当然现在ajax横行,但其所基于的也是另一个页面的访问),其所可复用,重用的部分是相当的。
那么,它如何解决并发的问题呢?
其实,服务器容器会为每一个Servlet维护一个连接池,是c3p0或dbcp,都实现了DataSource,是其实现类。在编程实现上可理解为一个Application级别的Vector(自身线程安全的集合类),里面装载相当数量的Connection。每一个HTTP请求到来时,将分配一个Connection去响应请求。并在结束响应后,把Connection放回连接池。而如果当前可用Connection小于一个标准数量值,便自动添加新的Connection到Vector中。
所以,除了Servlet文件的编写及相关的部署外,所有的其他操作其实都交付给了服务器容器来进行管理。
另外,在Tomcat上,Container的实现形式有Wrapper、Context、Host、Engine,其级别递增。而,每一个Servlet就代表了一个Wrapper容器,那么很自然的,也可以“证明”上面的说法。