最近在做將公司項(xiàng)目從Weblogic移植到GlassFish的工作,遇到的其中一個(gè)比較頭疼的問題就是Weblogic的LifeCycleManager。Weblogic提供了一個(gè)ApplicationLifecycleListener類可以在application部署和卸載時(shí)做一些工作,這是Weblogic獨(dú)有的特性,并不是J2EE的規(guī)范,在GlassFish中也沒有對應(yīng)的實(shí)現(xiàn),據(jù)說在將來的版本中考慮支持。GlassFish中目前提供的LifeCycleListener不能滿足該要求,它只能在GlassFish啟動(dòng)和停止的時(shí)候去做工作,而且,采用了這種方式,等于又將我們的應(yīng)用綁定在了GlassFish上。思前想后,想到了一個(gè)臨時(shí)的解決方案,雖然不能達(dá)到Weblogic的LCM的作用,但能滿足當(dāng)前應(yīng)用中的需求,就是采用在ear中加入一個(gè)web module,在web container啟動(dòng)和停止的時(shí)候去完成這項(xiàng)工作。由于較長的一段時(shí)間沒有寫過Web應(yīng)用了,對一些知識(shí)點(diǎn)已經(jīng)遺忘了,眼前對于是采用Servlet的init和destroy來作為實(shí)現(xiàn),還是采用ServletContextListener,有點(diǎn)不清楚他們之間確切的區(qū)別,只好重新作以復(fù)習(xí)了。
        下面先來看Servlet是如何工作的,Servlet的生命周期包含了四個(gè)階段:
        1、加載和實(shí)例化
        當(dāng)servlet容器啟動(dòng)時(shí),或者在容器需要這個(gè)servlet來響應(yīng)第一個(gè)請求時(shí),創(chuàng)建servlet實(shí)例。因?yàn)槿萜魇峭ㄟ^Java的反射API來創(chuàng)建servlet實(shí)例,調(diào)用的是servlet的默認(rèn)構(gòu)造方法,所以在編寫servlet的時(shí)候,不應(yīng)該提供帶參數(shù)的構(gòu)造方法。
        2、初始化
        在servlet實(shí)例化之后,容器將調(diào)用servlet的init方法初始化這個(gè)對象。初始化的目的是為了讓servlet對象在處理請求錢完成一些初始化的工作,每個(gè)servlet實(shí)例的init方法只被調(diào)用一次。在初始化期間,servlet實(shí)例可以使用ServletConfig對象從Web應(yīng)用的配置信息(在web.xml中配置)中獲取初始化的參數(shù)信息。在初始化期間,如果發(fā)生錯(cuò)誤,Servlet實(shí)例可以拋出ServletException或者UnavailableException來通知容器。ServletException用于指明一般的初始化失敗,例如沒有找到初始化參數(shù);而UnavailableException用于通知容器該Servlet實(shí)例不可用。例如,數(shù)據(jù)庫服務(wù)器沒有啟動(dòng),數(shù)據(jù)庫連接無法建立,Servlet就可以拋出UnavailableException向容器指出它暫時(shí)或永久不可用。
        3、請求處理
        Servlet容器調(diào)用Servlet的service方法對請求進(jìn)行處理。要注意的是,在service方法調(diào)用之前,init方法必須成功執(zhí)行。在service方法執(zhí)行期間,如果發(fā)生錯(cuò)誤,Servlet實(shí)例可以拋出ServletException或者UnavailableException。如果UnavailableException指示了該實(shí)例永久不可用,Servlet容器將調(diào)用實(shí)例的destroy()方法,釋放該實(shí)例。
        4、服務(wù)終止
        當(dāng)容器檢測到一個(gè)Servlet實(shí)例應(yīng)該從服務(wù)中被移除的時(shí)候,容器就會(huì)調(diào)用實(shí)例的destroy方法。當(dāng)需要釋放內(nèi)存或者容器關(guān)閉時(shí),容器就會(huì)調(diào)用Servlet實(shí)例的destroy方法。在destroy方法調(diào)用之后,容器會(huì)釋放這個(gè)Servlet實(shí)例,該實(shí)例隨后會(huì)被Java的垃圾收集器所回收,如果再次需要這個(gè)Servlet處理請求,Servlet容器會(huì)創(chuàng)建一個(gè)新的 Servlet實(shí)例。

        再看ServletContextListener。ServletContextListener接口能夠監(jiān)聽ServletContext對象的生命周期,也就是監(jiān)聽Web應(yīng)用的生命周期。當(dāng)Servlet容器啟動(dòng)或終止Web應(yīng)用時(shí),會(huì)觸發(fā)ServletContextEvent事件,該事件由ServletContextListener來處理。在ServletContextListener 接口中定義了處ServletContextEvent事件的兩個(gè)方法:
        contextInitialized(ServletContextEvent event):當(dāng)Servlet容器啟動(dòng)Web應(yīng)用時(shí)調(diào)用該方法。在調(diào)用完該方法之后,容器再對Filter初始化,并且對那些在Web應(yīng)用啟動(dòng)時(shí)就需要被初始化的Servlet進(jìn)行初始化。 
        contextDestroyed(ServletContextEvent event):當(dāng)Servlet容器終止Web應(yīng)用時(shí)調(diào)用該方法。在調(diào)用該方法之前,容器會(huì)先銷毀所有的Servlet和Filter過濾器。
        因此我們可以得知,在Web應(yīng)用啟動(dòng)時(shí),Servlet容器會(huì)先調(diào)用ServletContextListener的contextInitalized方法,再調(diào)用Servlet的init方法;當(dāng)Web應(yīng)用終止時(shí),Servlet容器先調(diào)用Servlet的destroy方法,再調(diào)用ServletContextListener的contextDestroyed方法。由此可見,在Web應(yīng)用的生命周期中,ServletContext對象是最早被創(chuàng)建,最晚被銷毀。