zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    UVtool 工具解释1
    系统自己提供的 非常方便进行轴向的改变。
    解释脚本语言 计算两个向量的夹角度数。
    转换到正交视图的算法。
    对于 位 的判断我一般都转数组 其实这里有这个很好、
    服务器开启防火墙时千万别忘了开3389!
    j2me笔记
    成为那一小部分人!
    SEO笔记
    Java代码优化方案 J2ME内存优化
  • 原文地址:https://www.cnblogs.com/MattCheng/p/9183501.html
Copyright © 2011-2022 走看看