目录
5.DataBinder源码分析BeanWrapper、Errors、BindingResult
3.数据绑定时类型转换
类型转换都是发生在数据绑定时。并且在DataBinder上可以看到实现两个接口
public class DataBinder implements PropertyEditorRegistry, TypeConverter
其中PropertyEditorRegistry作用注册已有类型转换器。根据Bean属性类型从这些类型转换器中选择需 要使用的。虽然DataBinder是PropertyEditorRegistry的实现类,但是PropertyEditorRegistry使用最多 的实现类是PropertyEditorRegistrySupport。
TypeConverter表示类可以进行类型转换服务,底层是基于BeanWrapper实现的(装饰器设计模式)。
在使用TypeConverter时,如果是简单类型,可以直接使用 SimpleTypeConverter,SimpleTypeConverter即实现了PropertyEditorRegistry,又实现了 TypeConverter。使用也很简单
@Test
void testSimpleTypeConverter(){
SimpleTypeConverter simpleTypeConverter = new SimpleTypeConverter();
Integer result = simpleTypeConverter.convertIfNecessary("123",Integer.class);
System.out.println(result);
}
在DataBinder中进行简单数据类型转换,默认使用getSimpleTypeConverter进行转换的。
@Test
void test123(){
DataBinder dataBinder = new DataBinder("","integer");
Integer result = dataBinder.convertIfNecessary("123", Integer.class);
System.out.println(result);
}
4.装饰器设计模式
4.1 装饰器设计模式介绍
装饰器设计模式,又称装饰者设计模式。英文多见:Wrapper、Decorator。
装饰器设计模式属于继承的一种替代模式,使用装饰模式可以动态扩展实现功能。
其本质:“使用关联代替继承”,毕竟继承这种关系属于一种强关系,是Java之父自己承认:“java中自己认 为最后悔的功能是继承”
4.2 装饰器模式角色图
设定需求:
在学校有专业:Java、前端专业。每个专业有自己的学习内容。现赶上学校20周年校庆,学校对专业下 达命令,各个专业根据自己的实力对学生分配对象。 针对这个需求,可以直接修改原有代码,在父类添加公共方法,子类去重写具体怎么分配对象。但是这样不符合开闭原则(对修改关闭,对扩展开放)
也可以直接对Java专业和前端专业新建子类。但是这种方式随着项目功能变多,子类数量也会特别多。
使用装饰器模式,主要就是添加一个新的角色。装饰者,让装饰者也实现专业接口,并在装饰者里面放 置原来父接口的关联对象。 按照下图修改后主要分为四个角色:
1、组件(Component) : 专业接口(Subject),提出出公共方法的接口。
2、组件实现(Concrete Component):需要被装饰的实现类。例如:Java专业和前端专业。
3、抽象装饰(Decorator): 定义需要装饰的方法。
4、SubjectWrapper 具体装饰(Concrete Decorator):实现装饰的方法。 SubjectWrapperImpl
4.3 装饰器模式代码实现
新建专业接口
// 专业接口
public interface Subject {
// 专业内容方法
void subject();
}
新建java专业实现类
// java专业
public class JavaSubject implements Subject{
@Override
public void subject() {
System.out.println("java专业");
}
}
前端专业实现类
public class FrontendSubject implements Subject {
@Override
public void subject() {
System.out.println("前端专业");
}
}
新建专业包装接口,提出需要包装的方法
public interface SubjectWrapper extends Subject {
void friend();
}
创建包装的具体实现
public class SubjectWrapperImpl implements SubjectWrapper{
private Subject subject;
public SubjectWrapperImpl(Subject subject) {
this.subject = subject;
}
@Override
public void subject() {
subject.subject();
friend();// 调用包装方法
}
@Override
public void friend() {
System.out.println("分配女朋友");
}
}
测试类测试效果
public class TestWrapper {
public static void main(String[] args) {
JavaSubject javaSubject = new JavaSubject();
SubjectWrapperImpl subjectWrapper = new SubjectWrapperImpl(javaSubject);
subjectWrapper.subject();
}
}
4.4 装饰器模式优缺点
优点:
动态扩充,不需要修改原有代码,不需要过度使用继承
缺点:
多重装饰比较复杂。
5.DataBinder源码分析BeanWrapper、Errors、BindingResult
BeanWrapper 是 基于装饰器设计模式实现的。实现了PropertyEditorRegistry接口和TypeConverter接 口。
在DataBinder数据绑定时,Bean的属性绑定过程就是使用BeanWrapper实现的。
BeanWrapper只有一个实现类BeanWrapperImpl,这个类还是PropertyEditorSupport子类,所以具备 类型转换的能力。
在使用DataBinder的convertIfNecessary()方法时,底层通过getTypeConverter()获取到类型转换器对 象。
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException {
return getTypeConverter().convertIfNecessary(value, requiredType);
}
getTypeConverter()方法中类型转换器是通过getInternaleBindingResult()获取到的。
protected TypeConverter getTypeConverter() {
if (getTarget() != null) {
return getInternalBindingResult().getPropertyAccessor();
}
else {
return getSimpleTypeConverter();
}
}
getInternaleBindingResult()方法返回结果类型为AbstractPropertyBindingResult,通过断电可以看出来 实际返回结果子类BeanPropertyBindingResult。
protected AbstractPropertyBindingResult getInternalBindingResult() {
if (this.bindingResult == null) {
this.bindingResult = (this.directFieldAccess ? createDirectFieldBindingResult():
createBeanPropertyBindingResult());
}
return this.bindingResult;
}
createBeanPropertyBindingResult()方法
protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
if (this.conversionService != null) {
result.initConversion(this.conversionService);
}
if (this.messageCodesResolver != null) {
result.setMessageCodesResolver(this.messageCodesResolver);
}
return result;
}
AbstractPropertyBindingResult是Errors接口和BindingResult接口的实现类。 Errors接口里面存储数据绑定和错误信息。也就是是说通过Errors可以获取到属性的信息并且给属性绑 定一个错误信息。 Errors接口里面方法比较多
public interface Errors {
/**
* 实际值为点(.)
*/
String NESTED_PATH_SEPARATOR = PropertyAccessor.NESTED_PROPERTY_SEPARATOR;
/**
* 返回绑定对象名字
*/
String getObjectName();
/**
* 设置嵌套路径,当有关联对象时使用
*/
void setNestedPath(String nestedPath);
/**
* 获取嵌套路径字符串类型值
*/
String getNestedPath();
/**
* 把嵌套路径放入栈中
*/
void pushNestedPath(String subPath);
/**
* 移除嵌套路径从栈中
*/
void popNestedPath() throws IllegalStateException;
/**
* 给对象绑定一个全局的错误码(errorCode)
*/
void reject(String errorCode);
/**
* 给对象绑定一个全局的错误码(errorCode)和错误信息(defaultMessage)
*/
void reject(String errorCode, String defaultMessage);
/**
* 给对象绑定一个全局错误码(errorCode)、错误参数(errorArgs),错误信息(defualtMessage)
*/
void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String
defaultMessage);
/**
* 给属性绑定错误码
*/
void rejectValue(@Nullable String field, String errorCode);
/**
* 给属性绑定错误码和错误信息
*/
void rejectValue(@Nullable String field, String errorCode, String
defaultMessage);
/**
* 给属性绑定错误码和错误参数及错误信息
*/
void rejectValue(@Nullable String field, String errorCode,
@Nullable Object[] errorArgs, @Nullable String defaultMessage);
/**
* 把参数(errors)实例所有内容添加到当前对象中
*/
void addAllErrors(Errors errors);
/**
* 判断是否有错误
*/
boolean hasErrors();
/**
* 返回错误的数量
*/
int getErrorCount();
/**
* 获取到所有的错误对象
*/
List<ObjectError> getAllErrors();
/**
* 是否有全局错误
*/
boolean hasGlobalErrors();
/**
* 获取全局错误信息
*/
int getGlobalErrorCount();
/**
* 获取所有全局错误实例
*/
List<ObjectError> getGlobalErrors();
/**
* 获取全局错误对象
*/
@Nullable
ObjectError getGlobalError();
/**
* 是否有属性错误
*/
boolean hasFieldErrors();
/**
* 获取属性错误个数
*/
int getFieldErrorCount();
/**
* 获取属性错误集合
*/
List<FieldError> getFieldErrors();
/**
* 获取属性错误对象
*/
@Nullable
FieldError getFieldError();
/**
* 判断某个属性是否有错误
*/
boolean hasFieldErrors(String field);
/**
* 获取某个属性错误个数
*/
int getFieldErrorCount(String field);
/**
* 获取某个属性错误集合
*/
List<FieldError> getFieldErrors(String field);
/**
* 获取某个属性错误个数
*/
@Nullable
FieldError getFieldError(String field);
/**
* 获取某个属性的值
*/
@Nullable
Object getFieldValue(String field);
/**
* 获取某个属性的类型
*/
@Nullable
Class<?> getFieldType(String field);
}
而BindingResult是Errors接口的子接口。也就是说里面也存储了错误信息和属性绑定信息。而 BindingResult接口主要是为了和数据校验Validation一起使用的。所以下一章节我们就讲解一下Validation是什么。 我们继续看源码getTypeConverter()方法中获取到BindingResult后,调用getPropertyAccessor()
protected TypeConverter getTypeConverter() {
if (getTarget() != null) {
return getInternalBindingResult().getPropertyAccessor();
}
else {
return getSimpleTypeConverter();
}
}
getPropertyAccessor()方法最终返回的是BeanWrapper,如果继续跟源码,可以知道 createBeanWrapper()方法实际返回结果为BeanWrapperImpl。这也说明了DataBinder数据绑定时, 类型转换器本质是BeanWrapper的实现类BeanWrapperImpl。使用了装饰者模式。
@Override
public final ConfigurablePropertyAccessor getPropertyAccessor() {
if (this.beanWrapper == null) {
this.beanWrapper = createBeanWrapper();
this.beanWrapper.setExtractOldValueForEditor(true);
this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
}
return this.beanWrapper;
}