From 6a374133c774ed9dc0ad00d37c2eb2cf9c498637 Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 4 Sep 2020 20:38:14 +0800 Subject: [PATCH] add EnumItem and enhance converter --- .../core/convert/ConverterRegistry.java | 112 +++++++++--------- .../java/cn/hutool/core/convert/EnumItem.java | 64 ---------- .../core/convert/impl/EnumConverter.java | 64 +++++----- .../java/cn/hutool/core/lang/EnumItem.java | 78 ++++++++++++ .../hutool/core/util/ServiceLoaderUtil.java | 41 +++---- .../java/cn/hutool/core/util/XmlUtil.java | 10 +- 6 files changed, 193 insertions(+), 176 deletions(-) delete mode 100644 hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/EnumItem.java diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java b/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java index a405e36a0..f36174bfb 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/ConverterRegistry.java +++ b/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; *

* 在此类中,存放着默认转换器和自定义转换器,默认转换器是Hutool中预定义的一些转换器,自定义转换器存放用户自定的转换器。 *

- * - * @author Looly * + * @author Looly */ -public class ConverterRegistry implements Serializable{ +public class ConverterRegistry implements Serializable { private static final long serialVersionUID = 1L; - /** 默认类型转换器 */ + /** + * 默认类型转换器 + */ private Map> defaultConverterMap; - /** 用户自定义类型转换器 */ + /** + * 用户自定义类型转换器 + */ private volatile Map> 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 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{ /** * 获得转换器
- * - * @param 转换的目标类型 - * - * @param type 类型 + * + * @param 转换的目标类型 + * @param type 类型 * @param isCustomFirst 是否自定义转换器优先 * @return 转换器 */ @@ -185,8 +191,8 @@ public class ConverterRegistry implements Serializable{ /** * 获得默认转换器 - * - * @param 转换的目标类型(转换器转换到的类型) + * + * @param 转换的目标类型(转换器转换到的类型) * @param type 类型 * @return 转换器 */ @@ -197,9 +203,8 @@ public class ConverterRegistry implements Serializable{ /** * 获得自定义转换器 - * - * @param 转换的目标类型(转换器转换到的类型) - * + * + * @param 转换的目标类型(转换器转换到的类型) * @param type 类型 * @return 转换器 */ @@ -210,11 +215,11 @@ public class ConverterRegistry implements Serializable{ /** * 转换值为指定类型 - * - * @param 转换的目标类型(转换器转换到的类型) - * @param type 类型目标 - * @param value 被转换值 - * @param defaultValue 默认值 + * + * @param 转换的目标类型(转换器转换到的类型) + * @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(type).convert(value, defaultValue); } - + // 无法转换 throw new ConvertException("No Converter for type [{}]", rowType.getName()); } @@ -270,10 +275,10 @@ public class ConverterRegistry implements Serializable{ /** * 转换值为指定类型
* 自定义转换器优先 - * - * @param 转换的目标类型(转换器转换到的类型) - * @param type 类型 - * @param value 值 + * + * @param 转换的目标类型(转换器转换到的类型) + * @param type 类型 + * @param value 值 * @param defaultValue 默认值 * @return 转换后的值 * @throws ConvertException 转换器不存在 @@ -284,9 +289,9 @@ public class ConverterRegistry implements Serializable{ /** * 转换值为指定类型 - * - * @param 转换的目标类型(转换器转换到的类型) - * @param type 类型 + * + * @param 转换的目标类型(转换器转换到的类型) + * @param type 类型 * @param value 值 * @return 转换后的值,默认为null * @throws ConvertException 转换器不存在 @@ -296,20 +301,21 @@ public class ConverterRegistry implements Serializable{ } // ----------------------------------------------------------- Private method start + /** * 特殊类型转换
* 包括: - * + * *
 	 * Collection
 	 * Map
 	 * 强转(无需转换)
 	 * 数组
 	 * 
- * - * @param 转换的目标类型(转换器转换到的类型) - * @param type 类型 - * @param value 值 + * + * @param 转换的目标类型(转换器转换到的类型) + * @param type 类型 + * @param value 值 * @param defaultValue 默认值 * @return 转换后的值 */ @@ -357,7 +363,7 @@ public class ConverterRegistry implements Serializable{ /** * 注册默认转换器 - * + * * @return 转换器 */ private ConverterRegistry defaultConverter() { diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java b/hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java deleted file mode 100644 index f620f6ea8..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/convert/EnumItem.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.hutool.core.convert; - -import java.io.Serializable; - -/** - * - *枚举元素通用接口,在自定义枚举上实现此接口可以用于数据转换
- *数据库保存时建议保存 intVal()而非ordinal()防备需求变更
- * @param - */ -public interface EnumItem> 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; - } - - -} - diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java index 634222ecf..6886736df 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java +++ b/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 { private static final long serialVersionUID = 1L; - private static final Map, Map, Method>> VALUE_OF_METHOD_CACHE = new ConcurrentHashMap<>(); + private static final SimpleCache, Map, Method>> VALUE_OF_METHOD_CACHE = new SimpleCache<>(); private final Class enumClass; @@ -40,7 +40,7 @@ public class EnumConverter extends AbstractConverter { @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 { * 尝试找到类似转换的静态方法调用实现转换且优先使用
* 约定枚举类应该提供 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, Method> valueOfMethods = getValueOfMethods(enumClass); - if (MapUtil.isNotEmpty(valueOfMethods)) { + + // 用户自定义方法 + // 查找枚举中所有返回值为目标枚举对象的方法,如果发现方法参数匹配,就执行之 + final Map, Method> methodMap = getMethodMap(enumClass); + if (MapUtil.isNotEmpty(methodMap)) { final Class valueClass = value.getClass(); - for (Map.Entry, Method> entry : valueOfMethods.entrySet()) { + for (Map.Entry, 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 { //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 { * 获取用于转换为enum的所有static方法 * * @param enumClass 枚举类 - * @return 转换方法map + * @return 转换方法map,key为方法参数类型,value为方法 */ - private static Map, Method> getValueOfMethods(Class enumClass) { - Map, 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, 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))); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/EnumItem.java b/hutool-core/src/main/java/cn/hutool/core/lang/EnumItem.java new file mode 100644 index 000000000..3f90cd19b --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/EnumItem.java @@ -0,0 +1,78 @@ +package cn.hutool.core.lang; + +import java.io.Serializable; + +/** + * 枚举元素通用接口,在自定义枚举上实现此接口可以用于数据转换
+ * 数据库保存时建议保存 intVal()而非ordinal()防备需求变更
+ * + * @param Enum类型 + * @author nierjia + * @since 5.4.2 + */ +public interface EnumItem> 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; + } + + +} + diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java index 4fd3360bb..113703285 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ServiceLoaderUtil.java +++ b/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 loadFirstAvailable(Class clazz) { final Iterator 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 loadFirst(Class clazz) { final Iterator 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 ServiceLoader load(Class clazz) { - return load(clazz,null); + return load(clazz, null); } /** @@ -75,37 +76,31 @@ public class ServiceLoaderUtil { * @return 服务接口实现列表 */ public static ServiceLoader load(Class clazz, ClassLoader loader) { - if(loader==null) { - loader = Thread.currentThread().getContextClassLoader(); - } return ServiceLoader.load(clazz, loader); } + /** * 加载服务 并已list列表返回 - * @param 接口类型 + * + * @param 接口类型 * @param clazz 服务接口 * @return 服务接口实现列表 + * @since 5.4.2 */ - public static List loadList(Class clazz){ - return loadList(clazz); + public static List loadList(Class clazz) { + return loadList(clazz, null); } + /** * 加载服务 并已list列表返回 - * @param 接口类型 - * @param clazz 服务接口 + * + * @param 接口类型 + * @param clazz 服务接口 * @param loader {@link ClassLoader} * @return 服务接口实现列表 + * @since 5.4.2 */ - public static List loadList(Class clazz, ClassLoader loader){ - final Iterator iterator = load(clazz).iterator(); - List list=new ArrayList<>(); - while(iterator.hasNext()){ - try { - list.add(iterator.next()); - } catch (ServiceConfigurationError e) { - // ignore - } - } - return list; + public static List loadList(Class clazz, ClassLoader loader) { + return ListUtil.list(false, load(clazz)); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java index 4369ebe94..4b9d80a93 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java @@ -836,7 +836,7 @@ public class XmlUtil { */ public static T xmlToBean(Node node, Class bean) { final Map 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