深刻理解Spring系列之十三:IntrospectorCleanupListener解析

Introspector做用及影响

在分析IntrospectorCleanupListener以前,先了解一下Introspector。Introspector是JDK中java.beans包下的类,它为目标JavaBean提供了一种了解原类方法、属性和事件的标准方法。通俗的说,就是能够经过Introspector构建一个BeanInfo对象,而这个BeanInfo对象中包含了目标类中的属性、方法和事件的描述信息,而后可使用这个BeanInfo对象对目标对象进行相关操做。java

下面看一个简单的示例会很容易明白。为了简单,Student类中只有一个name属性。web




结果输出:Student{name='张三'}
复制代码

经过查看Introspector.getBeanInfo方法的源码会发现,Introspector在构建一个BeanInfo对象的时候,会将构建的BeanInfo对象和原类缓存到一个Map中,源码以下。spring


经过上的代码能够得出,Introspector间接持有了BeanInfo的强引用。若是使用Introspector操做了不少类,那么Introspector将间接持有这些BeanInfo的强引用。在发生垃圾收集的时候,检测到这些BeanInfo存在引用链,则这些类和对应的类加载器将不会被垃圾收集器回收,进而致使内存泄漏。因此,为了解决这个问题,在使用Introspector操做完成后,调用Introspector类的flushCaches方法清除缓存。缓存


经过上面的代码会发现,清除的时候是清空了整个缓存,由于没有很好的办法来肯定每一个缓存是属于哪一个应用的,因此清除的时候会清除全部应用的缓存。bash

IntrospectorCleanupListener解析

上面分析了Introspector的做用和影响,那IntrospectorCleanupListener和Introspector有什么关系呢?
IntrospectorCleanupListener是spring-web jar中的类,源码以下。框架


IntrospectorCleanupListener实现了ServletContextListener接口,也就是说,在web容器初始化(准确的说是在filters或servlets初始化以前)的时候会执行contextInitialized方法,在ServletContext销毁(准确的说是在filters和servlets销毁以后)的时候会执行contextDestroyed方法。从图中contextDestroyed方法,能够看到在销毁ServletContext的时候调用了Introspector.flushCaches方法,清空了对应缓存。IntrospectorCleanupListener中为何要这么作?难道是Spring使用Introspector操做后没有清空对应缓存?查看IntrospectorCleanupListener类的源码,会发现有这样一段标注。spa


大意是说,在使用Spring自己的时候并不须要使用此监听器,由于Spring本身的内部机制会当即清空对应的缓存。虽然,Spring自己不存在这样的问题,可是若是和其它框架结合使用,而其它框架有这个问题,如Struts、Quartz等,那就须要配置这个监听器,在销毁ServletContext的时候清空对应缓存。code

有一点须要注意的是,像这样一个简单的Introspector内存泄漏将会致使整个应用的类加载器不会被垃圾收集器回收,若是有内存泄漏的问题,能够考虑此因素。cdn

配置IntrospectorCleanupListener

在以往的工做经历中,屡次看到在web.xml中将IntrospectorCleanupListener配置成非第一个listener。xml


其实,看过源码的都知道,官方的表述是必须将此监听器配置成web.xml中的第一个listener,才能在合适的时间发挥最有效的做用。

缘由其实很简单,在Servlet3.0规范以前,监听器的调用是随机的,而从Servlet3.0开始,监听器的调用顺序是根据其在web.xml中配置的顺序,而且实现ServletContextListener的监听器,contextInitialized方法调用顺序是按照在web.xml中配置的顺序正序依次执行,而contextDestroyed方法的调用顺序是按照在web.xml中配置的顺序逆序依次执行。因此,若是IntrospectorCleanupListener被配置成了第一个listener,那么它的contextDestroyed方法将最后一个执行,将发挥最有效的清除做用;而若是不是,那么可能会残留未被清除的缓存。

相关文章
相关标签/搜索