• 常用的Introspector相关类

    主要介绍一下几个核心类所提供的方法。

    Introspector

    Introspector类似于BeanInfo的静态工厂类,主要是提供静态方法通过Class实例获取到BeanInfo,得到BeanInfo之后,就能够获取到其他描述符。主要方法:

    BeanInfo

    BeanInfo是一个接口,具体实现是GenericBeanInfo,通过这个接口可以获取一个类的各种类型的描述符。主要方法:

    这里要注意一点,通过BeanInfo#getPropertyDescriptors()获取到的PropertyDescriptor数组中,除了Bean属性的之外,「还会带有一个属性名为classPropertyDescriptor实例」,它的来源是ClassgetClass方法,如果不需要这个属性那么最好判断后过滤,这一点需要紧记,否则容易出现问题。

    PropertyDescriptor

    PropertyDescriptor类表示JavaBean类通过存储器(SetterGetter)导出一个属性,它应该是内省体系中最常见的类。主要方法:

    举个例子:

    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能被垃圾回收器正确回收。

    也就是说JDKIntrospector缓存管理是有一定缺陷的。但是如果使用在Spring体系则不会出现这种问题,因为SpringIntrospector缓存的管理移交到Spring自身而不是JDK(或者在Web容器销毁后完全不管),在加载并分析完所有类之后,会针对类加载器对Introspector缓存进行清理,避免内存泄漏的问题,详情可以看CachedIntrospectionResultsSpringBoot刷新上下文的方法AbstractApplicationContext#refresh()finally代码块中存在清理缓存的方法AbstractApplicationContext#resetCommonCaches();。但是有很多程序和框架在使用了JavaBeans Introspector之后,都没有进行清理工作,比如QuartzStruts等,这类操作会成为内存泄漏的隐患。

    小结

    (本文完 e-a-20200811 c-1-d 封面来源于《言叶之庭》)

    聊聊Java内省Introspector-LMLPHP

    冷饭新炒:理解Snowflake算法的实现原理

    Canal v1.1.4版本避坑指南

    Java线程生命周期与状态切换

    本文分享自微信公众号 - Throwable文摘(throwable-doge)。
    如有侵权,请联系 support@oschina.cn 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    09-03 12:51