摘要:一些使用方式其实在我上面写一些构造函数的时候,我想大家应该已经感受到与反射相关了,起码我感觉上是这样的,所以我一开始想到这样的案例形式,通过反射与这个属性描述类去赋予我的类。
本文首发于本博客 猫叔的博客,转载请申明出处前言
感谢GY丶L粉丝的提问:属性描述器PropertyDescriptor是干嘛用的?
本来我也没有仔细了解过描述符这一块的知识,不过粉丝问了,我就抽周末的时间看看,顺便学习一下,粉丝问的刚好是PropertyDescriptor这个属性描述符,我看了下源码。
/** * A PropertyDescriptor describes one property that a Java Bean * exports via a pair of accessor methods. */ public class PropertyDescriptor extends FeatureDescriptor { //... }
emmmm,假装自己英语能厉害的说,属性描述符描述了一个属性,即Java Bean 通过一对访问器方法来导出。(没错,他确实是存在于java.beans包下的)
通过类关系图,可以知道,我们应该提前了解一下FeatureDescriptor才行了。很好,起码目前还没有设计抽象类或者接口。
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 { //... }
okay,这是很合理的设计方式,FeatureDescriptor为类似PropertyDescriptor、EvebtSetDescriptor、MethodDescriptor的描述符提供了一些共用的常量信息。同时它也提供一个扩展功能,方便任意属性或键值对可以于设计功能相关联。
这里简单的说下,在我大致看了一下源码后(可能不够详细,最近有点忙,时间较赶),FeatureDescriptor主要是针对一下属性的一些get/set,同时这些属性都是基本通用于PropertyDescriptor、EvebtSetDescriptor、MethodDescriptor。
private boolean expert; // 专有 private boolean hidden; // 隐藏 private boolean preferred; // 首选 private String shortDescription; //简单说明 private String name; // 编程名称 private String displayName; //本地名称 private Hashtabletable; // 属性表
其实该类还有另外几个方法,比如深奥的构造函数等等,这里就不深入探讨了。
PropertyDescriptor那么我们大致知道了FeatureDescriptor,接下来就可以来深入了解看看这个属性描述符PropertyDescriptor。
说到属性,大家一定会想到的就是get/set这个些基础的东西,当我打开PropertyDescriptor源码的时候,我也看到了一开始猜想的点。
private final MethodRef readMethodRef = new MethodRef(); private final MethodRef writeMethodRef = new MethodRef(); private String writeMethodName; private String readMethodName;
这里的代码是我从源码中抽离的一部分,起码我们这样看可以大致理解,是分为写和读的步骤,那么就和我们初学java的get/set是一致的。
同时我还看到了,这个,及其注释。
// 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;
这好像可以解释,为什么我们的属性在生成get/set的时候,第一个字母变成大写?!注释好像确实是这样写的。
由于可能需要一个Bean对象,所以我以前在案例中先创建了一个Cat类。
public class Cat { private String name; private String describe; private int age; private int weight; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescribe() { return describe; } public void setDescribe(String describe) { this.describe = describe; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }构造函数
起码目前,我还不知道我应该怎么使用它,那么我们就一步一步来吧,我看到它有好几个构造函数,这是一个有趣而且有难度的事情,我们先试着创建一个PropertyDescriptor吧。
第一种构造函数
/** * 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)); }
这个好像是参数最少的,它只需要我们传入一个属性字符串,还有对应的类就好了,其实它也是调用了另一个构造函数,只是它会帮我们默认生成读方法和写方法。方法中的Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName)其实就是自己拼出一个默认的get/set方法,大家有兴趣可以去看看源码。
那么对应的实现内容,我想大家应该都想到了。
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()); }
第二种构造函数
/** * 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); }
没错,这个构造函数就是第一种构造函数内部二次调用的,所需要的参数很简单,同时我也希望大家可以借鉴这个方法中的一些检测方式。这次的实现方式也是同样的形式。
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()); }
第三种构造函数
/** * 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); }
这个不用传类,因为你需要传递两个实际的方法进来,所以主要三个对应属性的参数既可。看看大致的实现内容
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()); }
好了,大致介绍了几种构造函数与实现方式,起码我们现在知道它需要什么。
一些使用方式其实在我上面写一些构造函数的时候,我想大家应该已经感受到与反射相关了,起码我感觉上是这样的,所以我一开始想到这样的案例形式,通过反射与这个属性描述类去赋予我的类。
public static void main(String[] args) throws Exception { //获取类 Class classType = Class.forName("com.example.demo.beans.Cat"); Object catObj = classType.newInstance(); //获取Name属性 PropertyDescriptor catPropertyOfName = new PropertyDescriptor("name",classType); //得到对应的写方法 Method writeOfName = catPropertyOfName.getWriteMethod(); //将值赋进这个类中 writeOfName.invoke(catObj,"river"); Cat cat = (Cat)catObj; System.out.println(cat.toString()); }
运行结果还是顺利的。
Cat{name="river", describe="null", age=0, weight=0}
可以看到,我们确实得到了一个理想中的对象。
那么我是不是可以改变一个已经创建的对象呢?
public static void main(String[] args) throws Exception { //一开始的默认对象 Cat cat = new Cat("river","黑猫",2,4); //获取name属性 PropertyDescriptor catPropertyOfName = new PropertyDescriptor("name",Cat.class); //得到读方法 Method readMethod = catPropertyOfName.getReadMethod(); //获取属性值 String name = (String) readMethod.invoke(cat); System.out.println("默认:" + name); //得到写方法 Method writeMethod = catPropertyOfName.getWriteMethod(); //修改值 writeMethod.invoke(cat,"copy"); System.out.println("修改后:" + cat); }
上面的demo是,我先创建了一个对象,然后通过属性描述符读取name值,再进行修改值,最后输出的对象的值也确实改变了。
默认:river收尾
修改后:Cat{name="copy", describe="黑猫", age=2, weight=4}
这是一个有趣的API,我想另外两个(EvebtSetDescriptor、MethodDescriptor)应该也差不多,大家可以再通过此方法去探究,只有自己尝试一次才能学到这里面的一些东西,还有一些项目场景的使用方式,不过一般的业务场景应该很少使用到这个API。那么这个东西究竟可以干什么呢?我想你试着敲一次也许有一些答案了。
公众号:Java猫说现架构设计(码农)兼创业技术顾问,不羁平庸,热爱开源,杂谈程序人生与不定期干货。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/73099.html
摘要:关于它的数据转换使用了如下两种机制隶属于规范。这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进中,这种对象称为值对象,或。 每篇一句 千古以来要饭的没有要早饭的,知道为什么吗? 相关阅读 【小家Spring】聊聊Spring中的数据转换:Converter、ConversionService、TypeConverter、Pro...
摘要:源码分析源码一览本节,我们先来看一下填充属性的方法,即。所有的属性值是在方法中统一被注入到对象中的。检测是否存在与相关的或。这样可以在很大程度上降低源码分析的难度。若候选项是非类型,则表明已经完成了实例化,此时直接返回即可。 1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的。我在前面几篇文章中介绍过 Spring 创建 bea...
摘要:前言最近开发遇到一个问题,两个对象进行属性值拷贝。理论上来说可以直接借助来进行拷贝,奈何两个对象属性名不同,懵逼脸。 1、前言 最近开发遇到一个问题,两个对象进行属性值拷贝。理论上来说可以直接借助org.springframework.beans.BeanUtils.copyProperties(Object source, Object target)来进行拷贝,奈何两个对象属性名不...
摘要:在这一步里,将配置文件的信息装入到容器的定义注册表中,但此时还未初始化。注册后处理器根据反射机制从中找出所有类型的,并将它们注册到容器后处理器的注册表中。是属性编辑器的注册表,主要作用就是注册和保存属性编辑器。 点击进入我的博客 1 Spring容器整体流程 1.1 ApplicationContext内部原理 AbstractApplicationContext是Applicati...
摘要:前置知识在分析源码前,我们先温习一下以下的知识点。类在中万物皆对象,而且我们在代码中写的每一个类也都是对象,是类的对象。总结一个看似简单的工具类,其实里面包含的基础的知识点非常多,包括类型信息反射线程安全引用类型类加载器等。 背景 在我们着手一个Java Web项目的时候,经常会遇到DO、VO、DTO对象之间的属性拷贝,若采用get、set的方法来进行赋值的话,代码会相当冗长丑陋,一般...
阅读 1395·2021-11-22 15:11
阅读 2841·2019-08-30 14:16
阅读 2756·2019-08-29 15:21
阅读 2917·2019-08-29 15:11
阅读 2453·2019-08-29 13:19
阅读 2987·2019-08-29 12:25
阅读 419·2019-08-29 12:21
阅读 2831·2019-08-29 11:03