前言
一直想开发一个功能比较强大的项目,但是一直没有动手,最近终于有点时间来折腾它了。由于时隔两年没有接触前端了,所以需要一个小项目先练练手感。等这个项目完工之后在着手搞一个大工程。都说好记星不如烂笔头,现在就将这一个过程记录下来,万一有什么踩坑的地方,也可以提示后来人。
背景
由于项目中需要使用到日期和字符串的一个转换,使用到了Converter,所以记录下来。
/**
* 日期转换
*/
@Component
public class DateConverter implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String source) {
try {
return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} catch (Exception e) {
}
return null;
}
}
Spring提供了3种converter接口,分别是Converter、ConverterFactory和GenericConverter.一般用于1:1, 1:N, N:N的source->target类型转化
Converter
public interface Converter<S, T> {
// 将S转换成T
T convert(S source);
}
GenericConverter
用于在两种或更多种类型之间转换的通用转换器接口
public interface GenericConverter {
@Nullable
Set<GenericConverter.ConvertiblePair> getConvertibleTypes();
@Nullable
Object convert(@Nullable Object var1, TypeDescriptor var2, TypeDescriptor var3);
public static final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
public Class<?> getSourceType() {
return this.sourceType;
}
public Class<?> getTargetType() {
return this.targetType;
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
} else if (other != null && other.getClass() == GenericConverter.ConvertiblePair.class) {
GenericConverter.ConvertiblePair otherPair = (GenericConverter.ConvertiblePair)other;
return this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType;
} else {
return false;
}
}
public int hashCode() {
return this.sourceType.hashCode() * 31 + this.targetType.hashCode();
}
public String toString() {
return this.sourceType.getName() + " -> " + this.targetType.getName();
}
}
}
ConverterFactory
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> var1);
}
TyperConverter
- 定义类型转换方法的接口。通常(但不一定)与PropertyEditorRegistry接口一起实现,通常接口TypeConverter的实现是基于非线程安全的PropertyEditors类,因此也不是线程安全的
public interface TypeConverter {
// 将参数中的var1转换成var2类型,从String到任何类型的转换通常使用 PropertyEditor类的setAsText方法或ConversionService中的Spring Converter
@Nullable
<T> T convertIfNecessary(@Nullable Object var1, @Nullable Class<T> var2) throws TypeMismatchException;
// 意义同上,增加了作为转换目标的方法参数,主要用于分析泛型类型,可能是null
@Nullable
<T> T convertIfNecessary(@Nullable Object var1, @Nullable Class<T> var2, @Nullable MethodParameter var3) throws TypeMismatchException;
// 意义同上,增加了转换目标的反射field
@Nullable
<T> T convertIfNecessary(@Nullable Object var1, @Nullable Class<T> var2, @Nullable Field var3) throws TypeMismatchException;
@Nullable
default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
}
}
TypeConverterSupport
- TypeConverter的基本实现类,同时也是BeanWrapperImpl类的依赖类
public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
// 委托给TypeConverterDelegate来转换
@Nullable
TypeConverterDelegate typeConverterDelegate;
public TypeConverterSupport() {
}
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException {
return this.convertIfNecessary(value, requiredType, TypeDescriptor.valueOf(requiredType));
}
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam) throws TypeMismatchException {
return this.convertIfNecessary(value, requiredType, methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType));
}
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field) throws TypeMismatchException {
return this.convertIfNecessary(value, requiredType, field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType));
}
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
return this.typeConverterDelegate.convertIfNecessary((String)null, (Object)null, value, requiredType, typeDescriptor);
} catch (IllegalStateException | ConverterNotFoundException var5) {
throw new ConversionNotSupportedException(value, requiredType, var5);
} catch (IllegalArgumentException | ConversionException var6) {
throw new TypeMismatchException(value, requiredType, var6);
}
}
}
TypeConverterDelegate
- 类型转换的委托类,所有类型转换的工作都由该类完成,即将属性转换为其他类型的Spring内部使用方法(内部实现: 先使用PropertyEditor转换器器转换,如果没找到对应的转换器器,会⽤ConversionService来进⾏行行对象转换
class TypeConverterDelegate {
private static final Log logger = LogFactory.getLog(TypeConverterDelegate.class);
private final PropertyEditorRegistrySupport propertyEditorRegistry;
@Nullable
private final Object targetObject;
public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry) {
this(propertyEditorRegistry, (Object)null);
}
public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry, @Nullable Object targetObject) {
this.propertyEditorRegistry = propertyEditorRegistry;
this.targetObject = targetObject;
}
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, Object newValue, @Nullable Class<T> requiredType) throws IllegalArgumentException {
return this.convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
}
@Nullable
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
} catch (ConversionFailedException var14) {
conversionAttemptEx = var14;
}
}
}
Object convertedValue = newValue;
if (editor != null || requiredType != null && !ClassUtils.isAssignableValue(requiredType, newValue)) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && newValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String)newValue);
}
}
}
if (editor == null) {
editor = this.findDefaultEditor(requiredType);
}
convertedValue = this.doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
if (requiredType != null) {
if (convertedValue != null) {
if (Object.class == requiredType) {
return convertedValue;
}
if (requiredType.isArray()) {
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String)convertedValue);
}
return this.convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
if (convertedValue instanceof Collection) {
convertedValue = this.convertToTypedCollection((Collection)convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
} else if (convertedValue instanceof Map) {
convertedValue = this.convertToTypedMap((Map)convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
return convertedValue.toString();
}
if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor<T> strCtor = requiredType.getConstructor(String.class);
return BeanUtils.instantiateClass(strCtor, new Object[]{convertedValue});
} catch (NoSuchMethodException var12) {
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", var12);
}
} catch (Exception var13) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", var13);
}
}
}
String trimmedValue = ((String)convertedValue).trim();
if (requiredType.isEnum() && trim