Java introspection mechanism

describe

Here the author start with my own understanding of what is introspection mechanism of Java, Java reflection mechanism is a kind of reflection encapsulation, is a Java provided to development view of an object attribute and operation, method of operation and the description of the object, may say that are abstract, then we will give some examples to illustrate Of course, This mechanism has some limitations when it comes to property manipulation, which we'll keep in suspense. Later in this article, we'll explain Java's introspection mechanism in particular, which inevitably involves several classes: Introspector, PropertyDescriptor, PropertyEditor, and BeanInfo. Let's describe these classes in detail and illustrate them with some examples so that you can understand the introspection mechanism of Java more clearlyCopy the code

PropertyDescriptor

Let's start with an example:public class User {
    private String name;

    private String aName;

    getter / setter / toString
}

public static void main (String[] args) throws Exception {
    User user = new User();
    System.out.println( user );
    PropertyDescriptor propertyDescriptor = new PropertyDescriptor( "name", User.class );
    Method readMethod = propertyDescriptor.getReadMethod();
    System.out.println( readMethod.invoke( user ) );
    Method writeMethod = propertyDescriptor.getWriteMethod();
    writeMethod.invoke( user, "hello"); System.out.println( user ); } Output: User{name='null', aName='null'}
    null
    User{name='hello', aName='null'}} we created an object, and then output the result with all the properties in itnull, this need not explain bar.......... And then we create a PropertyDescriptor, and we pass in the PropertyDescriptor name and the class that it's in, and then you might think, well, there should be two properties in the PropertyDescriptor, maybe targetClass, PropertyName, which holds the two values that are passed in. Okay, so go down to...... GetReadMethod gets a read method. What is a read method? It's getXXXX, so the property we're describing is name, so we get getName, and the return value is a Method object, and by reflection call, passing in the user object, we get the name property of that object, getWriteMethod gets the Method, What is the writing method? SetXXXX, so the property we're describing is name, we get setName, we return a Method object, we call it by reflection, we pass in the user object and the expected value, and when we output the user object again, It will be found that name has been set to a good value of the above steps after the description of the operation, you should have a sense of it..... Yeah, property descriptors, property descriptors, are sort of encapsulation of property reflection, so we can manipulate properties directly when we use constructorsnewThe inside will help us figure out the get and set methods for the property, which are the read and write methods for the property respectively, and the operations that we do on the PropertyDescriptor are just operations on the get and set methods using reflection, However, the internal implementation is somewhat interesting. It uses soft and weak references to hold references to methods and Class objects. The soft and weak references will also be uploaded to the Nuggets in the future. This is an operation to prevent memory leaks, or a direct reference to........ Let's look at another example where we reuse the previous User class:public static void main (String[] args) throws Exception{
    PropertyDescriptor propertyDescriptor = new PropertyDescriptor( "aName", User.class ); Method readMethod = propertyDescriptor.getReadMethod(); Method writeMethod = propertyDescriptor.getWriteMethod(); } Get and set methods for the aName attribute in the User classpublic String getaName(a) {
            return aName;
        }

        public void setaName(String aName) {
            this.aName = aName; } this is the idea of automatically generated, or the editor automatically generated get/set is such, but we will find that, when the implementation of the main method, has an error, an error message: Java beans. IntrospectionException: Method not found: IsAName Because Java introspection mechanism is a certain standard, to find a property of the get/set method, you need to uppercase the first letter of the property, and then before the get/set prefix splices, because the editor generated get/set method in the first two letters of the property is a large one and a small one, Does not change the first two letters of its set and get methods, which results in an error, which should be notedCopy the code

PropertyEditor PropertyEditor

PropertyEditor is also a Java extension of properties that can be used for type conversion. For example, when we set a property, we want to convert the String type to another type and then set it to an object. The Java PropertyEditor interface has a direct subclass, PropertyEditorSupport, which is basically defined like this (pseudocode):public class PropertyEditorSupport implements PropertyEditor {
    private Object value;

    public Object getValue(a) {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public void setAsText(String text) throws java.lang.IllegalArgumentException {
        if (value instanceof String) {
            setValue(text);
            return;
        }
        throw new java.lang.IllegalArgumentException(text);
    }

    public String getAsText(a) {
        return (this.value ! =null)?this.value.toString()
                : null; }} analysis: The above code is a small part of PropertyEditorSupport, which is a simple class with an Object property value that provides get/set methods. The setAsText and getAsText methods are provided. Normally we need to inherit this class to do the extension. For example, if the value is a Date type and we want to set it to a string, we need to inherit this class and rewrite the setAsText method as follows:public class DatePropertyEditor extends PropertyEditorSupport {
        @Override
        public String getAsText(a) {
            Date value = (Date) getValue();
            DateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
            return dateFormat.format( value );
        }

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            DateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
            try {
                Date date = dateFormat.parse( text );
                setValue( date );
            } catch(ParseException e) {}}} PropertyEditorSupport: PropertyEditorSupport: PropertyEditorSupport: PropertyEditorSupport: PropertyEditorSupport: PropertyEditorSupportpublic static void main(String[] args) throws Exception{
    DatePropertyEditor datePropertyEditor = new DatePropertyEditor();
    datePropertyEditor.setAsText( "The 2020-03-06 15:33:33"); Date value = (Date) datePropertyEditor.getValue(); System.out.println( value ); } create a custom Date attribute editor. When you call setAsText and pass in a string Date, it is parsed as a Date and stored in value. GetValue returns a Date. There are also source properties and listeners in this class. Source is usually an object we need to operate on. Listeners are listeners. When setValue is called, in addition to direct assignmentthisIn addition to value = value, all listeners will be fired, and listener methods will be called. An event object will be passed in the listener method, and the event object will hold the source, that is, PropertyEditorSupport has a source property of type Object and a collection of listener objects. This source property can be retrieved when listener Object methods are called (present in events, calling listener methods puts an event Object, The source is passed to construct the event object), because the two are not used for the moment, so it is not extended, there is no application scenario, the extension is not much meaningCopy the code

BeanInfo

GenericBeanInfo is an interface with a subclass GenericBeanInfo. GenericBeanInfo is a package access class in Introspector. Let's look at its structure briefly:class GenericBeanInfo extends SimpleBeanInfo {
    private BeanDescriptor beanDescriptor;
    private PropertyDescriptor[] properties;
    privateMethodDescriptor[] methods; } analysis: I've just listed a couple of simple properties, but that's enough, so BeanDescriptor is just a reference to a Class object, so all of the property descriptors of that Class are in PropertyDescriptor, so all of the method descriptors are in MethodDescriptor, BeanInfo is a collection of attributes, methods, and other reflection operations that encapsulate a class. This uses the class Introspector as follows:public static void main(String[] args) throws Exception { BeanInfo beanInfo = Introspector.getBeanInfo( Customer.class ); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor(); } We get the BeanInfo of a Class through Introspector. We get the attribute descriptor, method descriptor, and Class object through BeanInfo. We can put data into an instance of this classCopy the code

CachedIntrospectionResults

-------------- Don't panic if you see the code below...... Please look directly at the text analysis to see the code below I -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the public class CachedIntrospectionResults {static final ConcurrentMap < class <? >, CachedIntrospectionResults> strongClassCache = new ConcurrentHashMap<>(64); static final ConcurrentMap<Class<? >, CachedIntrospectionResults> softClassCache = new ConcurrentReferenceHashMap<>(64); private final BeanInfo beanInfo; private final Map<String, PropertyDescriptor> propertyDescriptorCache; static CachedIntrospectionResults forClass(Class<? > beanClass) throws BeansException { CachedIntrospectionResults results = strongClassCache.get(beanClass); if (results ! = null) { return results; } results = softClassCache.get(beanClass); if (results ! = null) { return results; } results = new CachedIntrospectionResults(beanClass); ConcurrentMap<Class<? >, CachedIntrospectionResults> classCacheToUse; if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) || isClassLoaderAccepted(beanClass.getClassLoader())) { classCacheToUse = strongClassCache; } else { classCacheToUse = softClassCache; } CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results); return (existing ! = null ? existing : results); }} analysis: CachedIntrospectionResults this class is provided by the Spring to the class of introspection mechanism using utility class, is different from the Introspector place lies in, this class provides class reflection data cache, The introspection PropertyDescriptor for these data cache First of all, let's take a look at the top of the two static methods, global variables to save the two maps, the key is the class object, the value is CachedIntrospectionResults object, And as you can imagine, this is kind of like a singleton that you implement with Map, that provides caching, and then you can access it directly with the static method and look at its two properties, BeanInfo, propertyDescriptorCache, Need not the author describes it, the former is the reflection of the function of a class of encapsulation, it has already been dedicated to illustrate the of this class, which is the property name to the property descriptor Map Map, this should also don't have to explain in detail the CachedIntrospectionResults class instance, Encapsulates a BeanInfo class through introspection mechanism and property descriptor mapping, global static variables to save all the classes to operate CachedIntrospectionResults class instance cache, With strong and soft references is to prevent memory leaks Take a look at forClass, said according to the Class object for the Class CachedIntrospectionResults Class instance, you can see, from a strong reference to the cache first, didn't get the access from the soft references, Here we are not familiar with the big four reference types, can think according to the Class object from the Map directly to obtain the corresponding CachedIntrospectionResults Class instance, if you don't have access to, create a summary on the Map by: CachedIntrospectionResults class objects through global variable Map provides the internal mechanism of province BeanInfo caching information, in order to facilitate our corresponding class introspection information by static methodsCopy the code

BeanWrapper

public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
	@Nullable
	privateCachedIntrospectionResults cachedIntrospectionResults; } analysis: As you can see, the BeanWrapper instances built a CachedIntrospectionResults, before analysis DispathcherSerlvet initialization process, illustrates the function of BeanWrapper, But there is no analysis of how to realize the attribute set, this time I have deep analysis Since the internal storage of a CachedIntrospectionResults instance, you should be easy to think of, the internal is through the instance to obtain the corresponding property description, And then get the read and write methods to set the properties? That's true, so let's take a look at setPropertyValue, there are a lot of overloaded methods, let's take the method that's set directly by the property name and the property value as an examplepublic void setPropertyValue(String propertyName, @Nullable Object value) {
    AbstractNestablePropertyAccessor nestedPa=getPropertyAccessorForPropertyPath(propertyName);
    
    PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
    nestedPa.setPropertyValue(tokens, newPropertyValue(propertyName, value)); } simple parsing, AbstractNestablePropertyAccessor provides a nested function of attribute set, such as an entity class and another entity class, this kind of circumstance also can set a success, don't tube what the code, follow the code below, You can see something that suddenly opens up... NestedPa. SetPropertyValue call processLocalProperty (tokens, pv), acquiring a PropertyHandler processLocalProperty, That's how we set the property, so let's see what this PropertyHandler isprotected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
    PropertyDescriptor pd 
        = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
    if(pd ! =null) {
        return new BeanPropertyHandler(pd);
    }
    return null; } in BeanWrapper, the PropertyDescriptor is finally set to !!!!Copy the code

conclusion

Starting from Java introspection mechanism, we introduced four core classes of Java introspection mechanism, namely PropertyDescriptor, PropertyEditor(used for type conversion), Introspector and BeanInfo. Also le clear reflection originally is the reflection of encapsulation, and raises the CachedIntrospectionResults class, the class is the Spring of the data obtained from the internal mechanism of province cache, which led to the realization of the BeanWrapper principle, Built inside a CachedIntrospectionResults object, to the operation of the property will eventually become the object of PropertyDescriptor operation, to be sure, CachedIntrospectionResults provides nested property is set, the need to pay attention to, in fact, the Spring encapsulation of Java introspection mechanism are also many, many say, perhaps in the near future, I'll write a series describing Spring's encapsulation of Java's introspection mechanism, which you can expect to see at.....Copy the code