add EnumItem and enhance converter

This commit is contained in:
Looly 2020-09-04 20:38:14 +08:00
parent 945a631f2b
commit 6a374133c7
6 changed files with 193 additions and 176 deletions

View File

@ -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;
@ -83,19 +82,26 @@ import java.util.concurrent.atomic.AtomicReference;
* </p>
*
* @author Looly
*
*/
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();
}
@ -111,17 +117,18 @@ 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));
if(null != type){
putCustom(type, converter);
}
} catch (Exception e) {
// 忽略注册失败的
}
@ -162,7 +169,6 @@ public class ConverterRegistry implements Serializable{
* 获得转换器<br>
*
* @param <T> 转换的目标类型
*
* @param type 类型
* @param isCustomFirst 是否自定义转换器优先
* @return 转换器
@ -199,7 +205,6 @@ public class ConverterRegistry implements Serializable{
* 获得自定义转换器
*
* @param <T> 转换的目标类型转换器转换到的类型
*
* @param type 类型
* @return 转换器
*/
@ -296,6 +301,7 @@ public class ConverterRegistry implements Serializable{
}
// ----------------------------------------------------------- Private method start
/**
* 特殊类型转换<br>
* 包括

View File

@ -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;
}
}

View File

@ -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;
@ -65,25 +65,32 @@ public class EnumConverter extends AbstractConverter<Object> {
if (value == null) {
return null;
}
// EnumItem实现转换
Enum enumResult = null;
if (EnumItem.class.isAssignableFrom(enumClass)) {
EnumItem first = (EnumItem) EnumUtil.getEnumAt(enumClass, 0);
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 - 男性
@ -109,19 +116,14 @@ public class EnumConverter extends AbstractConverter<Object> {
* 获取用于转换为enum的所有static方法
*
* @param enumClass 枚举类
* @return 转换方法map
* @return 转换方法mapkey为方法参数类型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())
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, (existing, replacement) -> existing));
VALUE_OF_METHOD_CACHE.put(enumClass, valueOfMethods);
}
return valueOfMethods;
.collect(Collectors.toMap(m -> m.getParameterTypes()[0], m -> m)));
}
}

View File

@ -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;
}
}

View File

@ -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;
@ -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 clazz 服务接口
* @return 服务接口实现列表
* @since 5.4.2
*/
public static <T> List<T> loadList(Class<T> clazz) {
return loadList(clazz);
return loadList(clazz, null);
}
/**
* 加载服务 并已list列表返回
*
* @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;
return ListUtil.list(false, load(clazz));
}
}