Spring MVC数据转换

Spring MVC数据转换


Spring MVC会根据请求方法签名不同,将请求息消息中的信息以一定的方式转换并绑定到请求方法的参数中。在请求消息到达真正调用处理方法的这一段时间内,Spring MVC还会完成很多其他的工作,包括请求信息转换、数据转换、数据格式化以及数据校验等。

数据绑定流程

Spring MVC通过反射机制对目标处理方法的签名进行分析,并将请求消息绑定到处理方法的参数中。数据绑定的核心部件是DateBinder,其运行机制如下图:

Spring MVC数据转换

Spring MVC框架将Servlet对象及处理方法的参数对象实例传递给DataBinder,它会调用装配在Spring Web上下文中的ConversionService组件进行数据类型转换、数据格式化工作,并将ServletRequest中的消息填充到参数对象中。然后在调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验,并最终生成数据绑定结果BindingResult对象。BindingResult包含已完成数据绑定的参数对象,还包含相应的校验错误对象,Spring MVC抽取BindingResult中的参数对象以及校验错误对象,将它们赋给处理方法的相应参数。

数据转换

org.springframework.core.convert.ConversionService是Spring类型转换体系的核心接口,在该接口中定义了以下4个方法:

  • boolean canConvert(Class<?> var1, Class<?> var2);

判断是否可以将一个Java类转换为另一个Java类.

  • boolean canConvert(TypeDescriptor var1, TypeDescriptor var2);

需要转换的类将以成员变量的方式出现,TypeDescriptor不但描述了需要转换类的信息,还描述了类的上下文信息,例如成员变量上的注解成员变量是否以数组、集合或者Map的方式呈现。类型转换逻辑可以利用这些信息做出各种灵活的控制。

  • <T> T convert(Object var1, Class<T> var2);

将原类型对象转换为目标类型对象。

  • Object convert(Object var1, TypeDescriptor var2, TypeDescriptor var3);

将对象从源类型对象转换为目标类型对象,通常会利用到类中的上下文信息。

可以利用org.springframework.context.support.ConversionServiceFactoryBean在Spring的上下文中定义一个ConversionService。Spring将自动识别出上下文中的ConversionService,并在Spring MVC处理方法的参数绑定中使用它进行数据转换。示例如下:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"></bean>

Spring支持的转换器

Spring在org.springframework.core.convert.converter包中定义了三种类型的转换器接口,我们可以实现其中任意一个,并将它们作为自定义转换器注册到ConversionServiceFactoryBean当中。这三种类型的转换器接口如下所示:
  • Converter<S, T>。这个接口是Spring中最简单的一个转换器接口,该接口中只有一个方法:

T convert(S var1) 该方法负责将S类型的对象转换为T类型的对象.

  • ConverterFactory<S, R>。 如果希望将一种类型的对象转换为另一种类型及其子类对象,比如将String转换为Number以及子类Integer、Double等对象,就需要一系列的Converter,如StringToInteger、StringToDouble等。ConverterFactory<S, R>接口的作用就是将相同系列多个Converter封装在一起。该接口中也只有一个方法:

<T extends R> Converter<S, T> getConverter(Class<T> var1);
S为转换的源类型,R为目标类型的基类,T为R的子类。

  • GenericConverter。这个接口会根据源类型对象以及目标对象的上下文信息进行类型转换。该接口中定义了两个方法:
  1. Set<GenericConverter.ConvertiblePair> getConvertibleTypes();
  2. Object convert(Object var1, TypeDescriptor var2, TypeDescriptor var3);

ConvertiblePair封装了源类型和目标类型,而TypeDescriptor包含了需要转换的类型对象的上下文信息,因此GenericConverter接口的convert()方法可以利用这些上下文信息进行类型转换的工作。

自定义一个字符转换器

ConversionService装配

代码如下:

public class String2DateConverter implements Converter<String, Date>{

    // 日期类型模版
    private String datePattern;

    public void setDatePattern(String datePattern) {
        this.datePattern = datePattern;
    }

    // Converter<S,T>接口的类型转换方法
    public Date convert(String s) {

        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.datePattern);
            return simpleDateFormat.parse(s);

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("日期转换失败!");
            return null;
        }


    }
}

在springmvc-config.xml中加入自定义字符转换器:

<!--装配自定义的类型转换器-->
<mvc:annotation-driven conversion-service="conversionService"/>

<!--自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.ccgogoing.javaee.converter.String2DateConverter" p:datePattern="yyyy-MM-dd"/>
         </list>
    </property>
</bean>

在mvc配置文件中,使用了<mvc:annotation-driven/>标签,该标签可以简化SpringMVC相关配置,自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个bean,这是springMVC为@Controller分发请求所必须的。
<mvc:annotation-driven/>标签还会注册一个默认的ConversionService,即FormattingConversionServiceFactoryBean转换类,因此需要显式定义一个ConversionService覆盖默认实现类;

使用InitBinder添加自定义编辑器转换数据

// 自定义属性编辑器
public class DateEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date date = simpleDateFormat.parse(text);
            setValue(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
// 在控制器初始化时注册属性编辑器
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // 注册自定义编辑器
        binder.registerCustomEditor(Date.class, new DateEditor());
    }

在UserController中增加一个initBinder方法,并使用@InitBinder注解,该注解会在控制器初始化时注册属性编辑器。WebDataBinder对象用于处理请求消息和处理方法的绑定工作.binder.registerCustomEditor方法将传入的Date类型使用DateEditer类进行转换.

使用WebBindingInitializer注册全局自定义编辑器转换数据

public class DateWebBingdingInitizlizer implements WebBindingInitializer {

    public void initBinder(WebDataBinder webDataBinder, WebRequest webRequest) {
        // 注册自定义编辑器
        webDataBinder.registerCustomEditor(Date.class,new DateEditor());
    }
}

DateWebBingdingInitizlizer类实现WebBindingInitializer接口,并在initBinder()方法中注册自定义编辑器DateEditor类.
UserController类中不需要在使用@InitBinder 注解注释的方法,而是在springmvc-config.xml配置文件中配置全局的自定义编辑器:

<!--通过AnnotationMethodHandlerAdapter装配自定义编辑器-->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="com.ccgogoing.javaee.binding.DateWebBingdingInitizlizer"/>
    </property>
</bean>

多种转换器的优先顺序

  1. 查询通过@InitBinder装配的自定义编辑器
  2. 查询通过ConversionService装配的自定义转换器
  3. 查询通过WebBindingInitializer接口装配的全局自定义编辑器

相关推荐