添加RecordUtil支持record类(issue#3931@Github)

This commit is contained in:
Looly 2025-05-07 10:23:16 +08:00
parent 87a6bdeaaa
commit b8e6b1ecc0
3 changed files with 139 additions and 2 deletions

View File

@ -2,7 +2,7 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
# 5.8.38(2025-04-26)
# 5.8.38(2025-05-07)
### 🐣新特性
* 【core 】 `PathUtil#del`增加null检查pr#1331@Gitee
@ -14,6 +14,7 @@
* 【extra 】 `TemplateConfig`增加`setUseCache`方法issue#IC3JRY@Gitee
* 【extra 】 `AbstractFtp`增加`rename`方法issue#IC3PMI@Gitee
* 【core 】 优化`PropDesc`缓存注解判断提升性能pr#1335@Gitee
* 【core 】 添加`RecordUtil`支持record类issue#3931@Github
### 🐞Bug修复
* 【setting】 修复`Setting`autoLoad可能的加载为空的问题issue#3919@Github

View File

@ -12,6 +12,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
@ -48,7 +49,11 @@ public class BeanDesc implements Serializable {
public BeanDesc(Class<?> beanClass) {
Assert.notNull(beanClass);
this.beanClass = beanClass;
init();
if(RecordUtil.isRecord(beanClass)){
initForRecord();
}else{
init();
}
}
/**
@ -153,6 +158,27 @@ public class BeanDesc implements Serializable {
return this;
}
/**
* 针对Record类的反射初始化
*/
private void initForRecord() {
final Class<?> beanClass = this.beanClass;
final Map<String, PropDesc> propMap = this.propMap;
final List<Method> getters = ReflectUtil.getPublicMethods(beanClass, method -> 0 == method.getParameterCount());
// 排除静态属性和对象子类
final Field[] fields = ReflectUtil.getFields(beanClass, field -> !ModifierUtil.isStatic(field) && !ReflectUtil.isOuterClassField(field));
for (final Field field : fields) {
for (final Method getter : getters) {
if (field.getName().equals(getter.getName())) {
//record对象getter方法与字段同名
final PropDesc prop = new PropDesc(field, getter, null);
propMap.putIfAbsent(prop.getFieldName(), prop);
}
}
}
}
/**
* 根据字段创建属性描述<br>
* 查找Getter和Setter方法时会

View File

@ -0,0 +1,110 @@
package cn.hutool.core.bean;
import cn.hutool.core.bean.copier.ValueProvider;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.JdkUtil;
import cn.hutool.core.util.ReflectUtil;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.AbstractMap;
import java.util.Map;
/**
* java.lang.Record 相关工具类封装<br>
* 来自于FastJSON2的BeanUtils
*
* @author fastjson2, Looly
* @since 5.8.38
*/
public class RecordUtil {
private static volatile Class<?> RECORD_CLASS;
private static volatile Method METHOD_GET_RECORD_COMPONENTS;
private static volatile Method METHOD_COMPONENT_GET_NAME;
private static volatile Method METHOD_COMPONENT_GET_GENERIC_TYPE;
/**
* 判断给定类是否为Record类
*
* @param clazz
* @return 是否为Record类
*/
public static boolean isRecord(final Class<?> clazz) {
if (JdkUtil.JVM_VERSION < 14) {
// JDK14+支持Record类
return false;
}
final Class<?> superClass = clazz.getSuperclass();
if (superClass == null) {
return false;
}
if (RECORD_CLASS == null) {
// 此处不使用同步代码重复赋值并不影响判断
final String superclassName = superClass.getName();
if ("java.lang.Record".equals(superclassName)) {
RECORD_CLASS = superClass;
return true;
} else {
return false;
}
}
return superClass == RECORD_CLASS;
}
/**
* 获取Record类中所有字段名称getter方法名与字段同名
*
* @param recordClass Record类
* @return 字段数组
*/
@SuppressWarnings("unchecked")
public static Map.Entry<String, Type>[] getRecordComponents(final Class<?> recordClass) {
if (JdkUtil.JVM_VERSION < 14) {
// JDK14+支持Record类
return new Map.Entry[0];
}
if (null == METHOD_GET_RECORD_COMPONENTS) {
METHOD_GET_RECORD_COMPONENTS = ReflectUtil.getMethod(Class.class, "getRecordComponents");
}
final Class<Object> recordComponentClass = ClassUtil.loadClass("java.lang.reflect.RecordComponent");
if (METHOD_COMPONENT_GET_NAME == null) {
METHOD_COMPONENT_GET_NAME = ReflectUtil.getMethod(recordComponentClass, "getName");
}
if (METHOD_COMPONENT_GET_GENERIC_TYPE == null) {
METHOD_COMPONENT_GET_GENERIC_TYPE = ReflectUtil.getMethod(recordComponentClass, "getGenericType");
}
final Object[] components = ReflectUtil.invoke(recordClass, METHOD_GET_RECORD_COMPONENTS);
final Map.Entry<String, Type>[] entries = new Map.Entry[components.length];
for (int i = 0; i < components.length; i++) {
entries[i] = new AbstractMap.SimpleEntry<>(
ReflectUtil.invoke(components[i], METHOD_COMPONENT_GET_NAME),
ReflectUtil.invoke(components[i], METHOD_COMPONENT_GET_GENERIC_TYPE)
);
}
return entries;
}
/**
* 实例化Record类
*
* @param recordClass
* @param valueProvider 参数值提供器
* @return Record类
*/
public static Object newInstance(final Class<?> recordClass, final ValueProvider<String> valueProvider) {
final Map.Entry<String, Type>[] recordComponents = getRecordComponents(recordClass);
final Object[] args = new Object[recordComponents.length];
for (int i = 0; i < args.length; i++) {
args[i] = valueProvider.value(recordComponents[i].getKey(), recordComponents[i].getValue());
}
return ReflectUtil.newInstance(recordClass, args);
}
}