diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java
index 4fb56579c..f37a18ab0 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CompositeConverter.java
@@ -27,13 +27,12 @@ import java.lang.reflect.Type;
import java.util.Optional;
/**
- * 复合转换器,融合了所有支持类型和自定义类型的转换规则
- *
- * 将各种类型Convert对象放入符合转换器,通过convert方法查找目标类型对应的转换器,将被转换对象转换之。
- *
- *
- * 在此类中,存放着默认转换器和自定义转换器,默认转换器是Hutool中预定义的一些转换器,自定义转换器存放用户自定的转换器。
- *
+ * 复合转换器,融合了所有支持类型和自定义类型的转换规则
+ * 在此类中,存放着默认转换器和自定义转换器,默认转换器是Hutool中预定义的一些转换器,自定义转换器存放用户自定的转换器。
+ * 转换过程类似于转换链,过程如下:
+ * {@code
+ * 处理null、Opt、Optional --> 自定义匹配转换器 --> 自定义类型转换器 --> 预注册的标准转换器 --> Map、集合、Enum等特殊转换器 --> Bean转换器
+ * }
*
* @author Looly
*/
@@ -143,7 +142,7 @@ public class CompositeConverter extends RegisterConverter {
}
// 标准转换器
- final Converter converter = getConverter(type, isCustomFirst);
+ final Converter converter = getConverter(type, value, isCustomFirst);
if (null != converter) {
return converter.convert(type, value, defaultValue);
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CustomConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/CustomConverter.java
deleted file mode 100644
index 7f0bb955d..000000000
--- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/CustomConverter.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2024 Hutool Team and hutool.cn
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.dromara.hutool.core.convert;
-
-import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
-import org.dromara.hutool.core.stream.StreamUtil;
-
-import java.io.Serializable;
-import java.lang.reflect.Type;
-import java.util.Set;
-
-/**
- * 用户自定义转换器
- * 通过自定义实现{@link MatcherConverter},可以通过{@link MatcherConverter#match(Type, Class, Object)} 检查目标类型是否匹配
- * 如果匹配,则直接转换,否则使用默认转换器转换。
- *
- * @author Looly
- * @since 6.0.0
- */
-public class CustomConverter implements Converter, Serializable {
- private static final long serialVersionUID = 1L;
-
- /**
- * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
- */
- private static class SingletonHolder {
- /**
- * 静态初始化器,由JVM来保证线程安全
- */
- private static final CustomConverter INSTANCE = new CustomConverter();
- }
-
- /**
- * 获得单例的 CustomConverter
- *
- * @return CustomConverter
- */
- public static CustomConverter getInstance() {
- return CustomConverter.SingletonHolder.INSTANCE;
- }
-
- /**
- * 用户自定义类型转换器
- */
- private volatile Set converterSet;
-
- @Override
- public Object convert(final Type targetType, final Object value) throws ConvertException {
- final Converter customConverter = getCustomConverter(targetType, value);
- return null == customConverter ? null : customConverter.convert(targetType, value);
- }
-
- /**
- * 获得匹配类型的自定义转换器
- *
- * @param type 类型
- * @param value 被转换的值
- * @return 转换器
- */
- public Converter getCustomConverter(final Type type, final Object value) {
- return getConverterFromSet(this.converterSet, type, value);
- }
-
- /**
- * 登记自定义转换器
- *
- * @param converter 转换器
- * @return ConverterRegistry
- */
- public CustomConverter add(final MatcherConverter converter) {
- if (null == this.converterSet) {
- synchronized (this) {
- if (null == this.converterSet) {
- this.converterSet = new ConcurrentHashSet<>();
- }
- }
- }
- this.converterSet.add(converter);
- return this;
- }
-
- /**
- * 从指定集合中查找满足条件的转换器
- *
- * @param type 类型
- * @return 转换器
- */
- private static Converter getConverterFromSet(final Set extends MatcherConverter> converterSet, final Type type, final Object value) {
- return StreamUtil.of(converterSet).filter((predicate) -> predicate.match(type, value)).findFirst().orElse(null);
- }
-}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java
index 4c934a3bd..96e2d2bcc 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/RegisterConverter.java
@@ -16,6 +16,7 @@
package org.dromara.hutool.core.convert;
+import org.dromara.hutool.core.collection.set.ConcurrentHashSet;
import org.dromara.hutool.core.convert.impl.*;
import org.dromara.hutool.core.lang.Opt;
import org.dromara.hutool.core.lang.tuple.Pair;
@@ -23,6 +24,7 @@ import org.dromara.hutool.core.lang.tuple.Triple;
import org.dromara.hutool.core.lang.tuple.Tuple;
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
import org.dromara.hutool.core.reflect.TypeUtil;
+import org.dromara.hutool.core.stream.StreamUtil;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.Serializable;
@@ -42,10 +44,12 @@ import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;
/**
- * 基于类型注册的转换器
- * 即转换的目标类型和转换器一一对应,转换器只针对目标类型,不针对源类型
- * 转换器默认提供一些固定的类型转换,用户可调用{@link #putCustom(Type, Converter)} 注册自定义转换规则
- * 注意:注册的转换器要求目标类型必须一致,不能是子类等
+ * 基于类型注册的转换器,提供两种注册方式,按照优先级依次为:
+ *
+ * - 按照匹配注册,使用{@link #register(MatcherConverter)}。
+ * 注册后一旦给定的目标类型和值满足{@link MatcherConverter#match(Type, Class, Object)},即可调用对应转换器转换。
+ * - 按照类型注册,使用{@link #register(Type, Converter)},目标类型一致,即可调用转换。
+ *
*
* @author looly
* @since 6.0.0
@@ -72,14 +76,19 @@ public class RegisterConverter implements Converter, Serializable {
return RegisterConverter.SingletonHolder.INSTANCE;
}
+ /**
+ * 用户自定义类型转换器,存储自定义匹配规则的一类对象的转换器
+ */
+ private volatile Set converterSet;
+ /**
+ * 用户自定义精确类型转换器
+ * 主要存储类型明确(无子类)的转换器
+ */
+ private volatile Map customConverterMap;
/**
* 默认类型转换器
*/
private Map, Converter> defaultConverterMap;
- /**
- * 用户自定义类型转换器
- */
- private volatile Map customConverterMap;
/**
* 构造
@@ -91,7 +100,7 @@ public class RegisterConverter implements Converter, Serializable {
@Override
public Object convert(final Type targetType, final Object value) throws ConvertException {
// 标准转换器
- final Converter converter = getConverter(targetType, true);
+ final Converter converter = getConverter(targetType, value, true);
if (null != converter) {
return converter.convert(targetType, value);
}
@@ -104,19 +113,26 @@ public class RegisterConverter implements Converter, Serializable {
* 获得转换器
*
* @param type 类型
+ * @param value 转换的值
* @param isCustomFirst 是否自定义转换器优先
* @return 转换器
*/
- public Converter getConverter(final Type type, final boolean isCustomFirst) {
+ public Converter getConverter(final Type type, final Object value, final boolean isCustomFirst) {
Converter converter;
if (isCustomFirst) {
- converter = this.getCustomConverter(type);
+ converter = this.getCustomConverter(type, value);
+ if(null == converter){
+ converter = this.getCustomConverter(type);
+ }
if (null == converter) {
converter = this.getDefaultConverter(type);
}
} else {
converter = this.getDefaultConverter(type);
if (null == converter) {
+ converter = this.getCustomConverter(type, value);
+ }
+ if(null == converter){
converter = this.getCustomConverter(type);
}
}
@@ -135,7 +151,21 @@ public class RegisterConverter implements Converter, Serializable {
}
/**
- * 获得自定义转换器
+ * 获得匹配类型的自定义转换器
+ *
+ * @param type 类型
+ * @param value 被转换的值
+ * @return 转换器
+ */
+ public Converter getCustomConverter(final Type type, final Object value) {
+ return StreamUtil.of(converterSet)
+ .filter((predicate) -> predicate.match(type, value))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * 获得指定类型对应的自定义转换器
*
* @param type 类型
* @return 转换器
@@ -145,13 +175,13 @@ public class RegisterConverter implements Converter, Serializable {
}
/**
- * 登记自定义转换器
+ * 登记自定义转换器,登记的目标类型必须一致
*
* @param type 转换的目标类型
* @param converter 转换器
* @return ConverterRegistry
*/
- public RegisterConverter putCustom(final Type type, final Converter converter) {
+ public RegisterConverter register(final Type type, final Converter converter) {
if (null == customConverterMap) {
synchronized (this) {
if (null == customConverterMap) {
@@ -163,6 +193,24 @@ public class RegisterConverter implements Converter, Serializable {
return this;
}
+ /**
+ * 登记自定义转换器,符合{@link MatcherConverter#match(Type, Class, Object)}则使用其转换器
+ *
+ * @param converter 转换器
+ * @return ConverterRegistry
+ */
+ public RegisterConverter register(final MatcherConverter converter) {
+ if (null == this.converterSet) {
+ synchronized (this) {
+ if (null == this.converterSet) {
+ this.converterSet = new ConcurrentHashSet<>();
+ }
+ }
+ }
+ this.converterSet.add(converter);
+ return this;
+ }
+
/**
* 注册默认转换器
*/
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/package-info.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/package-info.java
index abd236088..20c778060 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/package-info.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/package-info.java
@@ -15,12 +15,18 @@
*/
/**
- * 万能类型转换器以及各种类型转换的实现类,其中Convert为转换器入口,提供各种toXXX方法和convert方法
+ * 万能类型转换器以及各种类型转换的实现类,其中Convert为转换器入口,提供各种toXXX方法和convert方法
+ * 转换器是典型的策略模式应用,可自定义转换策略。Hutool提供了常用类型的转换策略,自定义转换接口包括:
+ *
+ * - {@link org.dromara.hutool.core.convert.Converter},标准转换接口,通过类型匹配策略后调用使用。
+ * - {@link org.dromara.hutool.core.convert.MatcherConverter},带有match方法的Converter,通过自身匹配判断调用转换。
+ *
*
- *
- * 转换器是典型的策略模式应用,通过实现{@link org.dromara.hutool.core.convert.Converter} 接口,
- * 自定义转换策略。Hutool提供了常用类型的转换策略。
- *
+ * 公共的转换器封装有两种:
+ *
+ * - {@link org.dromara.hutool.core.convert.RegisterConverter},提供预定义的转换规则,也可以注册自定义转换规则。
+ * - {@link org.dromara.hutool.core.convert.CompositeConverter},复合转换器,封装基于注册的、特别转换(泛型转换)等规则,实现万能转换。
+ *
*
* @author looly
*
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java
index 6dd53a737..ff42d0f16 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/convert/CompositeConverterTest.java
@@ -38,7 +38,7 @@ public class CompositeConverterTest {
@Test
public void getConverterTest() {
- final Converter converter = CompositeConverter.getInstance().getConverter(CharSequence.class, false);
+ final Converter converter = CompositeConverter.getInstance().getConverter(CharSequence.class, null, false);
assertNotNull(converter);
}
@@ -52,7 +52,7 @@ public class CompositeConverterTest {
//此处做为示例自定义CharSequence转换,因为Hutool中已经提供CharSequence转换,请尽量不要替换
//替换可能引发关联转换异常(例如覆盖CharSequence转换会影响全局)
- compositeConverter.putCustom(CharSequence.class, new CustomConverter());
+ compositeConverter.register(CharSequence.class, new CustomConverter());
result = (CharSequence) compositeConverter.convert(CharSequence.class, a);
assertEquals("Custom: 454553", result);
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java
index 20a46b4be..7456b2074 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java
@@ -45,7 +45,7 @@ import java.util.Optional;
* JSON转换器,实现Object对象转换为{@link JSON},支持的对象:
*
* - 任意支持的对象,转换为JSON
- * - JSOn转换为指定对象Bean
+ * - JSON转换为指定对象Bean
*
*
* @author looly
@@ -61,8 +61,8 @@ public class JSONConverter implements Converter, Serializable {
static {
final RegisterConverter converter = RegisterConverter.getInstance();
- converter.putCustom(JSONObject.class, INSTANCE);
- converter.putCustom(JSONArray.class, INSTANCE);
+ converter.register(JSONObject.class, INSTANCE);
+ converter.register(JSONArray.class, INSTANCE);
}
/**
@@ -188,7 +188,7 @@ public class JSONConverter implements Converter, Serializable {
// RFC8259,JSON字符串值、number, boolean, or null
final JSONParser jsonParser = JSONParser.of(new JSONTokener(jsonStr), config);
final Object value = jsonParser.nextValue();
- if(jsonParser.getTokener().nextClean() != JSONTokener.EOF){
+ if (jsonParser.getTokener().nextClean() != JSONTokener.EOF) {
// 对于用户提供的未转义字符串导致解析未结束,报错
throw new JSONException("JSON format error: {}", jsonStr);
}
@@ -197,7 +197,7 @@ public class JSONConverter implements Converter, Serializable {
case '\'':
return InternalJSONUtil.quote((CharSequence) value);
default:
- if(ObjUtil.equals(jsonStr, value)){
+ if (ObjUtil.equals(jsonStr, value)) {
// 对于直接的字符串,如abc,按照字符串处理
return InternalJSONUtil.quote((CharSequence) value);
}
@@ -208,7 +208,10 @@ public class JSONConverter implements Converter, Serializable {
// ----------------------------------------------------------- Private method start
/**
- * JSON转Bean
+ * JSON转Bean,流程为:
+ * {@code
+ * 自定义反序列化 --> 尝试转Kotlin --> 基于注册的标准转换器 --> Collection、Map等含有泛型的特殊转换器 --> 普通Bean转换器
+ * }
*
* @param 目标类型
* @param targetType 目标类型,
@@ -236,18 +239,18 @@ public class JSONConverter implements Converter, Serializable {
return KClassUtil.newInstance(rawType, new JSONGetterValueProvider<>((JSONGetter) json));
}
+ // 标准转换器
+ final Converter converter = RegisterConverter.getInstance().getConverter(targetType, json, true);
+ if (null != converter) {
+ return (T) converter.convert(targetType, json);
+ }
+
// 特殊类型转换,包括Collection、Map、强转、Array等
final T result = (T) SpecialConverter.getInstance().convert(targetType, rawType, json);
if (null != result) {
return result;
}
- // 标准转换器
- final Converter converter = RegisterConverter.getInstance().getConverter(targetType, true);
- if (null != converter) {
- return (T) converter.convert(targetType, json);
- }
-
// 尝试转Bean
if (BeanUtil.isWritableBean(rawType)) {
return BeanCopier.of(json,