Spring中自定义数据类型转换的方法详解

目录
  • 类型转换服务
  • 实现Converter接口
  • 实现ConverterFactory接口
  • 实现GenericConverter接口

环境:Spring5.3.12.RELEASE。

Spring 3引入了一个core.onvert包,提供一个通用类型转换系统。系统定义了一个SPI来实现类型转换逻辑,以及一个API来在运行时执行类型转换。在Spring容器中,可以使用这个系统作为PropertyEditor实现的替代,将外部化的bean属性值字符串转换为所需的属性类型。还可以在应用程序中需要类型转换的任何地方使用公共API。

类型转换服务

ConversionService 类型转换服务的接口。

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

在大多数情况下我们应该实现。

ConfigurableConversionService可配置的类型转换服务接口。该接口整合ConversionService的所有操作和ConverterRegistry接口的相关操作,可对具体的转换进行增删。在应用程序上下文引导代码中处理ConfigurableEnvironment实例时,后者特别有用。

ConfigurableConversionService接口。

public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {
}

Spring提供了GenericConversionService 实现类;该类适合在大多数环境中使用的基本 ConversionService实现。通过

ConfigurableConversionService接口间接实现ConverterRegistry作为注册API。该类没有提供默认的类型转换功能,需要我们自己添加转换接口。

示例:

GenericConversionService gcs = new GenericConversionService() ;
Long result = gcs.convert("10", Long.class) ;
System.out.println(result) ;

以上代码运行将报错:

Exception in thread "main" org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.lang.Long]
  at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322)
  at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
  at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175)
  at com.pack.main.conversion.GenericConversionServiceMain.main(GenericConversionServiceMain.java:9)

没有转换接口发现错误。

FormattingConversionService 类是GenericConversionService 子类对象,主要作用就是增加了格式化功能(还是类型转换的一种表现),该类提供了 Printer和Parser的支持,可对对象进行打印展示及将源数据解析成目标对象,示例:

FormattingConversionService fcs = new FormattingConversionService() ;
fcs.addParser(new Parser<Teacher>() {
  @Override
  public Teacher parse(String text, Locale locale) throws ParseException {
    String[] t = text.split("\\|") ;
    return new Teacher(t[0], Integer.valueOf(t[1])) ;
  }
});
System.out.println(fcs.convert("张晶晶|26", Teacher.class)) ;

这里的addParser方法最后还是将Parser转换为GenericConverter。

将对象转换为可读的信息,示例:

FormattingConversionService fcs = new FormattingConversionService() ;
fcs.addPrinter(new Printer<Teacher>() {
  @Override
  public String print(Teacher object, Locale locale) {
    return "【 name = " + object.getName() + ", age = " + object.getAge() + "】" ;
  }
});
System.out.println(fcs.convert(new Teacher("张晶晶", 26), String.class)) ;

以上介绍的类型转换服务默认没有任何的类型转换能力,都需要我们自定义添加,在Spring中还提供了DefaultConversionService 和 WebConversionService。

通过名称知道WebConversionService 针对Web项目,但是你也是可以在非Web项目中使用。这里我就介绍DefaultConversionService 。先看示例:

DefaultConversionService dcs = new DefaultConversionService() ;
Long result = dcs.convert("10", Long.class) ;
Date date = dcs.convert("2022-07-01", Date.class) ;
System.out.println(result) ;
System.out.println(date) ;

上面两个类型的转换都能成功,为什么呢?因为DefaultConversionService 内部已经帮我们注册了很多的类型转换,源码:

public class DefaultConversionService extends GenericConversionService {
  public DefaultConversionService() {
    addDefaultConverters(this);
  }
  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));
  }
}

通常我们一般都是使用DefaultConversionService。

在Web环境下默认使用的WebConversionService ,这里以SpringBoot为例,源码如下:

public class WebMvcAutoConfiguration {
  public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    @Bean
    @Override
    public FormattingConversionService mvcConversionService() {
      Format format = this.mvcProperties.getFormat();
      WebConversionService conversionService = new WebConversionService(new DateTimeFormatters().dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
      addFormatters(conversionService);
      return conversionService;
    }
  }
}

WebConversionService的继承关系

public class WebConversionService extends DefaultFormattingConversionService {}
public class DefaultFormattingConversionService extends FormattingConversionService {}
public class FormattingConversionService extends GenericConversionService
  implements FormatterRegistry, EmbeddedValueResolverAware {
}

Spring还提供了一个ConversionServiceFactoryBean来注册我们自定义的类型转换。

public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
  private Set<?> converters;
  private GenericConversionService conversionService;
  public void setConverters(Set<?> converters) {
    this.converters = converters;
  }
  @Override
  public void afterPropertiesSet() {
    this.conversionService = createConversionService();
    ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
  }
  protected GenericConversionService createConversionService() {
    return new DefaultConversionService();
  }
  @Override
  public ConversionService getObject() {
    return this.conversionService;
  }
  @Override
  public Class<? extends ConversionService> getObjectType() {
    return GenericConversionService.class;
  }
  @Override
  public boolean isSingleton() {
    return true;
  }
}

我们可以定个该Bean,然后注入converters属性值。

@Bean
public ConversionServiceFactoryBean conversionService() {
  ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean() ;
  // 自定义的类型转换
  factory.setConverters(...) ;
  return factory ;
}

Spring的类型转换服务是不是挺简单?接下来介绍Spring提供的各种自定义类型转换方式。

接下来我们都是以示例为主。

实现Converter接口

Converter接口。

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

自定义Converter接口,我们使用匿名内部类实现。

DefaultConversionService cs = new DefaultConversionService();
// 自定义类型转换器,当有了ConversionService完全可以替代PropertyEditor
cs.addConverter(new Converter<String, Users>() {
  public Users convert(String source) {
    String[] temp = source.split("\\|") ;
    return new Users(temp[0], Integer.parseInt(temp[1])) ;
  }
}) ;
Users users = cs.convert("张三|100", Users.class) ;
System.out.println(users) ;

实现ConverterFactory接口

当你需要集中整个类层次结构的转换逻辑时(例如,当从String转换到Enum对象时),你可以实现ConverterFactory。也就是有继承关系的类型转换。

ConverterFactory接口。

public interface ConverterFactory<S, R> {
  <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

自定义工厂类。

public class EnvObjectConvert implements ConverterFactory<String, EnvObject> {
  @Override
  public <T extends EnvObject> Converter<String, T> getConverter(Class<T> targetType) {
    return new EnvConvert<T>();
  }
  private class EnvConvert<T extends EnvObject> implements Converter<String, T> {
    @Override
    public T convert(String source) {
      String[] temp = source.split("\\|") ;
      return (T) new EnvObject(temp[0], Integer.valueOf(temp[1])) ;
    }
  }
}

实现GenericConverter接口

当你需要复杂的Converter实现时,请考虑使用GenericConverter接口。与Converter相比,GenericConverter具有更灵活但强类型较少的签名,因此它支持在多个源类型和目标类型之间进行转换。此外,GenericConverter提供了可用的源和目标字段上下文,你可以在实现转换逻辑时使用它们。这样的上下文允许通过字段注释或在字段签名上声明的泛型信息驱动类型转换。下面的清单显示了GenericConverter的接口定义:

GenericConverter接口。

public interface GenericConverter {
  Set<ConvertiblePair> getConvertibleTypes();
  Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

自定义GenericConverter。

public class CustomGenericConverter implements GenericConverter {
  @Override
  public Set<ConvertiblePair> getConvertibleTypes() {
    // 这里我们可以定义多组的类型转换关系
    ConvertiblePair teacherPair = new ConvertiblePair(String.class, Teacher.class) ;
    ConvertiblePair studentPair = new ConvertiblePair(String.class, Student.class) ;
    Set<ConvertiblePair> pairs = new HashSet<>() ;
    pairs.add(teacherPair) ;
    pairs.add(studentPair) ;
    return pairs ;
  }
  @Override
  public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
    // 下面分别看到不同的类型做不同的处理
    String str = null ;
    if (sourceType.getObjectType() == String.class) {
      str = (String) source ;
    }
    if (targetType.getObjectType() == Teacher.class) {
      String[] t = str.split("\\|") ;
      return new Teacher(t[0], Integer.valueOf(t[1])) ;
    }
    if (targetType.getObjectType() == Student.class) {
      String[] t = str.split("\\|") ;
      return new Student(t[0], t[1]) ;
    }
    return null ;
  }
}

以上就是Spring中自定义数据类型转换的方法详解的详细内容,更多关于Spring数据类型转换的资料请关注我们其它相关文章!

(0)

相关推荐

  • Spring常用注解及http数据转换教程

    注意:本节内容需要结合2.2.2小节的内容联合起来一起看,然后理解 一.HTTP协议的四种传参方式 HTTP协议组成 协议内容示例 对应Spring注解 path info传参 /articles/12 (查询id为12的文章,12是参数) @PathVariable URL Query String传参 /articles?id=12 @RequestParam Body 传参 Content-Type: multipart/form-data @RequestParam Body 传参 Co

  • Spring MVC 自定义数据转换器的思路案例详解

    数据转换器是指将客户端 http 请求中的参数转换为业务方法中定义的形参,自定义表示开发者可以自主设计转换模式,HandlerAdapter 已经提供了通用的转换,比如将 String 转成 int,String 转成 double,表单数据的封装等,但是在特殊的业务场景下,HandlerAdapter 无法进行转换,就需要开发者自定义转换器. 我们需要实现 Converter 接口来协助 Spring MVC 完成数据类型的转换,下面通过两个案例来介绍如何自定义数据转换器. 案例一:客户端输入

  • 浅谈spring ioc的注入方式及注入不同的数据类型

    关于Spring-IoC的简单使用参考: spring ioc的简单实例及bean的作用域属性解析 1.通过set方法注入不同数据类型 测试类代码(set方式注入的属性一定要加set方法) /**通过set方法注入示例*/ public class IoC_By_Set { /**注入Integer类型参数*/ private Integer id; /**注入String类型参数*/ private String name; /**注入实体Bean*/ private User user; /

  • Spring依赖注入多种类型数据的示例代码

    目录 Student实体类 StudentsClass实体类 beans.xml 测试 Student实体类 package entity; import java.util.*; /** * @author LeDao * @company * @create 2022-02-13 21:26 */ public class Student { private int id; private String name; private StudentClass studentClass; pri

  • 深入理解Spring MVC的数据转换

    本文主要给大家介绍了关于Spring MVC数据转换的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 数据绑定 SpringMVC负责将request中的信息以一定的方式转换并绑定到处理方法的参数上.整个过程的处理核心是由DataBinder完成.转换流程如下: 1.DataBinder从ServletRequest中获取参数信息: 2.DataBinder获取处理方法的参数: 3.DataBinder调用ConversionService组件数据类型转换和数据格式化

  • Spring中自定义数据类型转换的方法详解

    目录 类型转换服务 实现Converter接口 实现ConverterFactory接口 实现GenericConverter接口 环境:Spring5.3.12.RELEASE. Spring 3引入了一个core.onvert包,提供一个通用类型转换系统.系统定义了一个SPI来实现类型转换逻辑,以及一个API来在运行时执行类型转换.在Spring容器中,可以使用这个系统作为PropertyEditor实现的替代,将外部化的bean属性值字符串转换为所需的属性类型.还可以在应用程序中需要类型转

  • Spring Boot自定义错误视图的方法详解

    Spring Boot缺省错误视图解析器 Web应用在处理请求的过程中发生错误是非常常见的情况,SpringBoot中为我们实现了一个错误视图解析器(DefaultErrorViewResolver).它基于一些常见的约定,尝试根据HTTP错误状态码解析出错误处理视图.它会在目录/error下针对提供的HTTP错误状态码搜索模板或者静态资源,比如,给定了HTTP状态码404,它会尝试搜索如下模板或者静态资源: /<templates>/error/404.<ext> - 这里<

  • Spring中bean集合注入的方法详解

    目录 Map注入 List注入 Set注入 数组注入 应用 哈喽大家好啊,我是Hydra. Spring作为项目中不可缺少的底层框架,提供的最基础的功能就是bean的管理了.bean的注入相信大家都比较熟悉了,但是有几种不太常用到的集合注入方式,可能有的同学会不太了解,今天我们就通过实例看看它的使用. 首先,声明一个接口: public interface UserDao { String getName(); } 然后定义两个类来分别实现这个接口,并通过@Component注解把bean放入s

  • Android中自定义一个View的方法详解

    本文实例讲述了Android中自定义一个View的方法.分享给大家供大家参考,具体如下: Android中自定义View的实现比较简单,无非就是继承父类,然后重载方法,即便如此,在实际编码中难免会遇到一些坑,我把自己遇到的一些问题和解决方法总结一下,希望对广大码友们有所帮助. 注意点① 用xml定义Layout时,Root element 最好使用merge 当我们需要继承一个布局比较复杂的ViewGroup(比较多的是LinearLayout.RelativeLayout)时,通常会用xml来

  • Spring中自定义Schema如何解析生效详解

    前言 随着 Spring Boot 的日渐流行,应用里的大部分配置都被隐藏了起来,我们仅需要关心真正的业务内容, Controller, Service, Repository,拿起键盘就是一通业务代码的Coding,具体的 Component Scan,View,PlaceHolder ... 都可以抛在脑后.但其实这种零配置在 Java 应用开发中,还真不太久. 「由奢入俭难」,不少开发者都经历过 Spring XML 配置的冗长,再回到这种配置确实不好受. 但有些时候,由于配置的内容在 S

  • Spring中@Scheduled功能的使用方法详解

    目录 前言 一.Spring @Scheduled Annotation 1.2 如何启用@Scheduled 注释 1.3 使用@Scheduled 注释 二.固定的延时和频率使用@Scheduled 三.配合cron表达式使用@Scheduled 四.使用properties文件配置Cron 五.使用context配置Cron 总结 前言 Spring 为任务调度和基于使用@Scheduled 注释的 cron 表达式的异步方法执行提供了极好的支持.可以将@Scheduled 注释与触发器元

  • MySql中删除数据表的方法详解

    目录 定义: 1 删除一个或多个没有被其他表关联的数据表 1.1 新建一张表 1.2 执行删除命令 1.3 结果检查 2 删除被其他表关联的主表 2.1 创建两张具有关联关系的表 2.2 执行删除DROP TABLE命令 2.3 取消外键关系,再删除. 定义:   删除数据表就是将数据库中已经存在的表从数据库中删除.注意,在删除表的同时,表的定义和表中所有的数据均会被删除.因此,在进行删除操作前,最好对表中的数据做一个备份,以免造成无法挽回的后果.本节将详细讲解数据库表的删除方法. 1 删除一个

  • Java实现自定义Excel数据排序的方法详解

    目录 1.引入jar包 2.自定义排序 通常,我们可以在Excel中对指定列数据执行升序或者降序排序,排序时可依据单元格中的数值.单元格颜色.字体颜色或图标等.在需要自定义排序情况下,我们也可以自行根据排序需要编辑数据排列顺序.本文,将通过Java应用程序来实现如何自定义排序. 1.引入jar包 使用jar包:Spire.Xls.jar version: 12.8.4 导入方法1:手动下载jar到本地,解压,然后找到lib文件夹下的Spire.Xls.jar文件.然后在IDEA中打开“Proje

  • Spring Boot项目中定制拦截器的方法详解

    这篇文章主要介绍了Spring Boot项目中定制拦截器的方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供HandlerInterceptor(拦截器)工具.根据文档,HandlerInterceptor的功能跟过滤器类似,但拦截器提供更精细的控制能力:在request被响应之前.request被响应之后.视

  • Spring MVC自定义日期类型转换器实例详解

    Spring MVC自定义日期类型转换器实例详解 WEB层采用Spring MVC框架,将查询到的数据传递给APP端或客户端,这没啥,但是坑的是实体类中有日期类型的属性,但是你必须提前格式化好之后返回给它们.说真的,以前真没这样做过,之前都是一口气查询到数据,然后在jsp页面上格式化,最后展示给用户.但是这次不同,这次我纯属操作数据,没有页面.直接从数据库拿数据给它们返数据.它们给我传数据我持久化数据,说到这里一个小问题就默默的来了. 首先把问题还原一下吧(这是一个数据导出功能),下图中用红框圈

随机推荐