This article mainly analyzes the usage of Introspector. Introspector is a tool class that deals specifically with JavaBean and is used to get the descriptors in JavaBean. The commonly used descriptor-related classes of JavaBean are BeanInfo, PropertyDescriptor, MethodDescriptor, BeanDescriptor, EventSetDescriptor and ParameterDescriptor. The following will slowly analyze how these classes are used, as well as some of the features of Introspector.

What is JavaBean

JavaBean is a special (actually, it’s okay to say ordinary, it’s not very special) class, mainly used to pass data information, the methods in this class are mainly used to access private fields, and the method names conform to some naming rules (fields are private, each field has Setter and Getter methods, methods and fields named to meet the first letter lower case hump naming ). If information is passed between two modules, it can be encapsulated in a JavaBean, which is called a Value Object or VO. This information is stored in private variables of the class and is obtained through the Setter, Getter methods. The information of JavaBean corresponds to the concept of BeanInfo in Introspector, which contains all the Descriptor of JavaBean, mainly PropertyDescriptor, MethodDescriptor (MethodDescriptor contains ParameterDescriptor), BeanDescriptor and EventSetDescriptor.

The difference between Field and PropertiesDescriptor

If it is a strict JavaBean (the Field name is not repeated and the Field has Setter and Getter methods), its PropertyDescriptor will merge the results by parsing the Setter and Getter methods and finally get the corresponding PropertyDescriptor instance. So PropertyDescriptor contains the property name and the property’s Setter and Getter methods (if they exist).

The difference between Introspector and Reflection

  • Reflection: Reflection is the process of getting all the information about a class at runtime. You can get all the defined information about the class (including member variables, member methods, constructors, etc.) You can manipulate the fields, methods, constructors, and other parts of the class. It can be imagined as mirror reflection or looking in the mirror, such operation is with an objective color, that is, the reflection to obtain the class information is necessarily correct.
  • Introspector: introspection based on reflection, mainly used to operate JavaBean, based on the specification of JavaBean to Bean information descriptor resolution, based on the class Setter and Getter methods, you can get to the class descriptor. It can be imagined as “self-reflection”, which is subjective and not necessarily correct (if a class does not have Setter and Getter methods for its properties, introspection cannot be used).

It mainly introduces the methods provided by several core classes.

Introspector

Introspector is similar to the static factory class of BeanInfo, which mainly provides static methods to get BeanInfo through Class instances, and after getting BeanInfo, you will be able to get other descriptors. Main methods:

  • public static BeanInfo getBeanInfo(Class<? > beanClass): Get the BeanInfo instance from the Class instance.

BeanInfo

BeanInfo is an interface, the concrete implementation of which is GenericBeanInfo, through which you can get the descriptors of various types of a class. Main methods:

  • BeanDescriptor getBeanDescriptor(): get JavaBean descriptors.
  • EventSetDescriptor[] getEventSetDescriptors(): Get all the EventSetDescriptors of the JavaBean.
  • PropertyDescriptor[] getPropertyDescriptors(): Get all the PropertyDescriptors of the JavaBean.
  • MethodDescriptor[] getMethodDescriptors(): Get all the MethodDescriptors of the JavaBean.

Note that the PropertyDescriptor array obtained through BeanInfo#getPropertyDescriptors() will have an instance of PropertyDescriptor named class in addition to the Bean property, and its source is the getClass method of Class.

If this property is not needed then it is better to filter it after judging.

PropertyDescriptor

The PropertyDescriptor class represents the JavaBean class that exports a property through the memory (Setter and Getter) and it should be the most common class in the introspection system. Main methods.

  • synchronized Class<? > getPropertyType(): Get the Class object of the property.
  • synchronized Method getReadMethod(): gets the method used to read the value of the property.
  • synchronized Method getWriteMethod(): get the method used to write the value of the property.
  • int hashCode(): get the hash value of the object.
  • synchronized void setReadMethod(Method readMethod): set the method used to read the value of the property (Getter).
  • synchronized void setWriteMethod(Method writeMethod): sets the method used to write the value of the property (Setter).

As an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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;
        }
    }
}

Output results:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
age
setAge
getAge
=======================
id
setId
getId
=======================
name
setName
getName
=======================

Improper use of Introspector can lead to memory overflows

If the framework or program uses JavaBeans Introspector, then it is equivalent to enabling a system-level cache, which holds references to some Javabeans that have been loaded and analyzed, and when the web server is closed, because this cache holds references to these Javabeans, the garbage collector cannot recycle the JavaBean objects in the Web container, resulting in an increasing amount of memory. It is also worth noting that the only way to clear the Introspector cache is to flush the entire cache buffer. This is because the JDK cannot determine which references belong to the current application, so flushing the entire Introspector cache buffer will result in deleting the Introspector cache for all applications on the server. The org.springframework.web.util.IntrospectorCleanupListener provided in Spring is designed to solve this problem by cleaning up this Introspector cache when the Web server stops, so that those Javabean can be properly recycled by the garbage collector.

That is, the Introspector cache management of the JDK is somewhat flawed. But this is not a problem if used in the Spring system, because Spring transfers the management of the Introspector cache to Spring itself and not to JDK (or to the Web container after it is destroyed), and after loading and analyzing all classes, it cleans up the Introspector cache against the class loader. Introspector cache cleanup to avoid memory leaks, see CachedIntrospectionResults and SpringBoot refresh context method AbstractApplicationContext#refresh() for details. finally block of code exists to clear the cache method AbstractApplicationContext#resetCommonCaches();. But there are many programs and frameworks that do not clean up after using JavaBeans Introspector, such as Quartz, Struts, etc. This type of operation can become a potential memory leak.

Summary

  • In the standard JavaBean, you can consider using the Introspector system to parse the JavaBean, mainly to facilitate the use of reflection before the time to quickly get to the Setter and Getter methods of the JavaBean.
  • In the Spring system, in order to prevent the JDK’s cache of introspection information from being recovered by the garbage collection mechanism and causing memory overflow, the main operation can be prevented by configuring the IntrospectorCleanupListener, but there is another way, which is through the CachedIntrospectionResults class self-managed Introspector cache (this way is the elegant way, so as to avoid refreshing the entire Introspector cache buffer and cause other applications Introspector is also emptied), that is, the Jdk self-managed Introspector-related cache to Spring itself. The method AbstractApplicationContext#refresh() in the finally block of the SpringBoot refresh context has the method AbstractApplicationContext#resetCommonCaches();, which calls the CachedIntrospectionResults#clearClassLoader(getClassLoader() method to clear all the references to the cache in the Introspector under the specified ClassLoader.

Reference https://www.throwx.cn/2019/12/25/java-introspector-usage/