6.盘点springmvc的常用接口之Converter(中篇)

6.盘点springmvc的常用接口之Converter(中篇)###

上一章简单介绍了Converter接口的使用,Converter接口是用于明确原类型和目标类型之间的转换。

那么怎么才能从原类型转换到某一类的目标类型呢?比如字符串转枚举类型,我有PersonType和PersonStatus两个枚举类型,那么就得有两个转换器PersonTypeConverterPersonStatusConverter,枚举类型再多点,Converter也跟着多。所以Spring提供了一个工厂接口org.springframework.core.convert.converter.ConverterFactory

接口说明
public interface ConverterFactory<S, R> {

	<T extends R> Converter<S, T> getConverter(Class<T> targetType);

}

其中泛型S是source原类型,R是目标类型, T是R的子类型。

和一般的工厂模式一样,这就是提供给我们一个根据子类型返回相应转换器的工具。

如果按Converter的思路做,代码应该是这样子:

public class StringToPersonStatusConverter implements Converter<String, PersonStatus> {  
  
   @Override  
   public PersonStatus convert(String source) {  
       if (Objects.isNull(source)) {  
          return null;  
       }  
       return PersonStatus.valueOf(source);  
   }  
    
}  
public class StringToPersonTypeConverter implements Converter<String, PersonType> {  
  
   @Override  
   public PersonType convert(String source) {  
       if (Objects.isNull(source)) {  
          return null;  
       }  
       return PersonType.valueOf(source);  
   }  
    
}  

每个枚举都要写转换器也太麻烦了,我们可以使用ConverterFactory抽象出转换过程。


package org.springframework.core.convert.support;

import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;

@SuppressWarnings({"unchecked", "rawtypes"})
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

	@Override
	public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
		Class<?> enumType = targetType;
		while (enumType != null && !enumType.isEnum()) {
			enumType = enumType.getSuperclass();
		}
		if (enumType == null) {
			throw new IllegalArgumentException(
					"The target type " + targetType.getName() + " does not refer to an enum");
		}
		return new StringToEnum(enumType);
	}


	private class StringToEnum<T extends Enum> implements Converter<String, T> {

		private final Class<T> enumType;

		public StringToEnum(Class<T> enumType) {
			this.enumType = enumType;
		}

		@Override
		public T convert(String source) {
			if (source.length() == 0) {
				// It's an empty enum identifier: reset the enum value to null.
				return null;
			}
			return (T) Enum.valueOf(this.enumType, source.trim());
		}
	}
}

这就是Spring内置的StringToEnumConverterFactory源码。

下面这个org.springframework.core.convert.converter.GenericConverter接口是所有的Converter接口中最灵活也是最复杂的一个类型转换接口。之前介绍的Converter接口只支持从一个原类型转换为一个目标类型;ConverterFactory接口只支持从一个原类型转换为一个目标类型对应的子类型;而GenericConverter接口支持在多个不同的原类型和目标类型之间进行转换。

接口说明
public interface GenericConverter {
	//返回这个GenericConverter能够转换的原类型和目标类型的组合
	Set<ConvertiblePair> getConvertibleTypes();
	//用于进行类型转换
	Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

其中ConvertiblePair是一对原类型和目标类型的封装。

假设我们有个需求是用person的id或fullname转换成对应的Person对象,可以通过下面的

GenericConverter 实现。

package com.demo.mvc.component;

import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;

import com.demo.domain.Person;
import com.demo.service.PersonService;

public class PersonGenericConverter implements GenericConverter {

	@Autowired
	private PersonService personService;

	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		// 构建原类型和目标类型对
		Set<ConvertiblePair> pairs = new HashSet<ConvertiblePair>();
		pairs.add(new ConvertiblePair(Integer.class, Person.class));
		pairs.add(new ConvertiblePair(String.class, Person.class));
		return pairs;
	}

	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		if (source == null) {
			return null;
		}
		Person person = null;
		if (sourceType.getType() == Integer.class) {
			// 根据id查询person
			person = personService.findPersonById((Integer) source);
		} else if (sourceType.getType() == String.class) {
			// 根据fullname查询person
			person = personService.findPersonByFullname((String) source);
		}
		return person;
	}
}

getConvertibleTypes方法中添加了两组转换的组合,Integer到Person和String到Person。然后我们给PersonGenericConverter注入了一个PersonService,在convert方法根据参数的类型来决定查询的方法。

GenericConverter还有一个子类接口org.springframework.core.convert.converter.ConditionalGenericConverter 它另外继承了ConditionalConverter

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {

}
public interface ConditionalConverter {

	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);

}

顾名思义就是多了matches方法用于条件判断,使得转换器不仅在满足类型匹配时可以转换,还要满足此条件。看一下Spring内置的StringToArrayConverter就好理解了,它实现了ConditionalGenericConverter。在字符串转换到数组的过程中,不仅要原类型为String,目标类型是数组,还有目标类型数组的元素类型也要匹配。

final class StringToArrayConverter implements ConditionalGenericConverter {

	private final ConversionService conversionService;

	public StringToArrayConverter(ConversionService conversionService) {
		this.conversionService = conversionService;
	}

	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		return Collections.singleton(new ConvertiblePair(String.class, Object[].class));
	}

	@Override
	public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
		//此处还需判断数组的元素类型是否匹配	
      return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
	}

	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		if (source == null) {
			return null;
		}
		String string = (String) source;
		String[] fields = StringUtils.commaDelimitedListToStringArray(string);
		Object target = Array.newInstance(targetType.getElementTypeDescriptor().getType(), fields.length);
		for (int i = 0; i < fields.length; i++) {
			String sourceElement = fields[i];
			Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor());
			Array.set(target, i, targetElement);
		}
		return target;
	}
}

友情链接

盘点springmvc的常用接口目录

转载于:https://my.oschina.net/sugarZone/blog/706413

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值