Json转为JavaBean的原理

Json转为Bean的原理

以下源码来自于hutool工具依赖

一、类型准备

准备俩个类,如下

@Data
class Option<T> {
   String optionName;
   T value;
}

@Data
class Value<T> {
   T value;
}

构建一个泛型

String json = "{'optionName': 'log', 'value': {'value': 'slf4j'}}";

指定类型引用

Option<Value<String>> option = parse.toBean(new TypeReference<Option<Value<String>>>() {});

二、测试结果

@Test
public void test() throws ClassNotFoundException {
    String json = "{'optionName': 'log', 'value': {'value': 'slf4j'}}";
    JSON parse = JSONUtil.parse(json);
    Option<Value<String>> option = parse.toBean(new TypeReference<Option<Value<String>>>() {});
    System.out.println(option);
}

在这里插入图片描述

三、转换过程分析

源码:

default <T> T toBean(TypeReference<T> reference) {
	return toBean(reference.getType());
}

1.类型获取

TypeReferencegetType可以获取到一个参数化的类型实现ParamerteredTypeImpl

在这里插入图片描述
类的结构大致如下:

public class ParameterizedTypeImpl implements ParameterizedType, Serializable {
	private static final long serialVersionUID = 1L;

	private final Type[] actualTypeArguments;
	private final Type ownerType;
	private final Type rawType;

	/**
	 * 构造
	 *
	 * @param actualTypeArguments 实际的泛型参数类型
	 * @param ownerType 拥有者类型
	 * @param rawType 原始类型
	 */
	public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType) {
        // 泛型列表,比如List<E>列表就只有一个元素,Map<K, V>列表就有两个元素,以此类推
		this.actualTypeArguments = actualTypeArguments;
        // 明确的持有者,如果当前类型是个内部类,则此类型表示外部持有这个内部类的类型
		this.ownerType = ownerType;
        // 当前类的原生类型,class
		this.rawType = rawType;
	}

	@Override
	public Type[] getActualTypeArguments() {
		return actualTypeArguments;
	}

	@Override
	public Type getOwnerType() {
		return ownerType;
	}

	@Override
	public Type getRawType() {
		return rawType;
	}
}

2.获取转换器

从这一步开始,我们会看到很多次的方法参数value,这个参数就是我们最开始的JSONObject对象

源码:

// 标准转换器
final Converter<T> converter = getConverter(type, isCustomFirst);
if (null != converter) {
   return converter.convert(value, defaultValue);
}

hutool存在各种各样的转换器,用来转换许多不同的类型,比如:

在这里插入图片描述
转换器接口如下:

public interface Converter<T> {

	/**
	 * 转换为指定类型<br>
	 * 如果类型无法确定,将读取默认值的类型做为目标类型
	 *
	 * @param value 原始值
	 * @param defaultValue 默认值
	 * @return 转换后的值
	 * @throws IllegalArgumentException 无法确定目标类型,且默认值为{@code null},无法确定类型
	 */
	T convert(Object value, T defaultValue) throws IllegalArgumentException;

	/**
	 * 转换值为指定类型,可选是否不抛异常转换<br>
	 * 当转换失败时返回默认值
	 *
	 * @param value 值
	 * @param defaultValue 默认值
	 * @param quietly 是否静默转换,true不抛异常
	 * @return 转换后的值
	 * @since 5.8.0
	 * @see #convert(Object, Object)
	 */
	default T convertWithCheck(Object value, T defaultValue, boolean quietly) {
		try {
			return convert(value, defaultValue);
		} catch (Exception e) {
			if(quietly){
				return defaultValue;
			}
			throw e;
		}
	}
}

如果存在转换器就把json直接转换成返回值;如果解析的过程获取不到转换器,就会继续以下流程

3.获取原生类型

源码:

// 获取原生的类型
Class<T> rowType = (Class<T>) TypeUtil.getClass(type);
	if (null == rowType) {
		if (null != defaultValue) {
		rowType = (Class<T>) defaultValue.getClass();
	} else {
		// 无法识别的泛型类型,按照Object处理
		return (T) value;
	}
}

如果type作为ParamteredType那么就要先获取原生类型,例如上面的Option<Value<String>>,获取到的原生类型就是xxx.xxx.xxx.Option,之后就是进行特殊类型转换了

4.特殊类型转换

源码:

// 特殊类型转换,包括Collection、Map、强转、Array等
final T result = convertSpecial(type, rowType, value, defaultValue);

这个方法含义也是很简单,也是相当于再次获取一下转换器

方法源码:

private <T> T convertSpecial(Type type, Class<T> rowType, Object value, T defaultValue) {
		if (null == rowType) {
			return null;
		}

		// 集合转换(不可以默认强转)
		if (Collection.class.isAssignableFrom(rowType)) {
			final CollectionConverter collectionConverter = new CollectionConverter(type);
			return (T) collectionConverter.convert(value, (Collection<?>) defaultValue);
		}

		// Map类型(不可以默认强转)
		if (Map.class.isAssignableFrom(rowType)) {
			final MapConverter mapConverter = new MapConverter(type);
			return (T) mapConverter.convert(value, (Map<?, ?>) defaultValue);
		}

		// Map类型(不可以默认强转)
		if (Map.Entry.class.isAssignableFrom(rowType)) {
			final EntryConverter mapConverter = new EntryConverter(type);
			return (T) mapConverter.convert(value, (Map.Entry<?, ?>) defaultValue);
		}

		// 默认强转
		if (rowType.isInstance(value)) {
			return (T) value;
		}

		// 枚举转换
		if (rowType.isEnum()) {
			return (T) new EnumConverter(rowType).convert(value, defaultValue);
		}

		// 数组转换
		if (rowType.isArray()) {
			final ArrayConverter arrayConverter = new ArrayConverter(rowType);
			return (T) arrayConverter.convert(value, defaultValue);
		}

		// issue#I7FQ29 Class
		if("java.lang.Class".equals(rowType.getName())){
			final ClassConverter converter = new ClassConverter();
			return (T) converter.convert(value, (Class<?>) defaultValue);
		}

		// 表示非需要特殊转换的对象
		return null;
	}

如果不是上述的特殊类型对象,那么就会进行更加原始的转换

源码:

// 尝试转Bean
if (BeanUtil.isBean(rowType)) {
   // 5直接进行转换
   return new BeanConverter<T>(type).convert(value, defaultValue);
}

5.构建BeanConverter

上述 new BeanConverter<T>(type),最终进入的构造方法是这个

源码:

/**
* 构造
*
* @param beanType 转换成的目标Bean类
* @param copyOptions Bean转换选项参数
*/
@SuppressWarnings("unchecked")
public BeanConverter(Type beanType, CopyOptions copyOptions) {
    // 当前的类型,可知当前是ParameteredType
    this.beanType = beanType;
    // 获取当前ParameteredType的源类型,不然也无法获取class对象
    this.beanClass = (Class<T>) TypeUtil.getClass(beanType);
    // 掠过一些配置...
    this.copyOptions = copyOptions;
}

6.使用转换器转换Json

接下来就是主要转换的代码了,源码:

@Override
	@SuppressWarnings("unchecked")
	public T convert(Object value, T defaultValue) {
		// 获取目标类型,class对象,在这里就是Option.class了
		Class<T> targetType = getTargetType();
        
        // 这里的判断很长,其实也没啥,就是:我不知道什么类型,又没有默认返回值,那我解析个锤锤
		if (null == targetType && null == defaultValue) {
			throw new NullPointerException(StrUtil.format("[type] and [defaultValue] are both null for Converter [{}], we can not know what type to convert !", this.getClass().getName()));
		}
        
        // 拿默认返回值的类型作为目标类型
		if (null == targetType) {
			// 目标类型不确定时使用默认值的类型
			targetType = (Class<T>) defaultValue.getClass();
		}
        
        // 没有value就返回默认值
		if (null == value) {
			return defaultValue;
		}

        // 判断是否已经是目标类型了
		if (null == defaultValue || targetType.isInstance(defaultValue)) {
			if (targetType.isInstance(value) && 
                // 这个条件是排除Map及子类,因为JSONObject也是个map
                !Map.class.isAssignableFrom(targetType)) {
                
				// 除Map外,已经是目标类型,不需要转换(Map类型涉及参数类型,需要单独转换)
				return targetType.cast(value);
			}
			// 内部业务转换*
			final T result = convertInternal(value);
			return ((null == result) ? defaultValue : result);
		} else {
            
            // 转换失败抛出异常
			throw new IllegalArgumentException(
					StrUtil.format("Default value [{}]({}) is not the instance of [{}]", defaultValue, defaultValue.getClass(), targetType));
		}
	}

内部解析方法:

@Override
	protected T convertInternal(Object value) {
        // 获取父类,判断是否是解析器,这一步掠过
		final Class<?>[] interfaces = this.beanClass.getInterfaces();
		for (Class<?> anInterface : interfaces) {
			if("cn.hutool.json.JSONBeanParser".equals(anInterface.getName())){
				final T obj = ReflectUtil.newInstanceIfPossible(this.beanClass);
				ReflectUtil.invoke(obj, "parse", value);
				return obj;
			}
		}

		// 如果是map,或者json等
		if(value instanceof Map ||
				value instanceof ValueProvider ||
				BeanUtil.isBean(value.getClass())) {
            
            // 如果目标类型是接口,则转换需要转为代理类,因为接口无法实例化
			if(value instanceof Map && this.beanClass.isInterface()) {
				// 将Map动态代理为Bean
				return MapProxy.create((Map<?, ?>)value).toProxyBean(this.beanClass);
			}
            
			//转换为目标对象类型
			return BeanCopier.create(value, ReflectUtil.newInstanceIfPossible(this.beanClass), this.beanType, this.copyOptions).copy();
            
        // 以下内容也掠过
		} else if(value instanceof byte[]){
			return ObjectUtil.deserialize((byte[])value);
		} else if(StrUtil.isEmptyIfStr(value)){
			return null;
		}

		throw new ConvertException("Unsupported source type: {}", value.getClass());
	}

7.赋值JSON内容到对象中

上述的这段代码先拆分两步,1先创建Bean赋值对象,2再进行拷贝数据

return BeanCopier.create(value, ReflectUtil.newInstanceIfPossible(this.beanClass), this.beanType, this.copyOptions).copy();

这里偷偷new出了实例对象,注意一下,在之后它会叫做target

ReflectUtil.newInstanceIfPossible(this.beanClass)
(1)创建Bean拷贝对象

BeanCopier.create方法会调用到以下构造方法

/**
	 * 构造
	 *
	 * @param source 来源对象,可以是Bean或者Map
	 * @param target 目标Bean对象
	 * @param targetType 目标的泛型类型,用于标注有泛型参数的Bean对象
	 * @param copyOptions 拷贝属性选项
	 */
public BeanCopier(Object source, T target, Type targetType, CopyOptions copyOptions) {
    Assert.notNull(source, "Source bean must be not null!");
    Assert.notNull(target, "Target bean must be not null!");
    
    // 拷贝者对象
    Copier<T> copier;
    
    // 这里的来源肯定是Map,因为是JSONObject
    if (source instanceof Map) {
        if (target instanceof Map) {
            
            // 目标类型如果是Map的话
            copier = (Copier<T>) new MapToMapCopier((Map<?, ?>) source, (Map<?, ?>) target, targetType, copyOptions);
        } else {
            
            // json转bean的复制对象,这行情况是JSONObject会执行到的*
            copier = new MapToBeanCopier<>((Map<?, ?>) source, target, targetType, copyOptions);
			}
        
    }else if(source instanceof ValueProvider){
        //noinspection unchecked
        copier = new ValueProviderToBeanCopier<>((ValueProvider<String>) source, target, targetType, copyOptions);
    } else {
        
        // 这个target其实是defaultValue
        if (target instanceof Map) {
            // 获取它的Copier
            copier = (Copier<T>) new BeanToMapCopier(source, (Map<?, ?>) target, targetType, copyOptions);
        } else {
            
            // 其他情况
            copier = new BeanToBeanCopier<>(source, target, targetType, copyOptions);
        }
    }
    this.copier = copier;
}

MapToBeanCopier类的结构如下:

/**
	 * 构造
	 *
	 * @param source      来源Map
	 * @param target      目标Bean对象
	 * @param targetType  目标泛型类型
	 * @param copyOptions 拷贝选项
	 */
	public MapToBeanCopier(Map<?, ?> source, T target, Type targetType, CopyOptions copyOptions) {
        // 设置资源(json),一个刚刚实例化的对象, 拷贝配置(掠过)
		super(source, target, copyOptions);

		// 针对MapWrapper特殊处理,提供的Map包装了忽略大小写的Map,则默认转Bean的时候也忽略大小写,如JSONObject(以下代码掠过)
		if(source instanceof MapWrapper){
			final Map<?, ?> raw = ((MapWrapper<?, ?>) source).getRaw();
			if(raw instanceof CaseInsensitiveMap){
				copyOptions.setIgnoreCase(true);
			}
		}

		this.targetType = targetType;
	}
(2)拷贝对象的copy方法

以下是MapToBeanCopiercopy方法源码:

@Override
public T copy() {
    // 获取目标类型的class
    Class<?> actualEditable = target.getClass();
    
    // 掠过该判断
    if (null != copyOptions.editable) {
        Assert.isTrue(copyOptions.editable.isInstance(target),
                      "Target class [{}] not assignable to Editable class [{}]", actualEditable.getName(), copyOptions.editable.getName());
        actualEditable = copyOptions.editable;
    }
    
    // 获取成员属性的描述,就是买个json属性对应的java属性
    // 例如Map.of("name", PropDesc<String>)...
    // PropDesc对象结构 {
    // 		Field f;成员属性
    //		Method getter; get方法
    // 		Method setter; set方法
	//}
    final Map<String, PropDesc> targetPropDescMap = BeanUtil.getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase);

    // 变量JSON内容
    this.source.forEach((sKey, sValue) -> {
        
        // 获取成员属性PropDesc对象
        if (null == sKey) {
            return;
        }
        String sKeyStr = copyOptions.editFieldName(sKey.toString());
        if (null == sKeyStr) {
            return;
        }
        if (false == copyOptions.testKeyFilter(sKeyStr)) {
            return;
        }

        // 检查目标字段可写性
        final PropDesc tDesc = findPropDesc(targetPropDescMap, sKeyStr);
        if (null == tDesc || !tDesc.isWritable(this.copyOptions.transientSupport)) {
            // 字段不可写,跳过之
            return;
        }
        sKeyStr = tDesc.getFieldName();

        // 检查目标是否过滤属性(掠过此代码)
        if (!copyOptions.testPropertyFilter(tDesc.getField(), sValue)) return;

        // 获取目标字段真实类型并转换源值
        final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType());
        
        // 转换该字段的值*
        Object newValue = this.copyOptions.convertField(fieldType, sValue);
        newValue = copyOptions.editFieldValue(sKeyStr, newValue);

        // 目标赋值(给target的成员属性设置内容)
        tDesc.setValue(this.target, newValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
    });
    
    // 返回结果,解析完毕
    return this.target;
}
(3)递归转换

上述代码存在这一行:

// 转换该字段的值
Object newValue = this.copyOptions.convertField(fieldType, sValue);

进去方法体内容:

/**
	 * 使用自定义转换器转换字段值<br>
	 * 如果自定义转换器为{@code null},则返回原值。
	 *
	 * @param targetType 目标类型
	 * @param fieldValue 字段值
	 * @return 编辑后的字段值
	 * @since 5.8.0
	 */
	protected Object convertField(Type targetType, Object fieldValue) {
		return (null != this.converter) ?
			// 复杂的内容,如果字段是Value类型,就是本文章的例子,那么就会走这一行
			this.converter.convert(targetType, fieldValue) : 
        	// 普通值
        	fieldValue;
	}

这里的这个转换器是 TypeConverter

/**
 * 自定义类型转换器,默认使用全局万能转换器转换
 */
protected TypeConverter converter = (type, value) -> {
    if (null == value) {
        return null;
    }

    if (value instanceof IJSONTypeConverter) {
        return ((IJSONTypeConverter) value).toBean(ObjectUtil.defaultIfNull(type, Object.class));
    }
    // 递归调用转换器回到第2步
    return Convert.convertWithCheck(type, value, null, ignoreError);
};

四、小结

以上就是JSON转为JavaBean的过程,过程逻辑并不难。但是,一些基础的转换器实现是比较麻烦的,如果想要更加深入的了解,可以查看hutool的源码。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值