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

作者:Spring全家桶实战案例 时间:2022-10-09 02:56:51 

环境: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 ;
 }
}

来源:https://developer.51cto.com/article/711966.html

标签:Spring,自定义,数据,类型,转换
0
投稿

猜你喜欢

  • Java Web 简单的分页显示实例代码

    2023-04-19 20:06:21
  • C#利用Random得随机数求均值、方差、正态分布的方法

    2022-08-28 14:26:37
  • Spring Boot 详细分析Conditional自动化配置注解

    2021-11-25 21:56:14
  • 详解Spring Cloud中Hystrix的请求合并

    2022-07-06 14:53:06
  • Android 谷歌推荐的VR实现方式(分享)

    2022-01-01 10:22:44
  • java中Memcached的使用实例(包括与Spring整合)

    2021-11-28 13:29:06
  • C#运行时相互关系浅析

    2023-09-08 00:12:43
  • java 值Document解析xml详细介绍

    2021-11-13 15:59:00
  • Java中使用内存映射实现大文件上传实例

    2022-01-16 05:02:16
  • 使用Filter过滤器中访问getSession()要转化

    2022-10-01 16:20:04
  • Java实现数组转字符串及字符串转数组的方法分析

    2022-04-14 10:53:44
  • android Jsoup获取网站内容 android获取新闻标题实例

    2021-09-24 22:56:03
  • 详解Java并发包中线程池ThreadPoolExecutor

    2022-03-23 19:57:20
  • Android程序锁的实现以及逻辑

    2022-09-29 19:50:57
  • 浅谈java对象结构 对象头 Markword

    2023-04-26 04:50:34
  • jpa多数据源时Hibernate配置自动生成表不生效的解决

    2023-04-24 06:19:55
  • SpringCloud2020整合Nacos-Bootstrap配置不生效的解决

    2023-11-29 02:42:47
  • Android Room数据库多表查询的使用实例

    2022-11-07 02:49:49
  • 用Spring Native将SpringBoot程序转换为GraalVM

    2023-09-17 16:23:00
  • 在 C# 中使用 插值字符串

    2023-06-23 07:09:50
  • asp之家 软件编程 m.aspxhome.com