常用的Introspector相关类
主要介绍一下几个核心类所提供的方法。
Introspector
Introspector
类似于BeanInfo
的静态工厂类,主要是提供静态方法通过Class
实例获取到BeanInfo
,得到BeanInfo
之后,就能够获取到其他描述符。主要方法:
BeanInfo
BeanInfo
是一个接口,具体实现是GenericBeanInfo
,通过这个接口可以获取一个类的各种类型的描述符。主要方法:
这里要注意一点,通过BeanInfo#getPropertyDescriptors()
获取到的PropertyDescriptor
数组中,除了Bean
属性的之外,「还会带有一个属性名为class
的PropertyDescriptor
实例」,它的来源是Class
的getClass
方法,如果不需要这个属性那么最好判断后过滤,这一点需要紧记,否则容易出现问题。
PropertyDescriptor
PropertyDescriptor
类表示JavaBean
类通过存储器(Setter
和Getter
)导出一个属性,它应该是内省体系中最常见的类。主要方法:
举个例子:
public class Main {
public static void main(String[] args) throws Exception {
BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
if (!"class".equals(propertyDescriptor.getName())) {
System.out.println(propertyDescriptor.getName());
System.out.println(propertyDescriptor.getWriteMethod().getName());
System.out.println(propertyDescriptor.getReadMethod().getName());
System.out.println("=======================");
}
}
}
public static class Person {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
}
输出结果:
age
setAge
getAge
=======================
id
setId
getId
=======================
name
setName
getName
=======================
不正当使用Introspector会导致内存溢出
如果框架或者程序用到了JavaBeans Introspector
,那么就相当于「启用了一个系统级别的缓存」,这个缓存会存放一些曾加载并分析过的Javabean
的引用,当Web
服务器关闭的时候,由于这个缓存中存放着这些Javabean
的引用,所以垃圾回收器不能对Web
容器中的JavaBean
对象进行回收,导致内存越来越大。还有一点值得注意,清除Introspector
缓存的唯一方式是刷新整个缓存缓冲区,这是因为JDK
没法判断哪些是属于当前的应用的引用,所以刷新整个Introspector
缓存缓冲区会导致把服务器的所有应用的Introspector
缓存都删掉。Spring
中提供的org.springframework.web.util.IntrospectorCleanupListener
就是为了解决这个问题,它会在Web
服务器停止的时候,清理一下这个Introspector
缓存,使那些Javabean
能被垃圾回收器正确回收。
也就是说JDK
的Introspector
缓存管理是有一定缺陷的。但是如果使用在Spring
体系则不会出现这种问题,因为Spring
把Introspector
缓存的管理移交到Spring
自身而不是JDK
(或者在Web
容器销毁后完全不管),在加载并分析完所有类之后,会针对类加载器对Introspector
缓存进行清理,避免内存泄漏的问题,详情可以看CachedIntrospectionResults
和SpringBoot
刷新上下文的方法AbstractApplicationContext#refresh()
中finally
代码块中存在清理缓存的方法AbstractApplicationContext#resetCommonCaches();
。但是有很多程序和框架在使用了JavaBeans Introspector
之后,都没有进行清理工作,比如Quartz
、Struts
等,这类操作会成为内存泄漏的隐患。
小结
(本文完 e-a-20200811 c-1-d 封面来源于《言叶之庭》)
本文分享自微信公众号 - Throwable文摘(throwable-doge)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。