BeanWrapper这个接口主要用来操作一些标准的JavaBeans。对Bean属性的操作,封装了一个bean的行为,诸如设置和获取属性值等。大家熟知的主要有下面的工具类(Apache的BeanUtils和PropertyUtils、cglib的BeanMap和BeanCopier、spring的BeanUtils),但是Spring里有有一个更加强大的BeanWrapper操作类(支持设置嵌套属性、支持属性值的类型转换(设置ConversionService)。
实例:
public class AppTest {
@Test
public void shouldAnswerWithTrue() {
Staff staff = new Staff();
BeanWrapper staffWrapper = new BeanWrapperImpl(staff);
//设置属性
staffWrapper.setPropertyValue("id", "1001");
//设置属性
staffWrapper.setPropertyValue(new PropertyValue("name", "小王"));
//设置属性
staffWrapper.setPropertyValue("hobbies[0]", "羽毛球");
staffWrapper.setPropertyValue("hobbies[1]", "篮球");
System.out.println("================");
//获取属性
Object id = staffWrapper.getPropertyValue("id");
System.out.println("id="+id);
//获取属性
Object name = staffWrapper.getPropertyValue("name");
System.out.println("name="+name);
//获取属性
Object hobbies = staffWrapper.getPropertyValue("hobbies");
System.out.println("hobbies="+hobbies);
//获取属性
Object hobbies_0 = staffWrapper.getPropertyValue("hobbies[0]");
System.out.println("hobbies[0]="+hobbies_0);
System.out.println("================");
System.out.println("staff="+staff.toString());
System.out.println("================");
Company company = new Company();
BeanWrapper companyWrapper = new BeanWrapperImpl(company);
companyWrapper.setPropertyValue("staffs[" + staff.getId() + "]", staff);
System.out.println(company.getStaffs().get(staff.getId()));
}
}
class Staff {
private String id;
private String name;
private List<String> hobbies = new ArrayList<>();
public void setId(String id) {
System.out.println("setId 方法被调用");
this.id = id;
}
public void setName(String name) {
System.out.println("setName 方法被调用");
this.name = name;
}
public String getId() {
System.out.println("getId 方法被调用");
return id;
}
public String getName() {
System.out.println("getName 方法被调用");
return name;
}
public List<String> getHobbies() {
System.out.println("getHobbies 方法被调用");
return hobbies;
}
public void setHobbies(List<String> hobbies) {
System.out.println("getHobbies 方法被调用");
this.hobbies = hobbies;
}
@Override
public String toString() {
return "Staff{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", hobbies=" + Arrays.toString(hobbies.toArray()) +
'}';
}
}
class Company{
private Map<String, Staff> staffs = new HashMap<>();
public Map<String, Staff> getStaffs() {
return staffs;
}
public void setStaffs(Map<String, Staff> staffs) {
this.staffs = staffs;
}
}
结果:
setId 方法被调用
setName 方法被调用
getHobbies 方法被调用
getHobbies 方法被调用
================
getId 方法被调用
id=1001
getName 方法被调用
name=小王
getHobbies 方法被调用
hobbies=[羽毛球, 篮球]
getHobbies 方法被调用
hobbies[0]=羽毛球
================
staff=Staff{id='1001', name='小王', hobbies=[羽毛球, 篮球]}
================
getId 方法被调用
getId 方法被调用
Staff{id='1001', name='小王', hobbies=[羽毛球, 篮球]}
这个例子中,BeanWapper对Bean进包装,暴露统一的修改、查询 bean属性的方法,然后通过反射的方式调用Bean的get、set方法。
BeanWapper的继承关系:
BeanWrapper 继承上述三个接口,那么它就具有三重身份:
- 属性编辑器
- 属性编辑器注册表
- 类型转换器
ConfigurablePropertyAccessor
BeanWrapper 同时继承 ConfigurablePropertyAccessor,其做了一些额外的扩展
由于 TypeConverter 是基于线程不安全的 PropertyEditors ,因此 TypeConverters 本身也不被视为线程安全,在 Spring 3 后,不在采用 PropertyEditors 类作为 Spring 默认的类型转换接口,而是采用 ConversionService 体系,但 ConversionService 是线程安全的,所以在 Spring 3 后,如果你所选择的类型转换器是 ConversionService 而不是 PropertyEditors 那么 TypeConverters 则是线程安全的
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
/**
* 指定用于转换属性值的Spring3.0 ConversionService,作为JavaBeans属性编辑器的替代方法。
*/
void setConversionService(@Nullable ConversionService conversionService);
/**
* 返回关联的ConversionService(如果有)
*/
@Nullable
ConversionService getConversionService();
/**
*
* 设置在将属性编辑器应用于属性的新值时是否提取旧属性值
*/
void setExtractOldValueForEditor(boolean extractOldValueForEditor);
/**
* 返回在将属性编辑器应用于属性的新值时是否提取旧属性值。
*/
boolean isExtractOldValueForEditor();
/**
* Set whether this instance should attempt to "auto-grow" a
* nested path that contains a {@code null} value.
* <p>If {@code true}, a {@code null} path location will be populated
* with a default object value and traversed instead of resulting in a
* {@link NullValueInNestedPathException}.
* <p>Default is {@code false} on a plain PropertyAccessor instance.
*/
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
/**
* Return whether "auto-growing" of nested paths has been activated.
*/
boolean isAutoGrowNestedPaths();
}
参考:https://blog.csdn.net/qq330983778/article/details/88921230