Spring MVC 使用介绍(八)—— 类型转换

一、概述

spring类型转换有两种方式:

  • PropertyEditor:可实现String<--->Object 之间相互转换
  • Converter:可实现任意类型的相互转换

类型转换的过程中,当两者同时存在时,spring首先查找PropertyEditor进行类型转换,如果没有找到,则再查找Converter进行转换

 

二、PropertyEditor

1、基本介绍

PropertyEditor用于 String<--->Object 之间相互转换,spring内建了一些常用的PropertyEditor,如:

ClassEditor:  String<——>Class
FileEditor:  String<——>File
PatternEditor:  String<——>Pattern
URLEditor:  String<——>URL
ResourceEditor:  String<——>Resource

自定义的PropertyEditor须继承自PropertyEditorSupport,示例如下:

实体类

public class UserInfo {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

转换类

public class UserEditor extends PropertyEditorSupport  {
    
    public UserEditor() {
        System.out.println("UserEditor constructed");
    }
    
    @Override
    public String getAsText() {
        UserInfo userInfo = (UserInfo) this.getValue();
        return JSON.toJSONString(userInfo);
    }

    @Override
    public void setAsText(String text) throws java.lang.IllegalArgumentException {
        if (StringUtils.isEmpty(text)) {
            return;
        }
        
        UserInfo userInfo = JSON.parseObject(text, UserInfo.class);
        this.setValue(userInfo);
    }
}

控制器

@Controller
public class TestController5 {

    @RequestMapping("/type1")
    @ResponseBody
    public String testType1(@RequestParam("user") UserInfo userInfo) {
        System.out.println(userInfo.getName() + " " + userInfo.getAge());
        return "testType1";
    }
    
    @RequestMapping("/type2")
    @ResponseBody
    public String testType2() {
     System.out.println("void");
return "testType2"; }
   // 注册UserEditor @InitBinder
public void initBinder(WebDataBinder binder) { UserEditor userEditor = new UserEditor(); binder.registerCustomEditor(UserInfo.class, userEditor); System.out.println("initBinder invoked"); } }

web.xml与spring-mvc.xml配置略

启动后,先后访问:

http://localhost:8080/myweb/type1?user={"name":"matt","age":30}
http://localhost:8080/myweb/type2
http://localhost:8080/myweb/type1?user={"name":"matt","age":30}

输出:

UserEditor constructed
initBinder invoked
matt 30
void
UserEditor constructed
initBinder invoked
matt 30

从输出结果可以看出,处理方法若包含需要类型转换的参数,每次请求都会调用注册方法;处理方法如不包含则不会调用

2、自定义PropertyEditor的注册

自定义PropertyEditor的注册有三种级别:

  • 控制器级别,使用@InitBinder
  • web级别,使用WebBindingInitializer
  • 应用级别,自定义PropertyEditor与实体类同包,且命名为“实体名 + Editor”

i)控制器级别

控制器级别是在控制器类中使用@InitBinder注解来实现,注册范围为单个控制器,如上例所示

ii)web级别

web级别使用WebBindingInitializer来实现,注册范围为应用的所有控制器(即整个web层),示例:

WebBindingInitializer实现类

public class MyWebBindingInitializer implements WebBindingInitializer {

	public void initBinder(WebDataBinder binder, WebRequest request) {
		binder.registerCustomEditor(UserInfo.class, new UserEditor());
		System.out.println("initBinder invoked!");
	}
}

注册

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer">  
        <bean class="cn.matt.convertor.MyWebBindingInitializer"/>  
    </property> 
</bean> 

注释上例控制器中的注册方法,即可测试

该注册方法的调用方式与控制器级别相同,即处理方法若包含需要类型转换的参数,每次请求都会调用注册方法;处理方法如不包含则不会调用

补充:web级别的注册方法还有:1、基于继承的BaseController,2、基于@ControllerAdvice注解,这两种方式更简洁,详细可参考:

SpringMVC中利用@InitBinder来对页面数据进行解析绑定

Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理

iii)应用级别(推荐)

应用级别的注册范围为spring层,使用方式:自定义PropertyEditor与实体类同包,且命名为“实体名 + Editor”,示例:UserInfoEditor

该注册方法在每次类型转换时均创建新的实例

 

三、Converter

1、基本介绍

Converter接口定义:

public interface Converter<S, T> {
    T convert(S source);  
}   

Converter作为类型转换器,用于转换S类型到T类型,其实现必须是线程安全的且可以被共享,内建的常用Converter:

StringToBooleanConverter:  String----->Boolean
StringToEnumConverterFactory: String----->enum类型
EnumToStringConverter:  enum类型----->String

ConverterRegistry、ConversionService分别用于Converter的注册和运行支持,接口定义如下:

public interface ConverterRegistry {  
    void addConverter(Converter<?, ?> converter);  
    void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter);  
    void addConverter(GenericConverter converter);  
    void addConverterFactory(ConverterFactory<?, ?> converterFactory);  
    void removeConvertible(Class<?> sourceType, Class<?> targetType);  
}   
public interface ConversionService {  
    boolean canConvert(Class<?> sourceType, Class<?> targetType);  
    boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);  
    <T> T convert(Object source, Class<T> targetType);  
    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);  
}   

Spring提供了两个默认实现(其都实现了ConverterRegistryConversionService接口):

  • DefaultConversionService:默认的类型转换服务实现
  • DefaultFormattingConversionService:带数据格式化支持的类型转换服务实现,一般使用该实现即可

使用示例如下:

转换器

public class StringToUserInfoConverter implements Converter<String, UserInfo> {
    
    public StringToUserInfoConverter() {
        System.out.println("StringToUserInfoConverter constructed");
    }

    public UserInfo convert(String source) {
        if (StringUtils.isEmpty(source)) {
            return null;
        }
        
        UserInfo userInfo = JSON.parseObject(source, UserInfo.class);
        return userInfo;
    }
}

测试

@Test
public void test() {
    DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
    conversionService.addConverter(new StringToUserInfoConverter());
    UserInfo userInfo = conversionService.convert("{\"name\":\"matt\",\"age\":30}", UserInfo.class);
    System.out.println(userInfo.getName());
}

2、spring mvc中使用Converter

配置

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">  
    <property name="converters">  
       <list>  
            <bean class="cn.matt.convertor.StringToUserInfoConverter"/>  
        </list>  
    </property>  
</bean>  

<bean id="webBindingInitializer" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">  
    <property name="conversionService" ref="conversionService"/>  
</bean>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer" ref="webBindingInitializer" />  
</bean> 

控制器

@Controller
public class TestController6 {
    @RequestMapping("/converter")
    @ResponseBody
    public String testConverter(@RequestParam("user") UserInfo userInfo) {
        System.out.println(userInfo.getName() + " " + userInfo.getAge());
        return "testConverter";
    }
}

启动后,访问http://localhost:8080/myweb/type1?user={"name":"matt","age":30} 即可

补充:当使用<mvc:annotation-driven />时,配置如下

<mvc:annotation-driven conversion-service="conversionService"/>

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">  
    <property name="converters">  
       <list>  
            <bean class="cn.matt.convertor.StringToUserInfoConverter"/>  
        </list>  
    </property>  
</bean>  

 

参考:

SpringMVC数据类型转换——跟着开涛学SpringMVC

@EnableWebMvc

 

posted @ 2018-06-14 16:02  Matt_Cheng  阅读(223)  评论(0)    收藏  举报