Introspector作用及影響
在分析IntrospectorCleanupListener之前,先了解一下Introspector迹卢。Introspector是JDK中java.beans包下的類慎冤,它為目標JavaBean提供了一種了解原類方法默责、屬性和事件的標準方法弛说。通俗的說,就是可以通過Introspector構(gòu)建一個BeanInfo對象秕铛,而這個BeanInfo對象中包含了目標類中的屬性、方法和事件的描述信息缩挑,然后可以使用這個BeanInfo對象對目標對象進行相關(guān)操作但两。
下面看一個簡單的示例會很容易明白。為了簡單供置,Student類中只有一個name屬性谨湘。
結(jié)果輸出:Student{name='張三'}
通過查看Introspector.getBeanInfo方法的源碼會發(fā)現(xiàn),Introspector在構(gòu)建一個BeanInfo對象的時候芥丧,會將構(gòu)建的BeanInfo對象和原類緩存到一個Map中紧阔,源碼如下。
通過上的代碼可以得出续担,Introspector間接持有了BeanInfo的強引用擅耽。如果使用Introspector操作了很多類,那么Introspector將間接持有這些BeanInfo的強引用赤拒。在發(fā)生垃圾收集的時候秫筏,檢測到這些BeanInfo存在引用鏈,則這些類和對應的類加載器將不會被垃圾收集器回收挎挖,進而導致內(nèi)存泄漏这敬。所以,為了解決這個問題蕉朵,在使用Introspector操作完成后崔涂,調(diào)用Introspector類的flushCaches方法清除緩存。
通過上面的代碼會發(fā)現(xiàn)始衅,清除的時候是清空了整個緩存冷蚂,因為沒有很好的辦法來確定每個緩存是屬于哪個應用的缭保,所以清除的時候會清除所有應用的緩存。
IntrospectorCleanupListener解析
上面分析了Introspector的作用和影響蝙茶,那IntrospectorCleanupListener和Introspector有什么關(guān)系呢艺骂?
IntrospectorCleanupListener是spring-web jar中的類,源碼如下隆夯。
IntrospectorCleanupListener實現(xiàn)了ServletContextListener接口钳恕,也就是說,在web容器初始化(準確的說是在filters或servlets初始化之前)的時候會執(zhí)行contextInitialized方法蹄衷,在ServletContext銷毀(準確的說是在filters和servlets銷毀之后)的時候會執(zhí)行contextDestroyed方法忧额。從圖中contextDestroyed方法,可以看到在銷毀ServletContext的時候調(diào)用了Introspector.flushCaches方法愧口,清空了對應緩存睦番。IntrospectorCleanupListener中為什么要這么做?難道是Spring使用Introspector操作后沒有清空對應緩存耍属?查看IntrospectorCleanupListener類的源碼托嚣,會發(fā)現(xiàn)有這樣一段標注。
大意是說恬涧,在使用Spring本身的時候并不需要使用此監(jiān)聽器注益,因為Spring自己的內(nèi)部機制會立即清空對應的緩存。雖然溯捆,Spring本身不存在這樣的問題丑搔,但是如果和其它框架結(jié)合使用,而其它框架有這個問題提揍,如Struts啤月、Quartz等,那就需要配置這個監(jiān)聽器劳跃,在銷毀ServletContext的時候清空對應緩存谎仲。
有一點需要注意的是,像這樣一個簡單的Introspector內(nèi)存泄漏將會導致整個應用的類加載器不會被垃圾收集器回收刨仑,如果有內(nèi)存泄漏的問題郑诺,可以考慮此因素。
配置IntrospectorCleanupListener
在以往的工作經(jīng)歷中杉武,多次看到在web.xml中將IntrospectorCleanupListener配置成非第一個listener辙诞。
其實,看過源碼的都知道轻抱,官方的表述是必須將此監(jiān)聽器配置成web.xml中的第一個listener飞涂,才能在合適的時間發(fā)揮最有效的作用。
原因其實很簡單,在Servlet3.0規(guī)范之前较店,監(jiān)聽器的調(diào)用是隨機的士八,而從Servlet3.0開始,監(jiān)聽器的調(diào)用順序是根據(jù)其在web.xml中配置的順序梁呈,并且實現(xiàn)ServletContextListener的監(jiān)聽器婚度,contextInitialized方法調(diào)用順序是按照在web.xml中配置的順序正序依次執(zhí)行,而contextDestroyed方法的調(diào)用順序是按照在web.xml中配置的順序逆序依次執(zhí)行捧杉。所以陕见,如果IntrospectorCleanupListener被配置成了第一個listener秘血,那么它的contextDestroyed方法將最后一個執(zhí)行味抖,將發(fā)揮最有效的清除作用;而如果不是灰粮,那么可能會殘留未被清除的緩存仔涩。