This commit is contained in:
Looly 2024-08-23 19:42:34 +08:00
parent df180b2e12
commit 3d0e42f653
6 changed files with 97 additions and 146 deletions

View File

@ -27,13 +27,12 @@ import java.lang.reflect.Type;
import java.util.Optional;
/**
* 复合转换器融合了所有支持类型和自定义类型的转换规则
* <p>
* 将各种类型Convert对象放入符合转换器通过convert方法查找目标类型对应的转换器将被转换对象转换之
* </p>
* <p>
* 在此类中存放着默认转换器和自定义转换器默认转换器是Hutool中预定义的一些转换器自定义转换器存放用户自定的转换器
* </p>
* 复合转换器融合了所有支持类型和自定义类型的转换规则<br>
* 在此类中存放着默认转换器和自定义转换器默认转换器是Hutool中预定义的一些转换器自定义转换器存放用户自定的转换器<br>
* 转换过程类似于转换链过程如下
* <pre>{@code
* 处理nullOptOptional --> 自定义匹配转换器 --> 自定义类型转换器 --> 预注册的标准转换器 --> Map集合Enum等特殊转换器 --> Bean转换器
* }</pre>
*
* @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);
}

View File

@ -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;
/**
* 用户自定义转换器<br>
* 通过自定义实现{@link MatcherConverter}可以通过{@link MatcherConverter#match(Type, Class, Object)} 检查目标类型是否匹配<br>
* 如果匹配则直接转换否则使用默认转换器转换
*
* @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<MatcherConverter> 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);
}
}

View File

@ -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;
/**
* 基于类型注册的转换器<br>
* 即转换的目标类型和转换器一一对应转换器只针对目标类型不针对源类型<br>
* 转换器默认提供一些固定的类型转换用户可调用{@link #putCustom(Type, Converter)} 注册自定义转换规则<br>
* 注意注册的转换器要求目标类型必须一致不能是子类等
* 基于类型注册的转换器提供两种注册方式按照优先级依次为
* <ol>
* <li>按照匹配注册使用{@link #register(MatcherConverter)}
* 注册后一旦给定的目标类型和值满足{@link MatcherConverter#match(Type, Class, Object)}即可调用对应转换器转换</li>
* <li>按照类型注册使用{@link #register(Type, Converter)}目标类型一致即可调用转换</li>
* </ol>
*
* @author looly
* @since 6.0.0
@ -72,14 +76,19 @@ public class RegisterConverter implements Converter, Serializable {
return RegisterConverter.SingletonHolder.INSTANCE;
}
/**
* 用户自定义类型转换器存储自定义匹配规则的一类对象的转换器
*/
private volatile Set<MatcherConverter> converterSet;
/**
* 用户自定义精确类型转换器<br>
* 主要存储类型明确无子类的转换器
*/
private volatile Map<Type, Converter> customConverterMap;
/**
* 默认类型转换器
*/
private Map<Class<?>, Converter> defaultConverterMap;
/**
* 用户自定义类型转换器
*/
private volatile Map<Type, Converter> 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 {
* 获得转换器<br>
*
* @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;
}
/**
* 注册默认转换器
*/

View File

@ -15,12 +15,18 @@
*/
/**
* 万能类型转换器以及各种类型转换的实现类其中Convert为转换器入口提供各种toXXX方法和convert方法
* 万能类型转换器以及各种类型转换的实现类其中Convert为转换器入口提供各种toXXX方法和convert方法<br>
* 转换器是典型的策略模式应用可自定义转换策略Hutool提供了常用类型的转换策略自定义转换接口包括
* <ul>
* <li>{@link org.dromara.hutool.core.convert.Converter}标准转换接口通过类型匹配策略后调用使用</li>
* <li>{@link org.dromara.hutool.core.convert.MatcherConverter}带有match方法的Converter通过自身匹配判断调用转换</li>
* </ul>
*
* <p>
* 转换器是典型的策略模式应用通过实现{@link org.dromara.hutool.core.convert.Converter} 接口
* 自定义转换策略Hutool提供了常用类型的转换策略
* </p>
* 公共的转换器封装有两种
* <ul>
* <li>{@link org.dromara.hutool.core.convert.RegisterConverter}提供预定义的转换规则也可以注册自定义转换规则</li>
* <li>{@link org.dromara.hutool.core.convert.CompositeConverter}复合转换器封装基于注册的特别转换泛型转换等规则实现万能转换</li>
* </ul>
*
* @author looly
*

View File

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

View File

@ -45,7 +45,7 @@ import java.util.Optional;
* JSON转换器实现Object对象转换为{@link JSON}支持的对象
* <ul>
* <li>任意支持的对象转换为JSON</li>
* <li>JSOn转换为指定对象Bean</li>
* <li>JSON转换为指定对象Bean</li>
* </ul>
*
* @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 {
// RFC8259JSON字符串值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流程为
* <pre>{@code
* 自定义反序列化 --> 尝试转Kotlin --> 基于注册的标准转换器 --> CollectionMap等含有泛型的特殊转换器 --> 普通Bean转换器
* }</pre>
*
* @param <T> 目标类型
* @param targetType 目标类型
@ -236,18 +239,18 @@ public class JSONConverter implements Converter, Serializable {
return KClassUtil.newInstance(rawType, new JSONGetterValueProvider<>((JSONGetter<String>) json));
}
// 标准转换器
final Converter converter = RegisterConverter.getInstance().getConverter(targetType, json, true);
if (null != converter) {
return (T) converter.convert(targetType, json);
}
// 特殊类型转换包括CollectionMap强转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,