数据转换
SpringMVC封装自定义类型对象的时候。javaBean要和页面提交的数据进行一一绑定!
但如果页面提交的是字符串呢?此时如果想转换成想要的数据,比如某个特定格式的String要转成对象。
前端进行数据传输,传输的都是文本,即String。那么String是如何转成我们想要的Integer等类型的呢?
数据转换器
在Spring中自带有众多的数据转换器
1、当目标方法运行
mav = invokeHandlerMethod(request, response, handlerMethod);
2、invokeHandlerMethod有getDataBinderFactory方法
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
getDataBinderFactory方法会根据初始化的绑定方法创建一个数据绑定工厂
return createDataBinderFactory(initBinderMethods);
3、createDataBinderFactory方法
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
其中getWebBindingInitializer()会返回配置的WebBindingInitializer,如果没有则返回null
所有配置的converter都会在这里获取到
4、补充
默认的converter组件的注入和其他组件的注册也是一样的,参考SpringMVC源码第一篇,
在AnnotationDrivenBeanDefinitionParser的
public BeanDefinition parse(Element element, ParserContext context) {}这么一个方法
里面有一大段很长的一段实现。
其中
--
//你自己配有了就用你配的,没有就用他的默认实现
--
if (element.hasAttribute("conversion-service")) {
conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));
}
RuntimeBeanReference conversionService = getConversionService(element, source, context);
就是将converter组件注册进容器中,但还没有实例化
具体实现是
RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
所以,MVC中默认的converter组件类型是FormattingConversionServiceFactoryBean。
他的实例化会在创建bean的时候调用DefaultConversionService.addDefaultConverters(this);进行
addDefaultConverters添加默认转换器源码
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
转换器converter总结
默认的converter组件的注册和handlermapping等组件一样。
在AnnotationDrivenBeanDefinitionParser的
public BeanDefinition parse(Element element, ParserContext context) {}这么一个方法内部都是写好的。
默认的实现是FormattingConversionServiceFactoryBean,你配置了就用你配置的。
其初始化则和handlermapping它们不同,converter的初始化是在创建bean的时候进行的。
如果你在配置文件什么转换器都没配怎么办?
此时,在项目启动的时候就不会注册组件,在需要类型转换的时候binderFactory也没有转换器可以使用,
但是,无论你是否配置,mvc都会创建一个binder
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
用创建的binderreturn getSimpleTypeConverter();一个converter,实现类型的转换。
binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
在内部,你有合适的类型转换器则用你的
没有,也有默认的doConvertValue转换方法
convertIfNecessary细节
--
//如果有配置有converter,那么这里就会获取到合适的converter,然后进行转换
--
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
-------------------------------------
没有配置converter,会将传进来的值转成String类型
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
--
根据请求的参数类型找到一个编辑器
editor = findDefaultEditor(requiredType);
}
--
进行转换
--
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
没配置任何xml文件
自定义类型转换器
1、实现Converter接口,将要转换的类型和被转换类型<>写入
public class MyConverter implements Converter<String, Book> {
@Override
public Book convert(String source) {
--
具体的实现逻辑
--
String[] strings = source.split("-");
Book book = new Book();
book.setName(strings[0]);
book.setAuthor(strings[1]);
book.setPrice(Double.parseDouble(strings[2]));
book.setSales(Integer.parseInt(strings[3]));
book.setStock(Integer.parseInt(strings[4]));
return book;
}
}
2、dispatcher-servlet.xml文件配置
告诉SpringMVC用我们配置的注解
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.lly.Converter.MyConverter"></bean>
</set>
</property>
</bean>
3、某些区别
ConversionServiceFactoryBean里的converters是Set,会对原有的一些造成覆盖
所以应该使用SpringMVC默认的FormattingConversionServiceFactoryBean
数据绑定
数据绑定流程
① Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例
传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
② DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、
数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
③ 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,
并最终生成数据绑定结果 BindingData 对象
④ Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
说明补充
数据绑定和数据转换是分不开的,主要内容还是进行数据转换,然后根据入参的名字数据类型进行绑定
ps:
<mvc:annotation-driven />
<mvc:default-servlet-handler/>
两者的作用可以参考前面关于组件注入的分析,一样的
--------二者的区别--------------
1、都没有,动态资源能访问,因为有默认的实现策略,但静态资源不行
2、 <mvc:default-servlet-handler/>配了,静态资源能访问,动态不行。加了
会多出一个SimpleUrlHandlerMapping:将请求直接交给tomcat;有他,静态资源就没问题;
3、<mvc:annotation-driven />没配底层也会有默认的实现,配了2,就得配它,不然默认的策略不会执行
数据校验
我们提交的数据必须是合法的
前端校验:js+正则表达式;但不够安全,可以禁用掉js。
后端校验:重要数据也是必须的;
1)、校验成功!数据合法
2)、校验失败?
只做前端校验是不安全的;
在重要数据一定要加上后端验证;
1)、可以写程序将我们每一个数据取出进行校验,如果失败直接来到添加页面,提示其重新填写;x
2)、SpringMVC;可以JSR303来做数据校验
JDBC:规范---实现(各个厂商的驱动包)
JSR303:规范-----Hibernate Validator(第三方校验框架)
3)、如何快速的进行后端校验;
1)、导入校验框架的jar包;
有几个带el的jar不导入;tomcat中有;如果tomcat的版本是
7.0以下;tomcat7.0以上el表达式比较强大;
如果是7.0以下将带el的几个jar放在tomcat的lib文件夹下;
此部分内容先跳过