Browse Source

add EnumItem and enhance converter

Looly 5 years ago
parent
commit
6a374133c7

+ 59 - 53
hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java

@@ -61,7 +61,6 @@ import java.time.temporal.TemporalAccessor;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Currency;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
@@ -81,27 +80,34 @@ import java.util.concurrent.atomic.AtomicReference;
  * <p>
  * 在此类中,存放着默认转换器和自定义转换器,默认转换器是Hutool中预定义的一些转换器,自定义转换器存放用户自定的转换器。
  * </p>
- * 
- * @author Looly
  *
+ * @author Looly
  */
-public class ConverterRegistry implements Serializable{
+public class ConverterRegistry implements Serializable {
 	private static final long serialVersionUID = 1L;
 
-	/** 默认类型转换器 */
+	/**
+	 * 默认类型转换器
+	 */
 	private Map<Type, Converter<?>> defaultConverterMap;
-	/** 用户自定义类型转换器 */
+	/**
+	 * 用户自定义类型转换器
+	 */
 	private volatile Map<Type, Converter<?>> customConverterMap;
 
-	/** 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载 */
+	/**
+	 * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
+	 */
 	private static class SingletonHolder {
-		/** 静态初始化器,由JVM来保证线程安全 */
+		/**
+		 * 静态初始化器,由JVM来保证线程安全
+		 */
 		private static final ConverterRegistry INSTANCE = new ConverterRegistry();
 	}
 
 	/**
 	 * 获得单例的 {@link ConverterRegistry}
-	 * 
+	 *
 	 * @return {@link ConverterRegistry}
 	 */
 	public static ConverterRegistry getInstance() {
@@ -111,27 +117,28 @@ public class ConverterRegistry implements Serializable{
 	public ConverterRegistry() {
 		defaultConverter();
 		putCustomBySpi();
-		
-		
 	}
 
-	@SuppressWarnings("rawtypes")
+	/**
+	 * 使用SPI加载转换器
+	 */
 	private void putCustomBySpi() {
-		List<Converter> list = ServiceLoaderUtil.loadList(Converter.class);
-		list.forEach(converter->{
+		ServiceLoaderUtil.load(Converter.class).forEach(converter -> {
 			try {
 				Type type = TypeUtil.getTypeArgument(ClassUtil.getClass(converter));
-				putCustom(type, converter);
+				if(null != type){
+					putCustom(type, converter);
+				}
 			} catch (Exception e) {
-				 // 忽略注册失败的
+				// 忽略注册失败的
 			}
 		});
 	}
 
 	/**
 	 * 登记自定义转换器
-	 * 
-	 * @param type 转换的目标类型
+	 *
+	 * @param type           转换的目标类型
 	 * @param converterClass 转换器类,必须有默认构造方法
 	 * @return {@link ConverterRegistry}
 	 */
@@ -141,8 +148,8 @@ public class ConverterRegistry implements Serializable{
 
 	/**
 	 * 登记自定义转换器
-	 * 
-	 * @param type 转换的目标类型
+	 *
+	 * @param type      转换的目标类型
 	 * @param converter 转换器
 	 * @return {@link ConverterRegistry}
 	 */
@@ -160,10 +167,9 @@ public class ConverterRegistry implements Serializable{
 
 	/**
 	 * 获得转换器<br>
-	 * 
-	 * @param <T> 转换的目标类型
-	 * 
-	 * @param type 类型
+	 *
+	 * @param <T>           转换的目标类型
+	 * @param type          类型
 	 * @param isCustomFirst 是否自定义转换器优先
 	 * @return 转换器
 	 */
@@ -185,8 +191,8 @@ public class ConverterRegistry implements Serializable{
 
 	/**
 	 * 获得默认转换器
-	 * 
-	 * @param <T> 转换的目标类型(转换器转换到的类型)
+	 *
+	 * @param <T>  转换的目标类型(转换器转换到的类型)
 	 * @param type 类型
 	 * @return 转换器
 	 */
@@ -197,9 +203,8 @@ public class ConverterRegistry implements Serializable{
 
 	/**
 	 * 获得自定义转换器
-	 * 
-	 * @param <T> 转换的目标类型(转换器转换到的类型)
-	 * 
+	 *
+	 * @param <T>  转换的目标类型(转换器转换到的类型)
 	 * @param type 类型
 	 * @return 转换器
 	 */
@@ -210,11 +215,11 @@ public class ConverterRegistry implements Serializable{
 
 	/**
 	 * 转换值为指定类型
-	 * 
-	 * @param <T> 转换的目标类型(转换器转换到的类型)
-	 * @param type 类型目标
-	 * @param value 被转换值
-	 * @param defaultValue 默认值
+	 *
+	 * @param <T>           转换的目标类型(转换器转换到的类型)
+	 * @param type          类型目标
+	 * @param value         被转换值
+	 * @param defaultValue  默认值
 	 * @param isCustomFirst 是否自定义转换器优先
 	 * @return 转换后的值
 	 * @throws ConvertException 转换器不存在
@@ -231,9 +236,9 @@ public class ConverterRegistry implements Serializable{
 		if (TypeUtil.isUnknow(type)) {
 			type = defaultValue.getClass();
 		}
-		
-		if(type instanceof TypeReference) {
-			type = ((TypeReference<?>)type).getType();
+
+		if (type instanceof TypeReference) {
+			type = ((TypeReference<?>) type).getType();
 		}
 
 		// 标准转换器
@@ -251,18 +256,18 @@ public class ConverterRegistry implements Serializable{
 				return (T) value;
 			}
 		}
-		
+
 		// 特殊类型转换,包括Collection、Map、强转、Array等
 		final T result = convertSpecial(type, rowType, value, defaultValue);
 		if (null != result) {
 			return result;
 		}
-		
+
 		// 尝试转Bean
 		if (BeanUtil.isBean(rowType)) {
 			return new BeanConverter<T>(type).convert(value, defaultValue);
 		}
-		
+
 		// 无法转换
 		throw new ConvertException("No Converter for type [{}]", rowType.getName());
 	}
@@ -270,10 +275,10 @@ public class ConverterRegistry implements Serializable{
 	/**
 	 * 转换值为指定类型<br>
 	 * 自定义转换器优先
-	 * 
-	 * @param <T> 转换的目标类型(转换器转换到的类型)
-	 * @param type 类型
-	 * @param value 值
+	 *
+	 * @param <T>          转换的目标类型(转换器转换到的类型)
+	 * @param type         类型
+	 * @param value        
 	 * @param defaultValue 默认值
 	 * @return 转换后的值
 	 * @throws ConvertException 转换器不存在
@@ -284,9 +289,9 @@ public class ConverterRegistry implements Serializable{
 
 	/**
 	 * 转换值为指定类型
-	 * 
-	 * @param <T> 转换的目标类型(转换器转换到的类型)
-	 * @param type 类型
+	 *
+	 * @param <T>   转换的目标类型(转换器转换到的类型)
+	 * @param type  类型
 	 * @param value 值
 	 * @return 转换后的值,默认为<code>null</code>
 	 * @throws ConvertException 转换器不存在
@@ -296,20 +301,21 @@ public class ConverterRegistry implements Serializable{
 	}
 
 	// ----------------------------------------------------------- Private method start
+
 	/**
 	 * 特殊类型转换<br>
 	 * 包括:
-	 * 
+	 *
 	 * <pre>
 	 * Collection
 	 * Map
 	 * 强转(无需转换)
 	 * 数组
 	 * </pre>
-	 * 
-	 * @param <T> 转换的目标类型(转换器转换到的类型)
-	 * @param type 类型
-	 * @param value 值
+	 *
+	 * @param <T>          转换的目标类型(转换器转换到的类型)
+	 * @param type         类型
+	 * @param value        
 	 * @param defaultValue 默认值
 	 * @return 转换后的值
 	 */
@@ -357,7 +363,7 @@ public class ConverterRegistry implements Serializable{
 
 	/**
 	 * 注册默认转换器
-	 * 
+	 *
 	 * @return 转换器
 	 */
 	private ConverterRegistry defaultConverter() {

+ 0 - 64
hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java

@@ -1,64 +0,0 @@
-package cn.hutool.core.convert;
-
-import java.io.Serializable;
-
-/**
- * 
- *枚举元素通用接口,在自定义枚举上实现此接口可以用于数据转换<br>
- *数据库保存时建议保存 intVal()而非ordinal()防备需求变更<br>
- * @param <E>
- */
-public interface EnumItem<E extends EnumItem<E>> extends Serializable{
-	
-	String name();
-	/**
-	 * 在中文语境下,多数时间枚举会配合一个中文说明
-	 */
-	default String text() {
-		return name();
-	}
-	
-	int intVal();
-	
-	@SuppressWarnings("unchecked")
-	default E[] items() {
-		return (E[]) this.getClass().getEnumConstants();
-	}
-	/**
-	 * 通过int类型值查找兄弟其他枚举
-	 * @param intVal
-	 * @return
-	 */
-	default E fromInt(Integer intVal) {
-		if(intVal==null) {
-			return null;
-		}
-		E[] vs = items();
-		for (E enumItem : vs) {
-			if(enumItem.intVal()==intVal.intValue()) {
-				return enumItem;
-			}
-		}
-		return null;
-	}
-	/**
-	 * 通过String类型的值转换,根据实现可以用name/text
-	 * @param intVal
-	 * @return
-	 */
-	default E fromStr(String strVal) {
-		 if(strVal==null) {
-			 return null;
-		 }
-		 E[] vs = items();
-		 for (E enumItem : vs) {
-			 if(strVal.equalsIgnoreCase(enumItem.name())) {
-				 return enumItem;
-			 }
-		 }
-		 return null;
-	 }
-
-	
-}
-

+ 33 - 31
hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java

@@ -1,7 +1,8 @@
 package cn.hutool.core.convert.impl;
 
 import cn.hutool.core.convert.AbstractConverter;
-import cn.hutool.core.convert.EnumItem;
+import cn.hutool.core.lang.EnumItem;
+import cn.hutool.core.lang.SimpleCache;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ClassUtil;
 import cn.hutool.core.util.EnumUtil;
@@ -11,7 +12,6 @@ import cn.hutool.core.util.ReflectUtil;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
 /**
@@ -24,7 +24,7 @@ import java.util.stream.Collectors;
 public class EnumConverter extends AbstractConverter<Object> {
 	private static final long serialVersionUID = 1L;
 
-	private static final Map<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new ConcurrentHashMap<>();
+	private static final SimpleCache<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new SimpleCache<>();
 
 	private final Class enumClass;
 
@@ -40,7 +40,7 @@ public class EnumConverter extends AbstractConverter<Object> {
 	@Override
 	protected Object convertInternal(Object value) {
 		Enum enumValue = tryConvertEnum(value, this.enumClass);
-		if(null == enumValue && false == value instanceof String){
+		if (null == enumValue && false == value instanceof String) {
 			// 最后尝试valueOf转换
 			enumValue = Enum.valueOf(this.enumClass, convertToStr(value));
 		}
@@ -56,34 +56,41 @@ public class EnumConverter extends AbstractConverter<Object> {
 	 * 尝试找到类似转换的静态方法调用实现转换且优先使用<br>
 	 * 约定枚举类应该提供 valueOf(String) 和 valueOf(Integer)用于转换
 	 * oriInt /name 转换托底
-	 * 
+	 *
 	 * @param value     被转换的值
 	 * @param enumClass enum类
 	 * @return 对应的枚举值
 	 */
 	protected static Enum tryConvertEnum(Object value, Class enumClass) {
-		if(value==null) {
+		if (value == null) {
 			return null;
 		}
+
+		// EnumItem实现转换
 		Enum enumResult = null;
-		if(EnumItem.class.isAssignableFrom(enumClass)) {
-			EnumItem first = (EnumItem) EnumUtil.getEnumAt(enumClass, 0);
-			if(value instanceof Integer) {
-				return (Enum) first.fromInt((Integer) value);
-			}else if(value instanceof String){
-				return (Enum) first.fromStr( value.toString());
+		if (EnumItem.class.isAssignableFrom(enumClass)) {
+			final EnumItem first = (EnumItem) EnumUtil.getEnumAt(enumClass, 0);
+			if(null != first){
+				if (value instanceof Integer) {
+					return (Enum) first.fromInt((Integer) value);
+				} else if (value instanceof String) {
+					return (Enum) first.fromStr(value.toString());
+				}
 			}
 		}
-		// 用户自定义方法优先,
-		final Map<Class<?>, Method> valueOfMethods = getValueOfMethods(enumClass);
-		if (MapUtil.isNotEmpty(valueOfMethods)) {
+
+		// 用户自定义方法
+		// 查找枚举中所有返回值为目标枚举对象的方法,如果发现方法参数匹配,就执行之
+		final Map<Class<?>, Method> methodMap = getMethodMap(enumClass);
+		if (MapUtil.isNotEmpty(methodMap)) {
 			final Class<?> valueClass = value.getClass();
-			for (Map.Entry<Class<?>, Method> entry : valueOfMethods.entrySet()) {
+			for (Map.Entry<Class<?>, Method> entry : methodMap.entrySet()) {
 				if (ClassUtil.isAssignable(entry.getKey(), valueClass)) {
 					enumResult = ReflectUtil.invokeStatic(entry.getValue(), value);
 				}
 			}
 		}
+
 		//oriInt 应该滞后使用 以 GB/T 2261.1-2003 性别编码为例,对应整数并非连续数字会导致数字转枚举时失败
 		//0 - 未知的性别
 		//1 - 男性
@@ -91,9 +98,9 @@ public class EnumConverter extends AbstractConverter<Object> {
 		//5 - 女性改(变)为男性
 		//6 - 男性改(变)为女性
 		//9 - 未说明的性别
-		if(null == enumResult){		
+		if (null == enumResult) {
 			if (value instanceof Integer) {
-				enumResult = EnumUtil.getEnumAt(enumClass, (Integer)value);
+				enumResult = EnumUtil.getEnumAt(enumClass, (Integer) value);
 			} else if (value instanceof String) {
 				try {
 					enumResult = Enum.valueOf(enumClass, (String) value);
@@ -109,19 +116,14 @@ public class EnumConverter extends AbstractConverter<Object> {
 	 * 获取用于转换为enum的所有static方法
 	 *
 	 * @param enumClass 枚举类
-	 * @return 转换方法map
+	 * @return 转换方法map,key为方法参数类型,value为方法
 	 */
-	private static Map<Class<?>, Method> getValueOfMethods(Class<?> enumClass) {
-		Map<Class<?>, Method> valueOfMethods = VALUE_OF_METHOD_CACHE.get(enumClass);
-		if (null == valueOfMethods) {
-			valueOfMethods = Arrays.stream(enumClass.getMethods())
-					.filter(ModifierUtil::isStatic)
-					.filter(m -> m.getReturnType() == enumClass)
-					.filter(m -> m.getParameterCount() == 1)
-					.filter(m -> false == "valueOf".equals(m.getName()))
-					.collect(Collectors.toMap(m -> m.getParameterTypes()[0], m -> m, (existing, replacement) -> existing));
-			VALUE_OF_METHOD_CACHE.put(enumClass, valueOfMethods);
-		}
-		return valueOfMethods;
+	private static Map<Class<?>, Method> getMethodMap(Class<?> enumClass) {
+		return VALUE_OF_METHOD_CACHE.get(enumClass, ()-> Arrays.stream(enumClass.getMethods())
+				.filter(ModifierUtil::isStatic)
+				.filter(m -> m.getReturnType() == enumClass)
+				.filter(m -> m.getParameterCount() == 1)
+				.filter(m -> false == "valueOf".equals(m.getName()))
+				.collect(Collectors.toMap(m -> m.getParameterTypes()[0], m -> m)));
 	}
 }

+ 78 - 0
hutool-core/src/main/java/cn/hutool/core/lang/EnumItem.java

@@ -0,0 +1,78 @@
+package cn.hutool.core.lang;
+
+import java.io.Serializable;
+
+/**
+ * 枚举元素通用接口,在自定义枚举上实现此接口可以用于数据转换<br>
+ * 数据库保存时建议保存 intVal()而非ordinal()防备需求变更<br>
+ *
+ * @param <E> Enum类型
+ * @author nierjia
+ * @since 5.4.2
+ */
+public interface EnumItem<E extends EnumItem<E>> extends Serializable {
+
+	String name();
+
+	/**
+	 * 在中文语境下,多数时间枚举会配合一个中文说明
+	 *
+	 * @return enum名
+	 */
+	default String text() {
+		return name();
+	}
+
+	int intVal();
+
+	/**
+	 * 获取所有枚举对象
+	 *
+	 * @return 枚举对象数组
+	 */
+	@SuppressWarnings("unchecked")
+	default E[] items() {
+		return (E[]) this.getClass().getEnumConstants();
+	}
+
+	/**
+	 * 通过int类型值查找兄弟其他枚举
+	 *
+	 * @param intVal int值
+	 * @return Enum
+	 */
+	default E fromInt(Integer intVal) {
+		if (intVal == null) {
+			return null;
+		}
+		E[] vs = items();
+		for (E enumItem : vs) {
+			if (enumItem.intVal() == intVal) {
+				return enumItem;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * 通过String类型的值转换,根据实现可以用name/text
+	 *
+	 * @param strVal String值
+	 * @return Enum
+	 */
+	default E fromStr(String strVal) {
+		if (strVal == null) {
+			return null;
+		}
+		E[] vs = items();
+		for (E enumItem : vs) {
+			if (strVal.equalsIgnoreCase(enumItem.name())) {
+				return enumItem;
+			}
+		}
+		return null;
+	}
+
+
+}
+

+ 18 - 23
hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java

@@ -1,6 +1,7 @@
 package cn.hutool.core.util;
 
-import java.util.ArrayList;
+import cn.hutool.core.collection.ListUtil;
+
 import java.util.Iterator;
 import java.util.List;
 import java.util.ServiceConfigurationError;
@@ -30,7 +31,7 @@ public class ServiceLoaderUtil {
 	 */
 	public static <T> T loadFirstAvailable(Class<T> clazz) {
 		final Iterator<T> iterator = load(clazz).iterator();
-		while(iterator.hasNext()){
+		while (iterator.hasNext()) {
 			try {
 				return iterator.next();
 			} catch (ServiceConfigurationError e) {
@@ -49,7 +50,7 @@ public class ServiceLoaderUtil {
 	 */
 	public static <T> T loadFirst(Class<T> clazz) {
 		final Iterator<T> iterator = load(clazz).iterator();
-		if(iterator.hasNext()){
+		if (iterator.hasNext()) {
 			return iterator.next();
 		}
 		return null;
@@ -63,7 +64,7 @@ public class ServiceLoaderUtil {
 	 * @return 服务接口实现列表
 	 */
 	public static <T> ServiceLoader<T> load(Class<T> clazz) {
-		return load(clazz,null);
+		return load(clazz, null);
 	}
 
 	/**
@@ -75,37 +76,31 @@ public class ServiceLoaderUtil {
 	 * @return 服务接口实现列表
 	 */
 	public static <T> ServiceLoader<T> load(Class<T> clazz, ClassLoader loader) {
-		if(loader==null) {
-			loader = Thread.currentThread().getContextClassLoader();
-		}
 		return ServiceLoader.load(clazz, loader);
 	}
+
 	/**
 	 * 加载服务 并已list列表返回
-	 * @param <T> 接口类型
+	 *
+	 * @param <T>   接口类型
 	 * @param clazz 服务接口
 	 * @return 服务接口实现列表
+	 * @since 5.4.2
 	 */
-	public static <T> List<T> loadList(Class<T> clazz){
-		return loadList(clazz);
+	public static <T> List<T> loadList(Class<T> clazz) {
+		return loadList(clazz, null);
 	}
+
 	/**
 	 * 加载服务 并已list列表返回
-	 * @param <T> 接口类型
-	 * @param clazz 服务接口
+	 *
+	 * @param <T>    接口类型
+	 * @param clazz  服务接口
 	 * @param loader {@link ClassLoader}
 	 * @return 服务接口实现列表
+	 * @since 5.4.2
 	 */
-	public static <T> List<T> loadList(Class<T> clazz, ClassLoader loader){
-		final Iterator<T> iterator = load(clazz).iterator();
-		List<T> list=new ArrayList<>();
-		while(iterator.hasNext()){
-			try {
-				  list.add(iterator.next());
-			} catch (ServiceConfigurationError e) {
-				// ignore
-			}
-		}
-		return list;
+	public static <T> List<T> loadList(Class<T> clazz, ClassLoader loader) {
+		return ListUtil.list(false, load(clazz));
 	}
 }

+ 5 - 5
hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java

@@ -836,7 +836,7 @@ public class XmlUtil {
 	 */
 	public static <T> T xmlToBean(Node node, Class<T> bean) {
 		final Map<String, Object> map = xmlToMap(node);
-		if(null != map && map.size() == 1){
+		if (null != map && map.size() == 1) {
 			return BeanUtil.toBean(map.get(bean.getSimpleName()), bean);
 		}
 		return BeanUtil.toBean(map, bean);
@@ -1049,7 +1049,7 @@ public class XmlUtil {
 	/**
 	 * 将Bean转换为XML
 	 *
-	 * @param bean      Bean对象
+	 * @param bean Bean对象
 	 * @return XML
 	 * @since 5.3.4
 	 */
@@ -1269,7 +1269,7 @@ public class XmlUtil {
 		 */
 		private void examineNode(Node node, boolean attributesOnly) {
 			final NamedNodeMap attributes = node.getAttributes();
-			if(null != attributes){
+			if (null != attributes) {
 				for (int i = 0; i < attributes.getLength(); i++) {
 					Node attribute = attributes.item(i);
 					storeAttribute(attribute);
@@ -1278,7 +1278,7 @@ public class XmlUtil {
 
 			if (false == attributesOnly) {
 				final NodeList childNodes = node.getChildNodes();
-				if(null != childNodes){
+				if (null != childNodes) {
 					Node item;
 					for (int i = 0; i < childNodes.getLength(); i++) {
 						item = childNodes.item(i);
@@ -1296,7 +1296,7 @@ public class XmlUtil {
 		 * @param attribute to examine
 		 */
 		private void storeAttribute(Node attribute) {
-			if(null == attribute){
+			if (null == attribute) {
 				return;
 			}
 			// examine the attributes in namespace xmlns