摘要:一些使用方式其實(shí)在我上面寫(xiě)一些構(gòu)造函數(shù)的時(shí)候,我想大家應(yīng)該已經(jīng)感受到與反射相關(guān)了,起碼我感覺(jué)上是這樣的,所以我一開(kāi)始想到這樣的案例形式,通過(guò)反射與這個(gè)屬性描述類去賦予我的類。
本文首發(fā)于本博客 貓叔的博客,轉(zhuǎn)載請(qǐng)申明出處前言
感謝GY丶L粉絲的提問(wèn):屬性描述器PropertyDescriptor是干嘛用的?
本來(lái)我也沒(méi)有仔細(xì)了解過(guò)描述符這一塊的知識(shí),不過(guò)粉絲問(wèn)了,我就抽周末的時(shí)間看看,順便學(xué)習(xí)一下,粉絲問(wèn)的剛好是PropertyDescriptor這個(gè)屬性描述符,我看了下源碼。
/** * A PropertyDescriptor describes one property that a Java Bean * exports via a pair of accessor methods. */ public class PropertyDescriptor extends FeatureDescriptor { //... }
emmmm,假裝自己英語(yǔ)能厲害的說(shuō),屬性描述符描述了一個(gè)屬性,即Java Bean 通過(guò)一對(duì)訪問(wèn)器方法來(lái)導(dǎo)出。(沒(méi)錯(cuò),他確實(shí)是存在于java.beans包下的)
通過(guò)類關(guān)系圖,可以知道,我們應(yīng)該提前了解一下FeatureDescriptor才行了。很好,起碼目前還沒(méi)有設(shè)計(jì)抽象類或者接口。
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,這是很合理的設(shè)計(jì)方式,F(xiàn)eatureDescriptor為類似PropertyDescriptor、EvebtSetDescriptor、MethodDescriptor的描述符提供了一些共用的常量信息。同時(shí)它也提供一個(gè)擴(kuò)展功能,方便任意屬性或鍵值對(duì)可以于設(shè)計(jì)功能相關(guān)聯(lián)。
這里簡(jiǎn)單的說(shuō)下,在我大致看了一下源碼后(可能不夠詳細(xì),最近有點(diǎn)忙,時(shí)間較趕),F(xiàn)eatureDescriptor主要是針對(duì)一下屬性的一些get/set,同時(shí)這些屬性都是基本通用于PropertyDescriptor、EvebtSetDescriptor、MethodDescriptor。
private boolean expert; // 專有 private boolean hidden; // 隱藏 private boolean preferred; // 首選 private String shortDescription; //簡(jiǎn)單說(shuō)明 private String name; // 編程名稱 private String displayName; //本地名稱 private Hashtabletable; // 屬性表
其實(shí)該類還有另外幾個(gè)方法,比如深?yuàn)W的構(gòu)造函數(shù)等等,這里就不深入探討了。
PropertyDescriptor那么我們大致知道了FeatureDescriptor,接下來(lái)就可以來(lái)深入了解看看這個(gè)屬性描述符PropertyDescriptor。
說(shuō)到屬性,大家一定會(huì)想到的就是get/set這個(gè)些基礎(chǔ)的東西,當(dāng)我打開(kāi)PropertyDescriptor源碼的時(shí)候,我也看到了一開(kāi)始猜想的點(diǎn)。
private final MethodRef readMethodRef = new MethodRef(); private final MethodRef writeMethodRef = new MethodRef(); private String writeMethodName; private String readMethodName;
這里的代碼是我從源碼中抽離的一部分,起碼我們這樣看可以大致理解,是分為寫(xiě)和讀的步驟,那么就和我們初學(xué)java的get/set是一致的。
同時(shí)我還看到了,這個(gè),及其注釋。
// 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的時(shí)候,第一個(gè)字母變成大寫(xiě)?!注釋好像確實(shí)是這樣寫(xiě)的。
由于可能需要一個(gè)Bean對(duì)象,所以我以前在案例中先創(chuàng)建了一個(gè)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; } }構(gòu)造函數(shù)
起碼目前,我還不知道我應(yīng)該怎么使用它,那么我們就一步一步來(lái)吧,我看到它有好幾個(gè)構(gòu)造函數(shù),這是一個(gè)有趣而且有難度的事情,我們先試著創(chuàng)建一個(gè)PropertyDescriptor吧。
第一種構(gòu)造函數(shù)
/** * 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)); }
這個(gè)好像是參數(shù)最少的,它只需要我們傳入一個(gè)屬性字符串,還有對(duì)應(yīng)的類就好了,其實(shí)它也是調(diào)用了另一個(gè)構(gòu)造函數(shù),只是它會(huì)幫我們默認(rèn)生成讀方法和寫(xiě)方法。方法中的Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName)其實(shí)就是自己拼出一個(gè)默認(rèn)的get/set方法,大家有興趣可以去看看源碼。
那么對(duì)應(yīng)的實(shí)現(xiàn)內(nèi)容,我想大家應(yīng)該都想到了。
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()); }
第二種構(gòu)造函數(shù)
/** * 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); }
沒(méi)錯(cuò),這個(gè)構(gòu)造函數(shù)就是第一種構(gòu)造函數(shù)內(nèi)部二次調(diào)用的,所需要的參數(shù)很簡(jiǎn)單,同時(shí)我也希望大家可以借鑒這個(gè)方法中的一些檢測(cè)方式。這次的實(shí)現(xiàn)方式也是同樣的形式。
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()); }
第三種構(gòu)造函數(shù)
/** * 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); }
這個(gè)不用傳類,因?yàn)槟阈枰獋鬟f兩個(gè)實(shí)際的方法進(jìn)來(lái),所以主要三個(gè)對(duì)應(yīng)屬性的參數(shù)既可??纯创笾碌膶?shí)現(xiàn)內(nèi)容
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()); }
好了,大致介紹了幾種構(gòu)造函數(shù)與實(shí)現(xiàn)方式,起碼我們現(xiàn)在知道它需要什么。
一些使用方式其實(shí)在我上面寫(xiě)一些構(gòu)造函數(shù)的時(shí)候,我想大家應(yīng)該已經(jīng)感受到與反射相關(guān)了,起碼我感覺(jué)上是這樣的,所以我一開(kāi)始想到這樣的案例形式,通過(guò)反射與這個(gè)屬性描述類去賦予我的類。
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); //得到對(duì)應(yīng)的寫(xiě)方法 Method writeOfName = catPropertyOfName.getWriteMethod(); //將值賦進(jìn)這個(gè)類中 writeOfName.invoke(catObj,"river"); Cat cat = (Cat)catObj; System.out.println(cat.toString()); }
運(yùn)行結(jié)果還是順利的。
Cat{name="river", describe="null", age=0, weight=0}
可以看到,我們確實(shí)得到了一個(gè)理想中的對(duì)象。
那么我是不是可以改變一個(gè)已經(jīng)創(chuàng)建的對(duì)象呢?
public static void main(String[] args) throws Exception { //一開(kāi)始的默認(rèn)對(duì)象 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("默認(rèn):" + name); //得到寫(xiě)方法 Method writeMethod = catPropertyOfName.getWriteMethod(); //修改值 writeMethod.invoke(cat,"copy"); System.out.println("修改后:" + cat); }
上面的demo是,我先創(chuàng)建了一個(gè)對(duì)象,然后通過(guò)屬性描述符讀取name值,再進(jìn)行修改值,最后輸出的對(duì)象的值也確實(shí)改變了。
默認(rèn):river收尾
修改后:Cat{name="copy", describe="黑貓", age=2, weight=4}
這是一個(gè)有趣的API,我想另外兩個(gè)(EvebtSetDescriptor、MethodDescriptor)應(yīng)該也差不多,大家可以再通過(guò)此方法去探究,只有自己嘗試一次才能學(xué)到這里面的一些東西,還有一些項(xiàng)目場(chǎng)景的使用方式,不過(guò)一般的業(yè)務(wù)場(chǎng)景應(yīng)該很少使用到這個(gè)API。那么這個(gè)東西究竟可以干什么呢?我想你試著敲一次也許有一些答案了。
公眾號(hào):Java貓說(shuō)現(xiàn)架構(gòu)設(shè)計(jì)(碼農(nóng))兼創(chuàng)業(yè)技術(shù)顧問(wèn),不羈平庸,熱愛(ài)開(kāi)源,雜談程序人生與不定期干貨。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/73099.html
摘要:關(guān)于它的數(shù)據(jù)轉(zhuǎn)換使用了如下兩種機(jī)制隸屬于規(guī)范。這種類中的方法主要用于訪問(wèn)私有的字段,且方法名符合某種命名規(guī)則。如果在兩個(gè)模塊之間傳遞信息,可以將信息封裝進(jìn)中,這種對(duì)象稱為值對(duì)象,或。 每篇一句 千古以來(lái)要飯的沒(méi)有要早飯的,知道為什么嗎? 相關(guān)閱讀 【小家Spring】聊聊Spring中的數(shù)據(jù)轉(zhuǎn)換:Converter、ConversionService、TypeConverter、Pro...
摘要:源碼分析源碼一覽本節(jié),我們先來(lái)看一下填充屬性的方法,即。所有的屬性值是在方法中統(tǒng)一被注入到對(duì)象中的。檢測(cè)是否存在與相關(guān)的或。這樣可以在很大程度上降低源碼分析的難度。若候選項(xiàng)是非類型,則表明已經(jīng)完成了實(shí)例化,此時(shí)直接返回即可。 1. 簡(jiǎn)介 本篇文章,我們來(lái)一起了解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對(duì)象中的。我在前面幾篇文章中介紹過(guò) Spring 創(chuàng)建 bea...
摘要:前言最近開(kāi)發(fā)遇到一個(gè)問(wèn)題,兩個(gè)對(duì)象進(jìn)行屬性值拷貝。理論上來(lái)說(shuō)可以直接借助來(lái)進(jìn)行拷貝,奈何兩個(gè)對(duì)象屬性名不同,懵逼臉。 1、前言 最近開(kāi)發(fā)遇到一個(gè)問(wèn)題,兩個(gè)對(duì)象進(jìn)行屬性值拷貝。理論上來(lái)說(shuō)可以直接借助org.springframework.beans.BeanUtils.copyProperties(Object source, Object target)來(lái)進(jìn)行拷貝,奈何兩個(gè)對(duì)象屬性名不...
摘要:在這一步里,將配置文件的信息裝入到容器的定義注冊(cè)表中,但此時(shí)還未初始化。注冊(cè)后處理器根據(jù)反射機(jī)制從中找出所有類型的,并將它們注冊(cè)到容器后處理器的注冊(cè)表中。是屬性編輯器的注冊(cè)表,主要作用就是注冊(cè)和保存屬性編輯器。 點(diǎn)擊進(jìn)入我的博客 1 Spring容器整體流程 1.1 ApplicationContext內(nèi)部原理 AbstractApplicationContext是Applicati...
摘要:前置知識(shí)在分析源碼前,我們先溫習(xí)一下以下的知識(shí)點(diǎn)。類在中萬(wàn)物皆對(duì)象,而且我們?cè)诖a中寫(xiě)的每一個(gè)類也都是對(duì)象,是類的對(duì)象??偨Y(jié)一個(gè)看似簡(jiǎn)單的工具類,其實(shí)里面包含的基礎(chǔ)的知識(shí)點(diǎn)非常多,包括類型信息反射線程安全引用類型類加載器等。 背景 在我們著手一個(gè)Java Web項(xiàng)目的時(shí)候,經(jīng)常會(huì)遇到DO、VO、DTO對(duì)象之間的屬性拷貝,若采用get、set的方法來(lái)進(jìn)行賦值的話,代碼會(huì)相當(dāng)冗長(zhǎng)丑陋,一般...
閱讀 1420·2021-11-22 15:11
閱讀 2848·2019-08-30 14:16
閱讀 2766·2019-08-29 15:21
閱讀 2924·2019-08-29 15:11
閱讀 2463·2019-08-29 13:19
閱讀 2995·2019-08-29 12:25
閱讀 428·2019-08-29 12:21
閱讀 2841·2019-08-29 11:03