环境:s

Spring3引入了提供通用类型转换系统的core.convert软件包。

系统定义一个SPI来实现类型转换逻辑,定义一个API来在运行时执行类型转换。在Spring容器中,你可以使用此系统作为PropertyEditor实现的替代方案,将外部化的bean属性值字符串转换为所需的属性类型。你还可以在应用程序中需要进行类型转换的任何位置使用公共API。

Converter SPI

实现类型转换逻辑的SPI是简单且强类型的,如以下接口定义所示:

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

要创建自己的转换器,需要实现converter接口,并将S参数化为要转换的类型,将T参数化为要转换的类型。如果需要将S的集合或数组转换为T的集合或集合,还可以透明地应用这样的转换器,前提是同时注册了委托数组或集合转换器(默认情况下,DefaultConversionService会这样做)。

对于每个转换调用,保证源参数source不为null。如果转换失败,转换器可能会抛出任何未检查的异常。具体来说,它应该抛出IllegalArgumentException以报告无效的源值。注意确保转换器实现是线程安全的。

为了方便起见,core.convert.support包中提供了几种转换器实现。其中包括从字符串到数字和其他常见类型的转换器。下表显示了StringToInteger类,它是典型的转换器实现:

package org.;
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return In(source);
}
}

使用ConverterFactory

当需要集中整个类层次结构的转换逻辑时(例如,从字符串转换为枚举对象时),可以实现ConverterFactory,如下例所示:

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

将S参数化为要转换的类型,将R参数化为定义可转换为的类范围的基类型。然后实现getConverter(类<T>),其中T是R的一个子类。


StringToEnumConverterFactory为例:

package org.;

final class StringToEnumConverterFactory implements ConverterFactory<String, enum> {

public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {

private Class<T> enumType;

public StringToEnumConverter(Class<T> enumType) {
= enumType;
}

public T convert(String source) {
return (T) Enum.valueOf(, ());
}
}
}

自定义类型转换

现在需要将接受的字符串转换为Users对象

public class Users {
private String name ;
private Integer age ;
}

接口

@GetMapping("/convert2")
public Object convert2(Users users) {
return users ;
}

调用接口

如上,通过get方式users的参数通过逗号分割。接下来就是写类型转换器了

@SuppressWarnings({"rawtypes", "unchecked"})
public class UsersConverterFactory implements ConverterFactory<String, Users> {

@Override
public <T extends Users> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToUsersConverter() ;
}

private final class StringToUsersConverter<T extends Users> implements Converter<String, Users> {
public Users convert(String source) {
if (source == null || ().length() == 0) {
return null ;
}
Users user = new Users() ;
// 下面做简单处理,不做校验
String[] values = (",") ;
u(values[0]) ;
u(values[1]));
return user ;
}
}
}

注册类型转换器

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addFormatters(FormatterRegistry registry) {
regi(new UsersConverterFactory()) ;
}

}

编程方式使用类型转换器

要以编程方式使用ConversionService实例,可以像对任何其他bean一样向其注入引用。以下示例显示了如何执行此操作:

我们使用系统内置的类型转换器:字符串类型转枚举类型

public enum PayStatus {

START, PROCESS, COMPLETE

}@RestController
@requestMapping("/users")
public class UsersController {

@Resource
private ConversionService cs ;

@GetMapping("/convert")
public Object convert(String status) {
boolean canConvert = cs.canConver, PayS) ;
return canConvert ? cs.convert(status, PayS) : "UNKNOW";
}

}

先判断是否能够转换,其实就是判断有没有从source到target的类型转换器存在。

类型转换的实现原理

以自定义类型转换器为例

SpringMVC在进行接口调用是会执行相应的参数解析,确定了参数解析器后会执行转换服务。

  • 查找参数解析器
    查找合适的HandlerMethodArgumentResolver

public class InvocableHandlerMethod extends HandlerMethod {
protected Object[] getMethodArgumentValues(...) throws Exception {
// 查找合适的参数解析器(本例应用的是ServletModelAttributeMethodProcessor)
if (!(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = (parameter, mavContainer, Request, );
}
}
}

  • 解析参数

执行
ServletModelAttributeMethodProcessor父类的resolveArgument方法

public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver {
public final Object resolveArgument(...) {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
}
public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {
protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
// 这里得到的是原始值
String value = getRequestValueForAttribute(attributeName, request);
if (value != null) {
Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
if (attribute != null) {
return attribute;
}
}
return (attributeName, parameter, binderFactory, request);
}
protected Object createAttributeFromRequestValue(String sourceValue, String attributeName,MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
DataBinder binder = binderFac(request, null, attributeName);
// ConversionService对象是在容器启动的时候就初始化好的
// 在WebMvcAutoConfiguration#mvcConversionService方法中初始化。
ConversionService conversionService = binder.getConversionService();
if (conversionService != null) {
TypeDescriptor source = Ty);
TypeDescriptor target = new TypeDescriptor(parameter);
// 判断是否有合适的类型转换器
if (source, target)) {
// 此方法中进行类型的转换
return binder.convertIfNecessary(sourceValue, (), parameter);
}
}
return null;
}
}

完毕!!!

给个关注+转发谢谢

公众号:Springboot实战案例锦集

Spring MVC 异步请求方式

Spring MVC 异常处理方式

Spring 自定义Advisor以编程的方式实现AOP

Spring Cloud Sentinel 熔断降级

Spring Cloud Sentinel 基础配置

SpringBoot项目中应用Spring Batch批处理框架,处理大数据新方案

SpringMVC参数统一验证方法

SpringBoot项目查看线上日志

SpringCloud Feign实现原理源分析

Spring Cloud全链路追踪SkyWalking及整合Elasticsearch

Springboot整合MyBatis参数传值方式

Springboot整合MyBatis复杂查询应用

Spring Bean的作用域scope你知道多少?如何自定义作用域?

Spring Cloud链路追踪zipkin及整合Elasticsearch存储

相关推荐