This article was first published in the blog cat uncle’s blog, reproduced please state the source

preface

Thanks to GY, L fans for asking: what is the PropertyDescriptor used for?

I didn’t really know much about descriptors, but when my fans asked me, I took some time off over the weekend to learn about them, and they asked me about property descriptors, so I looked at the source code.

/** * A PropertyDescriptor describes one property that a Java Bean * exports via a pair of accessor methods. */
public class PropertyDescriptor extends FeatureDescriptor {
    / /...
}
Copy the code

Emmmm, pretending to speak good English, a property descriptor describes a property that a Java Bean exports through a pair of accessor methods. (Yes, it does exist under the java.beans package)

You can see from the class diagram, we should have looked at the FeatureDescriptor in advance. Good, at least not yet abstract classes or interfaces.

FeatureDescriptor

/** * The FeatureDescriptor class is the common baseclass for PropertyDescriptor, * EventSetDescriptor, and MethodDescriptor, etc. * 

* It supports some common information that can be set and retrieved for * any of the introspection descriptors. *

* In addition it provides an extension mechanism so that arbitrary * attribute/value pairs can be associated with a design feature. */

public class FeatureDescriptor { / /... } Copy the code

Okay, this is a very reasonable way to design, and feature Descriptors provide some common constant information for descriptors like property Descriptors, Evebtset Descriptors, method Descriptors. It also provides an extension so that any property or key-value pair can be associated with the design function.

So just to keep things simple, after I’ve looked at the source code, the FeatureDescriptor is basically a bunch of get/set attributes, And these properties are basically generic to PropertyDescriptor, EvebtSetDescriptor, MethodDescriptor.

    private boolean expert; / / proprietary
    private boolean hidden; / / hide
    private boolean preferred; / / the preferred
    private String shortDescription; // A brief explanation
    private String name; // Programming name
    private String displayName; // Local name
    private Hashtable<String, Object> table; / / property sheet
Copy the code

There are several other methods of this class, such as esoteric constructors, which I won’t go into here.

PropertyDescriptor

So we’ve got a general idea of feature descriptors, and now we can look at this PropertyDescriptor in more detail.

When you think about properties, you’re going to think about get/set, the basic stuff, and when I opened up the PropertyDescriptor source code, I saw the point that I thought it was going to be.

    private final MethodRef readMethodRef = new MethodRef();
    private final MethodRef writeMethodRef = new MethodRef();
    private String writeMethodName;
    private String readMethodName;
Copy the code

Here is the code I extracted from the source part, at least we can roughly understand, is divided into write and read steps, so we learn Java get/set is consistent.

I also saw this and the notes.

    // The base name of the method name which will be prefixed with the
    // read and write method. If name == "foo" then the baseName is "Foo"
    private String baseName;
Copy the code

This seems to explain why the first letter of our property becomes uppercase when we generate get/set. ! That’s exactly what the comments seem to say.

Since you might need a Bean object, I used to create a Cat class in my case.

public class Cat {

    private String name;

    private String describe;

    private int age;

    private int weight;

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescribe(a) {
        return describe;
    }

    public void setDescribe(String describe) {
        this.describe = describe;
    }

    public int getAge(a) {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getWeight(a) {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight; }}Copy the code

The constructor

At least for now, I don’t know how I’m going to use it, so let’s do it step by step, and I see that it has several constructors, which is an interesting and difficult thing to do, so let’s try to create a PropertyDescriptor first.

  • The first constructor
    /**
     * Constructs a PropertyDescriptor for a property that follows
     * the standard Java convention by having getFoo and setFoo
     * accessor methods.  Thus if the argument name is "fred", it will
     * assume that the writer method is "setFred" and the reader method
     * is "getFred" (or "isFred" for a boolean property).  Note that the
     * property name should start with a lower case character, which will
     * be capitalized in the method names.
     *
     * @param propertyName The programmatic name of the property.
     * @param beanClass The Class object for the target bean.  For
     *          example sun.beans.OurButton.class.
     * @exception IntrospectionException if an exception occurs during
     *              introspection.
     */
    public PropertyDescriptor(String propertyName, Class
        beanClass)
                throws IntrospectionException {
        this(propertyName, beanClass,
                Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName),
                Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName));
    }
Copy the code

This one seems to have the fewest arguments, it just needs us to pass in a property string and the corresponding class, but it also calls another constructor, but it will generate the read and write methods for us by default. The introspector.is_prefix + nameGenerator.capitalize (capitalize name) method is a default get/set method that you spell.

So the corresponding implementation, I think you’ve all figured it out.

    public static void main(String[] args) throws Exception {
        PropertyDescriptor CatPropertyOfName = new PropertyDescriptor("name", Cat.class);
        System.out.println(CatPropertyOfName.getPropertyType());
        System.out.println(CatPropertyOfName.getPropertyEditorClass());
        System.out.println(CatPropertyOfName.getReadMethod());
        System.out.println(CatPropertyOfName.getWriteMethod());
    }
Copy the code
  • The second constructor
/**
     * This constructor takes the name of a simple property, and method
     * names for reading and writing the property.
     *
     * @param propertyName The programmatic name of the property.
     * @param beanClass The Class object for the target bean.  For
     *          example sun.beans.OurButton.class.
     * @param readMethodName The name of the method used for reading the property
     *           value.  May be null if the property is write-only.
     * @param writeMethodName The name of the method used for writing the property
     *           value.  May be null if the property is read-only.
     * @exception IntrospectionException if an exception occurs during
     *              introspection.
     */
    public PropertyDescriptor(String propertyName, Class
        beanClass, String readMethodName, String writeMethodName)
                throws IntrospectionException {
        if (beanClass == null) {
            throw new IntrospectionException("Target Bean class is null");
        }
        if (propertyName == null || propertyName.length() == 0) {
            throw new IntrospectionException("bad property name");
        }
        if ("".equals(readMethodName) || "".equals(writeMethodName)) {
            throw new IntrospectionException("read or write method name should not be the empty string");
        }
        setName(propertyName);
        setClass0(beanClass);

        this.readMethodName = readMethodName;
        if(readMethodName ! =null && getReadMethod() == null) {
            throw new IntrospectionException("Method not found: " + readMethodName);
        }
        this.writeMethodName = writeMethodName;
        if(writeMethodName ! =null && getWriteMethod() == null) {
            throw new IntrospectionException("Method not found: " + writeMethodName);
        }
        // If this class or one of its base classes allow PropertyChangeListener,
        // then we assume that any properties we discover are "bound".
        // See Introspector.getTargetPropertyInfo() method.
        Class[] args = { PropertyChangeListener.class };
        this.bound = null! = Introspector.findMethod(beanClass,"addPropertyChangeListener", args.length, args);
    }
Copy the code

Yes, this constructor is called twice inside the first constructor. It takes very simple arguments, and I hope you can use some of the methods in this method. This time the implementation is of the same form.

    public static void main(String[] args) throws Exception {
        PropertyDescriptor CatPropertyOfName = new PropertyDescriptor("name", Cat.class,"getName"."setName");
        System.out.println(CatPropertyOfName.getPropertyType());
        System.out.println(CatPropertyOfName.getPropertyEditorClass());
        System.out.println(CatPropertyOfName.getReadMethod());
        System.out.println(CatPropertyOfName.getWriteMethod());
    }
Copy the code
  • The third constructor
    /**
     * This constructor takes the name of a simple property, and Method
     * objects for reading and writing the property.
     *
     * @param propertyName The programmatic name of the property.
     * @param readMethod The method used for reading the property value.
     *          May be null if the property is write-only.
     * @param writeMethod The method used for writing the property value.
     *          May be null if the property is read-only.
     * @exception IntrospectionException if an exception occurs during
     *              introspection.
     */
    public PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod)
                throws IntrospectionException {
        if (propertyName == null || propertyName.length() == 0) {
            throw new IntrospectionException("bad property name");
        }
        setName(propertyName);
        setReadMethod(readMethod);
        setWriteMethod(writeMethod);
    }
Copy the code

You don’t need to pass this class, because you need to pass in two actual methods, so the main three parameters of the corresponding properties are just fine. Take a look at the rough implementation

    public static void main(String[] args) throws Exception { Class<? > classType = Cat.class; Method CatNameOfRead = classType.getMethod("getName");
        Method CatNameOfWrite = classType.getMethod("setName", String.class);
        PropertyDescriptor CatPropertyOfName = new PropertyDescriptor("name", CatNameOfRead,CatNameOfWrite);
        System.out.println(CatPropertyOfName.getPropertyType());
        System.out.println(CatPropertyOfName.getPropertyEditorClass());
        System.out.println(CatPropertyOfName.getReadMethod());
        System.out.println(CatPropertyOfName.getWriteMethod());
    }
Copy the code

Ok, so with a few constructors and implementations outlined, at least we now know what it requires.

Some ways to use

In fact, when I wrote the constructors up here, I thought you might already have a sense of reflection, or at least I did, so I started with the idea of giving my class the case form of reflection and this attribute description class.

    public static void main(String[] args) throws Exception {
        / / class
        Class classType = Class.forName("com.example.demo.beans.Cat");
        Object catObj = classType.newInstance();
        // Get the Name attribute
        PropertyDescriptor catPropertyOfName = new PropertyDescriptor("name",classType);
        // get the corresponding writing method
        Method writeOfName = catPropertyOfName.getWriteMethod();
        // Assign values to this class
        writeOfName.invoke(catObj,"river");
        Cat cat = (Cat)catObj;
        System.out.println(cat.toString());
    }
Copy the code

The results were good.

Cat{name=’river’, describe=’null’, age=0, weight=0}

As you can see, we do have an ideal object.

Can I change an already created object?

    public static void main(String[] args) throws Exception {
        // The default object to start with
        Cat cat = new Cat("river"."Black cat".2.4);
        // Get the name attribute
        PropertyDescriptor catPropertyOfName = new PropertyDescriptor("name",Cat.class);
        // Get the read method
        Method readMethod = catPropertyOfName.getReadMethod();
        // Get the attribute value
        String name = (String) readMethod.invoke(cat);
        System.out.println("Default:" + name);
        // get the write method
        Method writeMethod = catPropertyOfName.getWriteMethod();
        / / modify the value
        writeMethod.invoke(cat,"copy");
        System.out.println("After modification:" + cat);
    }
Copy the code

In the demo above, I created an object, then read the name value through the property descriptor, then modified the value, and finally the value of the output object actually changed.

Cat{name=’copy’, describe=’ black Cat ‘, age=2, weight=4}

finishing

It’s an interesting API, and I think the other two (EvebtSetDescriptor, MethodDescriptor) are pretty much the same, so you can explore it again, and you can only learn something about it if you try it once, and how to use some of the project scenarios, However, a typical business scenario would rarely use this API. So what does this thing do? I think you might have some answers if you tried.

Public number: Java cat said

Now architecture designer (code farmer) and entrepreneurial technology consultant, unruly mediocre, love open source, talk about program life and irregular dry goods.