摘要:一些使用方式其實在我上面寫一些構(gòu)造函數(shù)的時候,我想大家應該已經(jīng)感受到與反射相關(guān)了,起碼我感覺上是這樣的,所以我一開始想到這樣的案例形式,通過反射與這個屬性描述類去賦予我的類。
本文首發(fā)于本博客 貓叔的博客,轉(zhuǎn)載請申明出處前言
感謝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包下的)
通過類關(guān)系圖,可以知道,我們應該提前了解一下FeatureDescriptor才行了。很好,起碼目前還沒有設(shè)計抽象類或者接口。
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è)計方式,F(xiàn)eatureDescriptor為類似PropertyDescriptor、EvebtSetDescriptor、MethodDescriptor的描述符提供了一些共用的常量信息。同時它也提供一個擴展功能,方便任意屬性或鍵值對可以于設(shè)計功能相關(guān)聯(lián)。
這里簡單的說下,在我大致看了一下源碼后(可能不夠詳細,最近有點忙,時間較趕),F(xiàn)eatureDescriptor主要是針對一下屬性的一些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; // 屬性表
其實該類還有另外幾個方法,比如深奧的構(gòu)造函數(shù)等等,這里就不深入探討了。
PropertyDescriptor那么我們大致知道了FeatureDescriptor,接下來就可以來深入了解看看這個屬性描述符PropertyDescriptor。
說到屬性,大家一定會想到的就是get/set這個些基礎(chǔ)的東西,當我打開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對象,所以我以前在案例中先創(chuàng)建了一個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ù)
起碼目前,我還不知道我應該怎么使用它,那么我們就一步一步來吧,我看到它有好幾個構(gòu)造函數(shù),這是一個有趣而且有難度的事情,我們先試著創(chuàng)建一個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)); }
這個好像是參數(shù)最少的,它只需要我們傳入一個屬性字符串,還有對應的類就好了,其實它也是調(diào)用了另一個構(gòu)造函數(shù),只是它會幫我們默認生成讀方法和寫方法。方法中的Introspector.IS_PREFIX + NameGenerator.capitalize(propertyName)其實就是自己拼出一個默認的get/set方法,大家有興趣可以去看看源碼。
那么對應的實現(xiàn)內(nèi)容,我想大家應該都想到了。
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); }
沒錯,這個構(gòu)造函數(shù)就是第一種構(gòu)造函數(shù)內(nèi)部二次調(diào)用的,所需要的參數(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); }
這個不用傳類,因為你需要傳遞兩個實際的方法進來,所以主要三個對應屬性的參數(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ù)與實現(xiàn)方式,起碼我們現(xiàn)在知道它需要什么。
一些使用方式其實在我上面寫一些構(gòu)造函數(shù)的時候,我想大家應該已經(jīng)感受到與反射相關(guān)了,起碼我感覺上是這樣的,所以我一開始想到這樣的案例形式,通過反射與這個屬性描述類去賦予我的類。
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()); }
運行結(jié)果還是順利的。
Cat{name="river", describe="null", age=0, weight=0}
可以看到,我們確實得到了一個理想中的對象。
那么我是不是可以改變一個已經(jīng)創(chuàng)建的對象呢?
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是,我先創(chuàng)建了一個對象,然后通過屬性描述符讀取name值,再進行修改值,最后輸出的對象的值也確實改變了。
默認:river收尾
修改后:Cat{name="copy", describe="黑貓", age=2, weight=4}
這是一個有趣的API,我想另外兩個(EvebtSetDescriptor、MethodDescriptor)應該也差不多,大家可以再通過此方法去探究,只有自己嘗試一次才能學到這里面的一些東西,還有一些項目場景的使用方式,不過一般的業(yè)務場景應該很少使用到這個API。那么這個東西究竟可以干什么呢?我想你試著敲一次也許有一些答案了。
公眾號:Java貓說現(xiàn)架構(gòu)設(shè)計(碼農(nóng))兼創(chuàng)業(yè)技術(shù)顧問,不羈平庸,熱愛開源,雜談程序人生與不定期干貨。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/73099.html
摘要:關(guān)于它的數(shù)據(jù)轉(zhuǎn)換使用了如下兩種機制隸屬于規(guī)范。這種類中的方法主要用于訪問私有的字段,且方法名符合某種命名規(guī)則。如果在兩個模塊之間傳遞信息,可以將信息封裝進中,這種對象稱為值對象,或。 每篇一句 千古以來要飯的沒有要早飯的,知道為什么嗎? 相關(guān)閱讀 【小家Spring】聊聊Spring中的數(shù)據(jù)轉(zhuǎn)換:Converter、ConversionService、TypeConverter、Pro...
摘要:源碼分析源碼一覽本節(jié),我們先來看一下填充屬性的方法,即。所有的屬性值是在方法中統(tǒng)一被注入到對象中的。檢測是否存在與相關(guān)的或。這樣可以在很大程度上降低源碼分析的難度。若候選項是非類型,則表明已經(jīng)完成了實例化,此時直接返回即可。 1. 簡介 本篇文章,我們來一起了解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對象中的。我在前面幾篇文章中介紹過 Spring 創(chuàng)建 bea...
摘要:前言最近開發(fā)遇到一個問題,兩個對象進行屬性值拷貝。理論上來說可以直接借助來進行拷貝,奈何兩個對象屬性名不同,懵逼臉。 1、前言 最近開發(fā)遇到一個問題,兩個對象進行屬性值拷貝。理論上來說可以直接借助org.springframework.beans.BeanUtils.copyProperties(Object source, Object target)來進行拷貝,奈何兩個對象屬性名不...
摘要:在這一步里,將配置文件的信息裝入到容器的定義注冊表中,但此時還未初始化。注冊后處理器根據(jù)反射機制從中找出所有類型的,并將它們注冊到容器后處理器的注冊表中。是屬性編輯器的注冊表,主要作用就是注冊和保存屬性編輯器。 點擊進入我的博客 1 Spring容器整體流程 1.1 ApplicationContext內(nèi)部原理 AbstractApplicationContext是Applicati...
摘要:前置知識在分析源碼前,我們先溫習一下以下的知識點。類在中萬物皆對象,而且我們在代碼中寫的每一個類也都是對象,是類的對象。總結(jié)一個看似簡單的工具類,其實里面包含的基礎(chǔ)的知識點非常多,包括類型信息反射線程安全引用類型類加載器等。 背景 在我們著手一個Java Web項目的時候,經(jīng)常會遇到DO、VO、DTO對象之間的屬性拷貝,若采用get、set的方法來進行賦值的話,代碼會相當冗長丑陋,一般...
閱讀 1393·2021-11-22 15:11
閱讀 2838·2019-08-30 14:16
閱讀 2755·2019-08-29 15:21
閱讀 2914·2019-08-29 15:11
閱讀 2451·2019-08-29 13:19
閱讀 2985·2019-08-29 12:25
閱讀 417·2019-08-29 12:21
閱讀 2829·2019-08-29 11:03