Administrator
发布于 2022-10-20 / 39 阅读
0
0

SpringMVC数据转换、格式化、数据校验

数据转换

SpringMVC封装自定义类型对象的时候。javaBean要和页面提交的数据进行一一绑定!
但如果页面提交的是字符串呢?此时如果想转换成想要的数据,比如某个特定格式的String要转成对象。

前端进行数据传输,传输的都是文本,即String。那么String是如何转成我们想要的Integer等类型的呢?

 

数据转换器

 
在Spring中自带有众多的数据转换器

image-1666252932795


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));
  }

image-1666259368660
 

转换器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文件

image-1666259761633


 

自定义类型转换器

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,会对原有的一些造成覆盖

image-1666265708550


所以应该使用SpringMVC默认的FormattingConversionServiceFactoryBean

image-1666266077659

数据绑定

数据绑定流程

① Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例
传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象

② DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、
数据格式化工作。将 Servlet 中的请求信息填充到入参对象中

③ 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,
并最终生成数据绑定结果 BindingData 对象

④ Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参

Image [3]-1666266226586

说明补充

数据绑定和数据转换是分不开的,主要内容还是进行数据转换,然后根据入参的名字数据类型进行绑定

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文件夹下;

此部分内容先跳过


评论