概述BeanWrapper 簡介BeanWrapper 使用場景BeanWrapper 的使用BeanWrapper 源碼解析屬性設置和獲取定義屬性設置和獲取具體實現構造方法設置屬性獲取屬性參考文獻java
從上節 Spring IoC之ApplicationContext 解讀完畢以後,關於 Spring IoC 中比較重要的5個組件咱們已經分析完畢,可是這只是 IoC 容器涉及到的比較重要的組件,還有不少其餘組件起着重要的做用。因此接下來的文章咱們會對其餘一些類作必要的解讀。web
org.springframework.beans.BeanWrapper
是 Spring 框架中重要的組件類。在 Spring IoC之AbstractBeanFactory(二) 一章中,咱們介紹了各類 Scope 的 bean 的建立過程,在 doCreateBean()
方法中在對 bean 實例填充屬性前會調用 createBeanInstance(beanName, mbd, args)
方法簡單初始化一個實例,該實例的類型還不是咱們最後想要的 bean 類型,而是 BeanWrapper 實例。本章就是對這個類型進行簡單的介紹。spring
BeanWrapper 是 Spring 的低級 JavaBeans 基礎結構的中央接口, 至關於一個代理器, 提供用於分析和操做標準 JavaBean 的操做:得到和設置屬性值(單獨或批量),獲取屬性描述符以及查詢屬性的可讀性/可寫性的能力。BeanWrapper 大部分狀況下是在 Spring IoC 內部進行使用,經過 BeanWrapper,Spring IoC 容器能夠用統一的方式來訪問 bean 的屬性。用戶不多須要直接使用 BeanWrapper 進行編程。 編程
下面咱們來回顧一下 bean 的實例化過程,看一下 Spring 是怎麼使用 BeanWrapper。數組
bean 的實例過程:緩存
在上面 bean 的實例化過程當中,BeanWrapper 取出 XML 文件中定義的屬性值,而後經過屬性編輯器或類型轉換器把 XML 文件中的字符串轉換成 bean 中屬性對應的類型,最後之內省的方式填充到 bean 實例中。app
其實,在 BeanWrapper 接口中,最核心的功能就是讀取和設置 bean 的屬性,它是經過 Java 內省的方式完成 bean 屬性的訪問。爲了更好的認識 BeanWrapper 接口,下面將經過一個例子看一下怎麼使用 BeanWrapper 訪問 bean 的屬性。框架
在建立 bean 的過程當中,也就是 AbstractAutowireCapableBeanFactory 類中的 doCreateBean()
方法中,框架利用 BeanWrapper 來封裝實例化的 bean。首先執行 createBeanInstance()
建立一個 BeanWrapper 對象,在該方法中有一個 autowireConstructor()
方法,該方法的具體實如今 ConstructorResolver 類中:編輯器
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
Constructor<?> constructorToUse = null;
ConstructorResolver.ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
......
Assert.state(argsToUse != null, "Unresolved constructor arguments");
bw.setBeanInstance(this.instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
}
複製代碼
該方法即爲建立 BeanWrapper 對象的過程,這裏不作過多分析,有興趣的朋友能夠自行學習。ide
新建兩個 bean 類:Man 類和 Car 類。
Car.java
public class Car {
private int maxSpeed ;
private String brand ;
private double price ;
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"maxSpeed=" + maxSpeed +
", brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
複製代碼
Man.java
public class Man {
private String name;
private int age;
private Car car;
private String[] hobbies;
private Map<String,Object> relatives;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
public Map<String, Object> getRelatives() {
return relatives;
}
public void setRelatives(Map<String, Object> relatives) {
this.relatives = relatives;
}
@Override
public String toString() {
return "Man{" +
"name='" + name + '\'' +
", age=" + age +
", car=" + car +
'}';
}
}
複製代碼
測試代碼
@Test
public void doBeanWrapper(){
Car car = new Car();
BeanWrapper beanWrapperOfCar = PropertyAccessorFactory.forBeanPropertyAccess(car);
PropertyValue brandValue = new PropertyValue("brand","東風");
PropertyValue maxSpeedValue = new PropertyValue("maxSpeed",333);
PropertyValue priceValue = new PropertyValue("price",202020);
beanWrapperOfCar.setPropertyValue(brandValue);
beanWrapperOfCar.setPropertyValue(maxSpeedValue);
beanWrapperOfCar.setPropertyValue(priceValue);
Man person = new Man();
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(person);
PropertyValue nameValue = new PropertyValue("name","hresh");
PropertyValue ageValue = new PropertyValue("age",23);
PropertyValue carValue = new PropertyValue("car",car);
String[] hobbies = {"跑步","唱歌","看書"};
PropertyValue hobbyValue = new PropertyValue("hobbies",hobbies);
Map<String,Object> relatives = new HashMap<>();
relatives.put("Father","hresh");
relatives.put("Son","hresh");
PropertyValue relativeValue = new PropertyValue("relatives",relatives);
beanWrapper.setPropertyValue(nameValue);
beanWrapper.setPropertyValue(ageValue);
beanWrapper.setPropertyValue(carValue);
beanWrapper.setPropertyValue(hobbyValue);
beanWrapper.setPropertyValue(relativeValue);
System.out.println(person);
System.out.println(beanWrapper.getWrappedInstance());
System.out.println(person == beanWrapper.getWrappedInstance());
int age = (Integer) beanWrapper.getPropertyValue("age");
String hobby = (String) beanWrapper.getPropertyValue("hobbies[1]");
String brand = (String) beanWrapper.getPropertyValue("car.brand");
String relative = (String) beanWrapper.getPropertyValue("relatives['Father']");
String result = String.format("%s is %d years old ,is interested in %s, has a relative named %s , also has a %s car !",person.getName(),age,hobby,relative,brand);
System.out.println(result);
}
複製代碼
執行結果爲:
Man{name='hresh', age=23, car=Car{maxSpeed=333, brand='東風', price=202020.0}}
Man{name='hresh', age=23, car=Car{maxSpeed=333, brand='東風', price=202020.0}}
true
hresh is 23 years old ,is interested in 唱歌, has a relative named hresh , also has a 東風 car !
複製代碼
以上結果即爲 BeanWrapper 設置和獲取 bean 屬性的實現,有了初步的認識以後,接着咱們分析一下該源碼。
BeanWrapper 源碼定義以下:
public interface BeanWrapper extends ConfigurablePropertyAccessor {
//指定數組和集合自動增加的限制。
void setAutoGrowCollectionLimit(int var1);
//返回數組和集合自動增加的限制。
int getAutoGrowCollectionLimit();
//返回此對象包裝的bean實例。
Object getWrappedInstance();
//返回包裝的bean實例的類型。
Class<?> getWrappedClass();
//獲取包裝對象的PropertyDescriptors(由標準JavaBean自省肯定)。
PropertyDescriptor[] getPropertyDescriptors();
//獲取包裝對象的特定屬性的屬性描述符。
PropertyDescriptor getPropertyDescriptor(String var1) throws InvalidPropertyException;
}
複製代碼
BeanWrapper 接口自己的定義並無不少內容,可是繼承了 ConfigurablePropertyAccessor 接口,該接口又繼承了 PropertyAccessor, PropertyEditorRegistry, TypeConverter 這三個接口。它們的結構關係圖以下:
在 PropertyAccessor 接口中定義了方法,使得 BeanWrapper 具有了訪問 bean 屬性的能力。PropertyAccessor 接口定義以下:
public interface PropertyAccessor {
//嵌套屬性的路徑分隔符。
String NESTED_PROPERTY_SEPARATOR = ".";
char NESTED_PROPERTY_SEPARATOR_CHAR = '.';
String PROPERTY_KEY_PREFIX = "[";
char PROPERTY_KEY_PREFIX_CHAR = '[';
String PROPERTY_KEY_SUFFIX = "]";
char PROPERTY_KEY_SUFFIX_CHAR = ']';
//肯定指定的屬性是否可讀。
boolean isReadableProperty(String var1);
//肯定指定的屬性是否可寫。
boolean isWritableProperty(String var1);
//肯定指定屬性的屬性類型,或者檢查屬性描述符,或者在使用索引或映射元素的狀況下檢查值。
@Nullable
Class<?> getPropertyType(String var1) throws BeansException;
//返回指定屬性的類型描述符:最好從read方法返回到write方法。
@Nullable
TypeDescriptor getPropertyTypeDescriptor(String var1) throws BeansException;
//獲取指定屬性的當前值。
@Nullable
Object getPropertyValue(String var1) throws BeansException;
//將指定值設置爲當前屬性值。
void setPropertyValue(String var1, @Nullable Object var2) throws BeansException;
//將指定值設置爲當前屬性值。
void setPropertyValue(PropertyValue var1) throws BeansException;
//從map執行批量更新。
void setPropertyValues(Map<?, ?> var1) throws BeansException;
//執行批量更新的首選方法。
void setPropertyValues(PropertyValues var1) throws BeansException;
//執行批處理更新,以更好地控制行爲。
void setPropertyValues(PropertyValues var1, boolean var2) throws BeansException;
//執行對行爲的徹底控制的批處理更新。
void setPropertyValues(PropertyValues var1, boolean var2, boolean var3) throws BeansException;
}
複製代碼
PropertyAccessor 接口提供了訪問 bean 屬性的方法,還定義了訪問嵌套屬性的訪問表達式的切割符,重點看一下下面兩個方法:
Object getPropertyValue(String propertyName) throws BeansException;
void setPropertyValue(String propertyName, Object value) throws BeansException;
複製代碼
getPropertyValue 和 setPropertyValue 是分別用於獲取和設置 bean 的屬性值的。這裏的 propertyName 支持表達式:
表達式 | 說明 |
---|---|
name |
指向屬性name,與getName()和setName()相對應 |
car.brand |
指向屬性car的嵌套屬性brand,與之對應的是getCar().getBrand()和getCar().setBrand() |
hobbies[1] |
指向索引屬性hobbies的第二個元素,索引屬性多是一個數組、列表或其餘自然有序的容器 |
relatives['Father'] |
指向一個Map實體relatives中以「Father」做爲鍵值(key)所對應的值 |
BeanWrapper的實現類是 BeanWrapperImpl,它包裝了 bean 對象,緩存了 bean 的內省結果,並能夠訪問 bean 屬性、設置 bean 的屬性值。除此以外,BeanWrapperImpl 類提供了須要默認屬性編輯器,支持多種不一樣類型的類型轉換,例如,能夠將數組、集合類型的屬性轉換成指定特殊類型的數組或集合。用戶也能夠註冊自定義的屬性編輯器在 BeanWrapperImpl 中。
另外,BeanWrapperImpl 有一個 cachedIntrospectionResults 成員變量,它保存了被包裝 bean 的內省分析結果。cachedIntrospectionResults 有兩個成員變量,一個是 beanInfo,它是被包裹類的 BeanInfo;另外一個是 propertyDescriptorCache,它緩存了被包裹類的全部屬性的屬性描述器 PropertyDescriptor。
咱們看一下 BeanWrapperImpl 的結構類圖。
接着針對 BeanWrapperImpl 中的重要方法進行解讀。
在測試案例中有這麼一行代碼:
BeanWrapper beanWrapperOfCar = PropertyAccessorFactory.forBeanPropertyAccess(car);
複製代碼
實際執行的是 new BeanWrapperImpl(target)
,這只是 BeanWrapperImpl 中的一種構造方法,該類重載了不少種構造方法,咱們看一下 bean 實例做爲參數的構造方法。
public BeanWrapperImpl(Object object) {
super(object);
}
複製代碼
調用父類 AbstractNestablePropertyAccessor 的構造方法。
protected AbstractNestablePropertyAccessor(Object object) {
this.autoGrowCollectionLimit = 2147483647;
this.nestedPath = "";
// 標識可使用默認的屬性編輯器
this.registerDefaultEditors();
this.setWrappedInstance(object);
}
複製代碼
public void setWrappedInstance(Object object) {
this.setWrappedInstance(object, "", (Object)null);
}
public void setWrappedInstance(Object object, @Nullable String nestedPath, @Nullable Object rootObject) {
// bean設置爲BeanWrapperImpl的內部變量
this.wrappedObject = ObjectUtils.unwrapOptional(object);
Assert.notNull(this.wrappedObject, "Target object must not be null");
this.nestedPath = nestedPath != null ? nestedPath : "";
this.rootObject = !this.nestedPath.isEmpty() ? rootObject : this.wrappedObject;
this.nestedPropertyAccessors = null;
// 新建類型轉換器的委託類,這裏BeanWrapperImpl的實例爲propertyEditorRegistry,bean爲targetObject
this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject);
}
複製代碼
從上述代碼能夠看出構造方法作了兩件重要的事情,一是把傳進來的 bean 設爲內部變量,二是實例化了一個類型轉換器的委託類。因爲 BeanWrapperImpl 同時繼承了 PropertyEditorRegistrySupport,因此它做爲 TypeConverterDelegate 的屬性編輯器註冊中心的幫助類存在於 TypeConverterDelegate 中。
BeanWrapperImpl 支持多種設置 bean 屬性的方法,不過都是繼承父類 AbstractNestablePropertyAccessor 中的。
先看一下 setPropertyValue(String propertyName, @Nullable Object value)
方法。
public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
AbstractNestablePropertyAccessor nestedPa;
try {
//根據屬性名獲取BeanWrapImpl對象,支持多重屬性的遞歸分析處理
nestedPa = this.getPropertyAccessorForPropertyPath(propertyName);
} catch (NotReadablePropertyException var5) {
throw new NotWritablePropertyException(this.getRootClass(), this.nestedPath + propertyName, "Nested property in path '" + propertyName + "' does not exist", var5);
}
// 通過上面的遞歸後,獲取到最終須要操做的屬性的對象,下面將根據該屬性對象,獲取最終要操做的內嵌對象的屬性,
// 生成PropertyTokenHolder,內省設置屬性值
AbstractNestablePropertyAccessor.PropertyTokenHolder tokens = this.getPropertyNameTokens(this.getFinalPath(nestedPa, propertyName));
nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
複製代碼
getPropertyAccessorForPropertyPath()
方法根據屬性名的表達式獲取訪問該屬性的屬性訪問器 AbstractNestablePropertyAccessor,即 BeanWrapperImpl。這裏解釋一下屬性名錶達式是什麼意思,仍是用例子來講明吧。
@Test
public void doBeanWrapper(){
Car car = new Car();
Man person = new Man();
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(person);
PropertyValue nameValue = new PropertyValue("name","hresh");
PropertyValue ageValue = new PropertyValue("age",23);
PropertyValue carValue = new PropertyValue("car",car);
String[] hobbies = {"跑步","唱歌","看書"};
PropertyValue hobbyValue = new PropertyValue("hobbies",hobbies);
Map<String,Object> relatives = new HashMap<>();
relatives.put("Father","hresh");
relatives.put("Son","hresh");
PropertyValue relativeValue = new PropertyValue("relatives",relatives);
// beanWrapper.setPropertyValue(nameValue);
beanWrapper.setPropertyValue("name","hresh");
beanWrapper.setPropertyValue(ageValue);
beanWrapper.setPropertyValue(carValue);
beanWrapper.setPropertyValue("car.brand","東方紅");
beanWrapper.setPropertyValue(hobbyValue);
beanWrapper.setPropertyValue("hobbies[1]","跳舞");
beanWrapper.setPropertyValue(relativeValue);
beanWrapper.setPropertyValue("relatives['Father']","clearLove");
System.out.println(person);
}
複製代碼
propertyName 即屬性名,它可能只是普通的字符串,相似於"name",也有多是"car.brand"這樣的表達式,當存在這種內嵌屬性的狀況,咱們看一下它是怎麼處理的。具體看 getPropertyAccessorForPropertyPath()
方法。
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
// 若是是內嵌屬性的狀況,則獲取第一個內嵌屬性的位置,分隔符是"."
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
// 遞歸獲取內嵌屬性,若是propertyPath再也不存在分隔符「.」,返回遞歸結果
if (pos > -1) {
String nestedProperty = propertyPath.substring(0, pos);
String nestedPath = propertyPath.substring(pos + 1);
// 獲取本輪遞歸中AbstractNestablePropertyAccessor的屬性
AbstractNestablePropertyAccessor nestedPa = this.getNestedPropertyAccessor(nestedProperty);
return nestedPa.getPropertyAccessorForPropertyPath(nestedPath);
} else {
return this;
}
}
複製代碼
結合上述的測試案例,咱們看一下調試過程當中的結果展現,當執行 beanWrapper.setPropertyValue("name","hresh")
語句時,
此時返回的屬性訪問器(即 BeanWrapperImpl )中包含的 bean 屬性值爲 Man 類。
當執行 beanWrapper.setPropertyValue("car.brand","東方紅");
語句時,
發現存在內嵌屬性,則會進行分割,而後按序處理,先處理第一個屬性,即」car「屬性。此時調用 getNestedPropertyAccessor()
方法,一塊兒看下該方法的定義:
private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
if (this.nestedPropertyAccessors == null) {
this.nestedPropertyAccessors = new HashMap();
}
//根據屬性名獲取PropertyTokenHolder
AbstractNestablePropertyAccessor.PropertyTokenHolder tokens = this.getPropertyNameTokens(nestedProperty);
//值爲 car
String canonicalName = tokens.canonicalName;
// 根據PropertyTokenHolder獲取該內嵌屬性的實例化對象,在本例中即爲car對象
Object value = this.getPropertyValue(tokens);
//因爲咱們已經先setPropertyValue(carValue),因此這裏取到的value
if (value == null || value instanceof Optional && !((Optional)value).isPresent()) {
// 若是容許自動建立屬性,調用setDefaultValue建立默認的對象,不然拋異常
if (!this.isAutoGrowNestedPaths()) {
throw new NullValueInNestedPathException(this.getRootClass(), this.nestedPath + canonicalName);
}
value = this.setDefaultValue(tokens);
}
// 把上面獲取到的內嵌對象的實例,包裹爲一個新的BeanWrapperImpl,而後把該BeanWrapperImpl緩存到本級的緩存對象nestedPropertyAccessors。
AbstractNestablePropertyAccessor nestedPa = (AbstractNestablePropertyAccessor)this.nestedPropertyAccessors.get(canonicalName);
if (nestedPa != null && nestedPa.getWrappedInstance() == ObjectUtils.unwrapOptional(value)) {
if (logger.isTraceEnabled()) {
logger.trace("Using cached nested property accessor for property '" + canonicalName + "'");
}
} else {
//好比說案例中的內嵌屬性「car.brand",當處理car時,會將獲取到的car對象封裝成PropertyAccessor,並緩存到nestedPropertyAccessors中
if (logger.isTraceEnabled()) {
logger.trace("Creating new nested " + this.getClass().getSimpleName() + " for property '" + canonicalName + "'");
}
nestedPa = this.newNestedPropertyAccessor(value, this.nestedPath + canonicalName + ".");
this.copyDefaultEditorsTo(nestedPa);
this.copyCustomEditorsTo(nestedPa, canonicalName);
this.nestedPropertyAccessors.put(canonicalName, nestedPa);
}
return nestedPa;
}
複製代碼
執行該方法時調試結果以下圖所示:
在上面代碼中,getPropertyNameTokens 將根據 propertyName 生成一個統一操做的結構PropertyTokenHolder,此類保存了屬性名解析後的結構。咱們在前文講到 propertyName 支持三種格式,因此對應會出現三種解析後的結構。結合上述案例,
當 propertyPath 爲「car.brand」,因爲分割操做,會調用 getNestedPropertyAccessor()
方法,進入該方法後,會執行 getPropertyNameTokens(nestedProperty)
方法,此時 nestedProperty 等同於 propertyName,值爲「car」。解析後的結果爲:
當 propertyPath 爲「hobbies[1]」,程序會從 setPropertyValue(propertyName, value)
方法進入到 getPropertyNameTokens(propertyName)
,此時 propertyName 值爲「hobbies[1]」。解析後的結果爲:
當 propertyPath 爲「relatives['Father']」,程序會從 setPropertyValue(propertyName, value)
方法進入到 getPropertyNameTokens(propertyName)
,此時 propertyName 值爲「relatives['Father']」。解析後的結果爲:
咱們看一下 getPropertyNameTokens()
方法的實現過程。
private AbstractNestablePropertyAccessor.PropertyTokenHolder getPropertyNameTokens(String propertyName) {
String actualName = null;
List<String> keys = new ArrayList(2);
int searchIndex = 0;
while(true) {
int keyStart;
int keyEnd;
do {
do {
if (searchIndex == -1) {
//生成PropertyTokenHolder對象,更新actualName、canonicalName、keys內容
AbstractNestablePropertyAccessor.PropertyTokenHolder tokens = new AbstractNestablePropertyAccessor.PropertyTokenHolder(actualName != null ? actualName : propertyName);
if (!keys.isEmpty()) {
tokens.canonicalName = tokens.canonicalName + "[" + StringUtils.collectionToDelimitedString(keys, "][") + "]";
tokens.keys = StringUtils.toStringArray(keys);
}
return tokens;
}
//首先判斷propertyName是否爲數組、列表、map等帶有[]的形式,獲取「[」的索引,若是是普通字符串,則keyStart值爲-1
//多是二維數組,相似於「nums[1][1]「,因此繼續取出key值
keyStart = propertyName.indexOf("[", searchIndex);
searchIndex = -1;
} while(keyStart == -1);
//計算下一個字符「]」的索引值
keyEnd = this.getPropertyNameKeyEnd(propertyName, keyStart + "[".length());
} while(keyEnd == -1);
//不論是數組、列表仍是map,獲取那個集合的名稱
if (actualName == null) {
actualName = propertyName.substring(0, keyStart);
}
String key = propertyName.substring(keyStart + "[".length(), keyEnd);
//若是是map集合,獲取key的值
if (key.length() > 1 && key.startsWith("'") && key.endsWith("'") || key.startsWith("\"") && key.endsWith("\"")) {
key = key.substring(1, key.length() - 1);
}
keys.add(key);
searchIndex = keyEnd + "]".length();
}
}
複製代碼
關於代碼註釋中提到的二維數組的狀況,我進行測試操做,關於該部分的截圖以下:
在 getNestedPropertyAccessor()
還有一句比較重要的代碼:
Object value = this.getPropertyValue(tokens);
複製代碼
該方法根據 PropertyTokenHolder 獲取指定 property 屬性的實例,一塊兒看下這段代碼。
protected Object getPropertyValue(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
// 獲取PropertyHandler,內部實現是在BeanWrapperImpl類中,從cachedIntrospectionResults中取出該屬性的PropertyDescriptor,
// 而後取出屬性的PropertyType, ReadMethod , WriteMethod
AbstractNestablePropertyAccessor.PropertyHandler ph = this.getLocalPropertyHandler(actualName);
// 屬性不可讀,拋異常
if (ph != null && ph.isReadable()) {
try {
// 內省方式獲取屬性的實例
Object value = ph.getValue();
// 若是該屬性是數組、list、set,tokens的keys是不爲空的,keys將會保存須要訪問的索引號,
// 在map中,keys是一個字符串
// 下面就是經過該索引號獲取特定下標的屬性值。
if (tokens.keys != null) {
if (value == null) {
if (!this.isAutoGrowNestedPaths()) {
throw new NullValueInNestedPathException(this.getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value of property referenced in indexed property path '" + propertyName + "': returned null");
}
value = this.setDefaultValue(new AbstractNestablePropertyAccessor.PropertyTokenHolder(tokens.actualName));
}
StringBuilder indexedPropertyName = new StringBuilder(tokens.actualName);
//若是是多維數組的話,好比說二維數組nums[2][3],當要修改nums[1][2]的值時,此時通過getPropertyNameTokens方法返回的token的keys的值應該有兩個,可是當傳入的propertyPath不是」car.brand「形式,好比說數組、列表等,是從setPropertyValue()方法進入到getPropertyValue()方法中的,此外還會通過processKeyedProperty()和getPropertyHoldingValue()方法,尤爲須要注意getPropertyHoldingValue()方法,在該方法中,它會對得到的token中的keys進行截取操做。原本二維數組得到的token中的keys應該有兩個值,可是實際進入該方法時只有一個值。
for(int i = 0; i < tokens.keys.length; ++i) {
String key = tokens.keys[i];
if (value == null) {
throw new NullValueInNestedPathException(this.getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value of property referenced in indexed property path '" + propertyName + "': returned null");
}
int index;
//根據類型進行差異處理
if (value.getClass().isArray()) {
index = Integer.parseInt(key);
value = this.growArrayIfNecessary(value, index, indexedPropertyName.toString());
value = Array.get(value, index);
} else if (value instanceof List) {
index = Integer.parseInt(key);
List<Object> list = (List)value;
this.growCollectionIfNecessary(list, index, indexedPropertyName.toString(), ph, i + 1);
value = list.get(index);
} else if (value instanceof Set) {
Set<Object> set = (Set)value;
int index = Integer.parseInt(key);
if (index < 0 || index >= set.size()) {
throw new InvalidPropertyException(this.getRootClass(), this.nestedPath + propertyName, "Cannot get element with index " + index + " from Set of size " + set.size() + ", accessed using property path '" + propertyName + "'");
}
Iterator<Object> it = set.iterator();
for(int j = 0; it.hasNext(); ++j) {
Object elem = it.next();
if (j == index) {
value = elem;
break;
}
}
} else {
if (!(value instanceof Map)) {
throw new InvalidPropertyException(this.getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + "' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
}
Map<Object, Object> map = (Map)value;
Class<?> mapKeyType = ph.getResolvableType().getNested(i + 1).asMap().resolveGeneric(new int[]{0});
TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);
Object convertedMapKey = this.convertIfNecessary((String)null, (Object)null, key, mapKeyType, typeDescriptor);
value = map.get(convertedMapKey);
}
indexedPropertyName.append("[").append(key).append("]");
}
}
return value;
}
........
}
} else {
throw new NotReadablePropertyException(this.getRootClass(), this.nestedPath + propertyName);
}
}
複製代碼
除了經過 getNestedPropertyAccessor()
方法進入到 getPropertyValue()
方法,當傳入的 propertyName 參數值爲數組、集合、列表,Map 等,走的是另一條途徑,我專門畫了一張流程圖來展現:
當測試案例中給 Man 類增長一個二維數組屬性,而後進行調試,便可發如今 getPropertyValue()
方法中的區別,代碼以下:
@Test
public void doBeanWrapper(){
Car car = new Car();
Man person = new Man();
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(person);
PropertyValue nameValue = new PropertyValue("name","hresh");
PropertyValue ageValue = new PropertyValue("age",23);
PropertyValue carValue = new PropertyValue("car",car);
String[] hobbies = {"跑步","唱歌","看書"};
PropertyValue hobbyValue = new PropertyValue("hobbies",hobbies);
Map<String,Object> relatives = new HashMap<>();
relatives.put("Father","hresh");
relatives.put("Son","hresh");
PropertyValue relativeValue = new PropertyValue("relatives",relatives);
Integer[][] nums = {{1,2,3},{4,5,6}};
PropertyValue numsValue = new PropertyValue("nums",nums);
// beanWrapper.setPropertyValue(nameValue);
beanWrapper.setPropertyValue("name","hresh");
beanWrapper.setPropertyValue(ageValue);
beanWrapper.setPropertyValue(carValue);
beanWrapper.setPropertyValue("car.brand","東方紅");
beanWrapper.setPropertyValue(hobbyValue);
beanWrapper.setPropertyValue("hobbies[1]","跳舞");
beanWrapper.setPropertyValue(relativeValue);
beanWrapper.setPropertyValue("relatives['Father']","clearLove");
beanWrapper.setPropertyValue(numsValue);
beanWrapper.setPropertyValue("nums[1][2]",22);
System.out.println(person);
}
複製代碼
總結: 在經過 setPropertyValue 設置 bean 的屬性時,首先將會根據 propertyName 的字符串,遞歸獲取該屬性所在的內嵌屬性(假如屬性不在內嵌屬性,獲取的就是它本身),而後經過內省的方式設置該屬性的值。
BeanWrapperImpl 有兩個獲取屬性的方法。
public Object getPropertyValue(String propertyName) throws BeansException
protected Object getPropertyValue(PropertyTokenHolder tokens)
複製代碼
其中 getPropertyValue(PropertyTokenHolder tokens) 在上面設置屬性那一節已經出現過了,它是在內部使用的,不對外公開。
既然如此,看一下 getPropertyValue(propertyName)
方法。該方法在 AbstractNestablePropertyAccessor 中實現。
public Object getPropertyValue(String propertyName) throws BeansException {
AbstractNestablePropertyAccessor nestedPa = this.getPropertyAccessorForPropertyPath(propertyName);
AbstractNestablePropertyAccessor.PropertyTokenHolder tokens = this.getPropertyNameTokens(this.getFinalPath(nestedPa, propertyName));
return nestedPa.getPropertyValue(tokens);
}
複製代碼
其中 getPropertyAccessorForPropertyPath()
方法在上一節中已經有詳細的解析,它將遞歸獲取能夠訪問該屬性的 AbstractNestablePropertyAccessor
,這裏的實現類是 BeanWrapperImpl。例如 propertyName 是car.brand,那麼 BeanWrapperImpl 所包裹的就是 Car 的實例。而後根據 getPropertyNameTokens()
方法獲取 token 值,最後就是執行 getPropertyValue(tokens)
,請參考前面。
https://my.oschina.net/thinwonton/blog/1492224#h1_3