diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
index 98d6a8051..bdc032736 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
@@ -1,22 +1,15 @@
package cn.hutool.core.bean;
-import cn.hutool.core.annotation.AnnotationUtil;
-import cn.hutool.core.annotation.PropIgnore;
-import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.util.BooleanUtil;
-import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ModifierUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
-import cn.hutool.core.util.TypeUtil;
-import java.beans.Transient;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -333,305 +326,4 @@ public class BeanDesc implements Serializable {
return methodName.equals("set" + fieldName);
}
// ------------------------------------------------------------------------------------------------------ Private method end
-
- /**
- * 属性描述,包括了字段、getter、setter和相应的方法执行
- *
- * @author looly
- */
- public static class PropDesc {
-
- /**
- * 字段
- */
- private final Field field;
- /**
- * Getter方法
- */
- private Method getter;
- /**
- * Setter方法
- */
- private Method setter;
-
- /**
- * 构造
- * Getter和Setter方法设置为默认可访问
- *
- * @param field 字段
- * @param getter get方法
- * @param setter set方法
- */
- public PropDesc(Field field, Method getter, Method setter) {
- this.field = field;
- this.getter = ClassUtil.setAccessible(getter);
- this.setter = ClassUtil.setAccessible(setter);
- }
-
- /**
- * 获取字段名,如果存在Alias注解,读取注解的值作为名称
- *
- * @return 字段名
- */
- public String getFieldName() {
- return ReflectUtil.getFieldName(this.field);
- }
-
- /**
- * 获取字段名称
- *
- * @return 字段名
- * @since 5.1.6
- */
- public String getRawFieldName() {
- return null == this.field ? null : this.field.getName();
- }
-
- /**
- * 获取字段
- *
- * @return 字段
- */
- public Field getField() {
- return this.field;
- }
-
- /**
- * 获得字段类型
- * 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
- *
- * @return 字段类型
- */
- public Type getFieldType() {
- if (null != this.field) {
- return TypeUtil.getType(this.field);
- }
- return findPropType(getter, setter);
- }
-
- /**
- * 获得字段类型
- * 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
- *
- * @return 字段类型
- */
- public Class> getFieldClass() {
- if (null != this.field) {
- return TypeUtil.getClass(this.field);
- }
- return findPropClass(getter, setter);
- }
-
- /**
- * 获取Getter方法,可能为{@code null}
- *
- * @return Getter方法
- */
- public Method getGetter() {
- return this.getter;
- }
-
- /**
- * 获取Setter方法,可能为{@code null}
- *
- * @return {@link Method}Setter 方法对象
- */
- public Method getSetter() {
- return this.setter;
- }
-
- /**
- * 获取属性值
- * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
- *
- * @param bean Bean对象
- * @return 字段值
- * @since 4.0.5
- */
- public Object getValue(Object bean) {
- if (null != this.getter) {
- return ReflectUtil.invoke(bean, this.getter);
- } else if (ModifierUtil.isPublic(this.field)) {
- return ReflectUtil.getFieldValue(bean, this.field);
- }
- return null;
- }
-
- /**
- * 获取属性值,自动转换属性值类型
- * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
- *
- * @param bean Bean对象
- * @param valueType 返回属性值类型,null表示不转换
- * @param ignoreError 是否忽略错误,包括转换错误和注入错误
- * @return this
- * @since 5.4.2
- */
- public Object getValueWithConvert(Object bean, Type valueType, boolean ignoreError) {
- Object result = null;
- try {
- result = getValue(bean);
- } catch (Exception e) {
- if (false == ignoreError) {
- throw new BeanException(e, "Get value of [{}] error!", getFieldName());
- }
- }
-
- if (null != result && null != valueType) {
- // 尝试将结果转换为目标类型,如果转换失败,返回原类型。
- final Object convertValue = Convert.convertWithCheck(valueType, result, null, ignoreError);
- if (null != convertValue) {
- result = convertValue;
- }
- }
- return result;
- }
-
- /**
- * 设置Bean的字段值
- * 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值
- *
- * @param bean Bean对象
- * @param value 值,必须与字段值类型匹配
- * @return this
- * @since 4.0.5
- */
- public PropDesc setValue(Object bean, Object value) {
- if (null != this.setter) {
- ReflectUtil.invoke(bean, this.setter, value);
- } else if (ModifierUtil.isPublic(this.field)) {
- ReflectUtil.setFieldValue(bean, this.field, value);
- }
- return this;
- }
-
- /**
- * 设置属性值,可以自动转换字段类型为目标类型
- *
- * @param bean Bean对象
- * @param value 属性值,可以为任意类型
- * @param ignoreNull 是否忽略{@code null}值,true表示忽略
- * @param ignoreError 是否忽略错误,包括转换错误和注入错误
- * @return this
- * @since 5.4.2
- */
- public PropDesc setValueWithConvert(Object bean, Object value, boolean ignoreNull, boolean ignoreError) {
- if (ignoreNull && null == value) {
- return this;
- }
-
- // 当类型不匹配的时候,执行默认转换
- if (null != value) {
- final Class> propClass = getFieldClass();
- if (false == propClass.isInstance(value)) {
- value = Convert.convertWithCheck(propClass, value, null, ignoreError);
- }
- }
-
- // 属性赋值
- if (null != value || false == ignoreNull) {
- try {
- this.setValue(bean, value);
- } catch (Exception e) {
- if (false == ignoreError) {
- throw new BeanException(e, "Set value of [{}] error!", getFieldName());
- }
- // 忽略注入失败
- }
- }
-
- return this;
- }
-
- /**
- * 字段和Getter方法是否为Transient关键字修饰的
- *
- * @return 是否为Transient关键字修饰的
- * @since 5.3.11
- */
- public boolean isTransient() {
- boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
-
- // 检查Getter方法
- if (false == isTransient && null != this.getter) {
- isTransient = ModifierUtil.hasModifier(this.getter, ModifierUtil.ModifierType.TRANSIENT);
-
- // 检查注解
- if (false == isTransient) {
- isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class);
- }
- }
-
- return isTransient;
- }
-
- /**
- * 检查字段是否被忽略读,通过{@link PropIgnore} 注解完成,规则为:
- *
- * 1. 在字段上有{@link PropIgnore} 注解
- * 2. 在getXXX方法上有{@link PropIgnore} 注解
- *
- *
- * @return 是否忽略读
- * @since 5.4.2
- */
- public boolean isIgnoreGet() {
- return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
- || AnnotationUtil.hasAnnotation(this.getter, PropIgnore.class);
- }
-
- /**
- * 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为:
- *
- * 1. 在字段上有{@link PropIgnore} 注解
- * 2. 在setXXX方法上有{@link PropIgnore} 注解
- *
- *
- * @return 是否忽略写
- * @since 5.4.2
- */
- public boolean isIgnoreSet() {
- return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
- || AnnotationUtil.hasAnnotation(this.setter, PropIgnore.class);
- }
-
- //------------------------------------------------------------------------------------ Private method start
-
- /**
- * 通过Getter和Setter方法中找到属性类型
- *
- * @param getter Getter方法
- * @param setter Setter方法
- * @return {@link Type}
- */
- private Type findPropType(Method getter, Method setter) {
- Type type = null;
- if (null != getter) {
- type = TypeUtil.getReturnType(getter);
- }
- if (null == type && null != setter) {
- type = TypeUtil.getParamType(setter, 0);
- }
- return type;
- }
-
- /**
- * 通过Getter和Setter方法中找到属性类型
- *
- * @param getter Getter方法
- * @param setter Setter方法
- * @return {@link Type}
- */
- private Class> findPropClass(Method getter, Method setter) {
- Class> type = null;
- if (null != getter) {
- type = TypeUtil.getReturnClass(getter);
- }
- if (null == type && null != setter) {
- type = TypeUtil.getFirstParamClass(setter);
- }
- return type;
- }
- //------------------------------------------------------------------------------------ Private method end
- }
}
\ No newline at end of file
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
index f7e68e8bc..1eefdef40 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
@@ -1,6 +1,5 @@
package cn.hutool.core.bean;
-import cn.hutool.core.bean.BeanDesc.PropDesc;
import cn.hutool.core.bean.copier.BeanCopier;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.bean.copier.ValueProvider;
@@ -29,6 +28,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
/**
* Bean工具类
@@ -169,6 +169,17 @@ public class BeanUtil {
return BeanDescCache.INSTANCE.getBeanDesc(clazz, ()-> new BeanDesc(clazz));
}
+ /**
+ * 遍历Bean的属性
+ *
+ * @param clazz Bean类
+ * @param action 每个元素的处理类
+ * @since 5.4.2
+ */
+ public static void descForEach(Class> clazz, Consumer super PropDesc> action){
+ getBeanDesc(clazz).getProps().forEach(action);
+ }
+
// --------------------------------------------------------------------------------------------------------- PropertyDescriptor
/**
@@ -616,32 +627,11 @@ public class BeanUtil {
return null;
}
- final Collection props = BeanUtil.getBeanDesc(bean.getClass()).getProps();
-
- String key;
- Method getter;
- Object value;
- for (PropDesc prop : props) {
- key = prop.getFieldName();
- // 过滤class属性
- // 得到property对应的getter方法
- getter = prop.getGetter();
- if (null != getter) {
- // 只读取有getter方法的属性
- try {
- value = getter.invoke(bean);
- } catch (Exception ignore) {
- continue;
- }
- if (false == ignoreNullValue || (null != value && false == value.equals(bean))) {
- key = keyEditor.edit(key);
- if (null != key) {
- targetMap.put(key, value);
- }
- }
- }
- }
- return targetMap;
+ return BeanCopier.create(bean, targetMap,
+ CopyOptions.create()
+ .setIgnoreNullValue(ignoreNullValue)
+ .setFieldNameEditor(keyEditor)
+ ).copy();
}
// --------------------------------------------------------------------------------------------- copyProperties
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java b/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java
index e9a3a6621..bf8280288 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java
@@ -82,7 +82,7 @@ public class DynaBean extends CloneSupport implements Serializable {
if (Map.class.isAssignableFrom(beanClass)) {
return (T) ((Map, ?>) bean).get(fieldName);
} else {
- final BeanDesc.PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
+ final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
if(null == prop){
throw new BeanException("No public field or get method for {}", fieldName);
}
@@ -129,7 +129,7 @@ public class DynaBean extends CloneSupport implements Serializable {
if (Map.class.isAssignableFrom(beanClass)) {
((Map) bean).put(fieldName, value);
} else {
- final BeanDesc.PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
+ final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
if(null == prop){
throw new BeanException("No public field or set method for {}", fieldName);
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java b/hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java
new file mode 100644
index 000000000..0f6534e96
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java
@@ -0,0 +1,382 @@
+package cn.hutool.core.bean;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.annotation.PropIgnore;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ModifierUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.TypeUtil;
+
+import java.beans.Transient;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+/**
+ * 属性描述,包括了字段、getter、setter和相应的方法执行
+ *
+ * @author looly
+ */
+public class PropDesc {
+
+ /**
+ * 字段
+ */
+ final Field field;
+ /**
+ * Getter方法
+ */
+ protected Method getter;
+ /**
+ * Setter方法
+ */
+ protected Method setter;
+
+ /**
+ * 构造
+ * Getter和Setter方法设置为默认可访问
+ *
+ * @param field 字段
+ * @param getter get方法
+ * @param setter set方法
+ */
+ public PropDesc(Field field, Method getter, Method setter) {
+ this.field = field;
+ this.getter = ClassUtil.setAccessible(getter);
+ this.setter = ClassUtil.setAccessible(setter);
+ }
+
+ /**
+ * 获取字段名,如果存在Alias注解,读取注解的值作为名称
+ *
+ * @return 字段名
+ */
+ public String getFieldName() {
+ return ReflectUtil.getFieldName(this.field);
+ }
+
+ /**
+ * 获取字段名称
+ *
+ * @return 字段名
+ * @since 5.1.6
+ */
+ public String getRawFieldName() {
+ return null == this.field ? null : this.field.getName();
+ }
+
+ /**
+ * 获取字段
+ *
+ * @return 字段
+ */
+ public Field getField() {
+ return this.field;
+ }
+
+ /**
+ * 获得字段类型
+ * 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
+ *
+ * @return 字段类型
+ */
+ public Type getFieldType() {
+ if (null != this.field) {
+ return TypeUtil.getType(this.field);
+ }
+ return findPropType(getter, setter);
+ }
+
+ /**
+ * 获得字段类型
+ * 先获取字段的类型,如果字段不存在,则获取Getter方法的返回类型,否则获取Setter的第一个参数类型
+ *
+ * @return 字段类型
+ */
+ public Class> getFieldClass() {
+ if (null != this.field) {
+ return TypeUtil.getClass(this.field);
+ }
+ return findPropClass(getter, setter);
+ }
+
+ /**
+ * 获取Getter方法,可能为{@code null}
+ *
+ * @return Getter方法
+ */
+ public Method getGetter() {
+ return this.getter;
+ }
+
+ /**
+ * 获取Setter方法,可能为{@code null}
+ *
+ * @return {@link Method}Setter 方法对象
+ */
+ public Method getSetter() {
+ return this.setter;
+ }
+
+ /**
+ * 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
+ * @param checkTransient 是否检查Transient关键字或注解
+ * @return 是否可读
+ * @since 5.4.2
+ */
+ public boolean isReadable(boolean checkTransient){
+ // 检查是否有getter方法或是否为public修饰
+ if(null == this.getter && false == ModifierUtil.isPublic(this.field)){
+ return false;
+ }
+
+ // 检查transient关键字和@Transient注解
+ if(checkTransient && isTransientForGet()){
+ return false;
+ }
+
+ // 检查@PropIgnore注解
+ return false == isIgnoreGet();
+ }
+
+ /**
+ * 获取属性值
+ * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
+ * 此方法不检查任何注解,使用前需调用 {@link #isReadable(boolean)} 检查是否可读
+ *
+ * @param bean Bean对象
+ * @return 字段值
+ * @since 4.0.5
+ */
+ public Object getValue(Object bean) {
+ if (null != this.getter) {
+ return ReflectUtil.invoke(bean, this.getter);
+ } else if (ModifierUtil.isPublic(this.field)) {
+ return ReflectUtil.getFieldValue(bean, this.field);
+ }
+
+ return null;
+ }
+
+ /**
+ * 获取属性值,自动转换属性值类型
+ * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
+ *
+ * @param bean Bean对象
+ * @param targetType 返回属性值需要转换的类型,null表示不转换
+ * @param ignoreError 是否忽略错误,包括转换错误和注入错误
+ * @return this
+ * @since 5.4.2
+ */
+ public Object getValue(Object bean, Type targetType, boolean ignoreError) {
+ Object result = null;
+ try {
+ result = getValue(bean);
+ } catch (Exception e) {
+ if (false == ignoreError) {
+ throw new BeanException(e, "Get value of [{}] error!", getFieldName());
+ }
+ }
+
+ if (null != result && null != targetType) {
+ // 尝试将结果转换为目标类型,如果转换失败,返回原类型。
+ final Object convertValue = Convert.convertWithCheck(targetType, result, null, ignoreError);
+ if (null != convertValue) {
+ result = convertValue;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 检查属性是否可读(即是否可以通过{@link #getValue(Object)}获取到值)
+ * @param checkTransient 是否检查Transient关键字或注解
+ * @return 是否可读
+ * @since 5.4.2
+ */
+ public boolean isWritable(boolean checkTransient){
+ // 检查是否有getter方法或是否为public修饰
+ if(null == this.setter && false == ModifierUtil.isPublic(this.field)){
+ return false;
+ }
+
+ // 检查transient关键字和@Transient注解
+ if(checkTransient && isTransientForSet()){
+ return false;
+ }
+
+ // 检查@PropIgnore注解
+ return false == isIgnoreSet();
+ }
+
+ /**
+ * 设置Bean的字段值
+ * 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值
+ * 此方法不检查任何注解,使用前需调用 {@link #isWritable(boolean)} 检查是否可写
+ *
+ * @param bean Bean对象
+ * @param value 值,必须与字段值类型匹配
+ * @return this
+ * @since 4.0.5
+ */
+ public PropDesc setValue(Object bean, Object value) {
+ if (null != this.setter) {
+ ReflectUtil.invoke(bean, this.setter, value);
+ } else if (ModifierUtil.isPublic(this.field)) {
+ ReflectUtil.setFieldValue(bean, this.field, value);
+ }
+ return this;
+ }
+
+ /**
+ * 设置属性值,可以自动转换字段类型为目标类型
+ *
+ * @param bean Bean对象
+ * @param value 属性值,可以为任意类型
+ * @param ignoreNull 是否忽略{@code null}值,true表示忽略
+ * @param ignoreError 是否忽略错误,包括转换错误和注入错误
+ * @return this
+ * @since 5.4.2
+ */
+ public PropDesc setValue(Object bean, Object value, boolean ignoreNull, boolean ignoreError) {
+ if (ignoreNull && null == value) {
+ return this;
+ }
+
+ // 当类型不匹配的时候,执行默认转换
+ if (null != value) {
+ final Class> propClass = getFieldClass();
+ if (false == propClass.isInstance(value)) {
+ value = Convert.convertWithCheck(propClass, value, null, ignoreError);
+ }
+ }
+
+ // 属性赋值
+ if (null != value || false == ignoreNull) {
+ try {
+ this.setValue(bean, value);
+ } catch (Exception e) {
+ if (false == ignoreError) {
+ throw new BeanException(e, "Set value of [{}] error!", getFieldName());
+ }
+ // 忽略注入失败
+ }
+ }
+
+ return this;
+ }
+
+ //------------------------------------------------------------------------------------ Private method start
+
+ /**
+ * 通过Getter和Setter方法中找到属性类型
+ *
+ * @param getter Getter方法
+ * @param setter Setter方法
+ * @return {@link Type}
+ */
+ private Type findPropType(Method getter, Method setter) {
+ Type type = null;
+ if (null != getter) {
+ type = TypeUtil.getReturnType(getter);
+ }
+ if (null == type && null != setter) {
+ type = TypeUtil.getParamType(setter, 0);
+ }
+ return type;
+ }
+
+ /**
+ * 通过Getter和Setter方法中找到属性类型
+ *
+ * @param getter Getter方法
+ * @param setter Setter方法
+ * @return {@link Type}
+ */
+ private Class> findPropClass(Method getter, Method setter) {
+ Class> type = null;
+ if (null != getter) {
+ type = TypeUtil.getReturnClass(getter);
+ }
+ if (null == type && null != setter) {
+ type = TypeUtil.getFirstParamClass(setter);
+ }
+ return type;
+ }
+
+ /**
+ * 检查字段是否被忽略写,通过{@link PropIgnore} 注解完成,规则为:
+ *
+ * 1. 在字段上有{@link PropIgnore} 注解
+ * 2. 在setXXX方法上有{@link PropIgnore} 注解
+ *
+ *
+ * @return 是否忽略写
+ * @since 5.4.2
+ */
+ private boolean isIgnoreSet() {
+ return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
+ || AnnotationUtil.hasAnnotation(this.setter, PropIgnore.class);
+ }
+
+ /**
+ * 检查字段是否被忽略读,通过{@link PropIgnore} 注解完成,规则为:
+ *
+ * 1. 在字段上有{@link PropIgnore} 注解
+ * 2. 在getXXX方法上有{@link PropIgnore} 注解
+ *
+ *
+ * @return 是否忽略读
+ * @since 5.4.2
+ */
+ private boolean isIgnoreGet() {
+ return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
+ || AnnotationUtil.hasAnnotation(this.getter, PropIgnore.class);
+ }
+
+ /**
+ * 字段和Getter方法是否为Transient关键字修饰的
+ *
+ * @return 是否为Transient关键字修饰的
+ * @since 5.3.11
+ */
+ private boolean isTransientForGet() {
+ boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
+
+ // 检查Getter方法
+ if (false == isTransient && null != this.getter) {
+ isTransient = ModifierUtil.hasModifier(this.getter, ModifierUtil.ModifierType.TRANSIENT);
+
+ // 检查注解
+ if (false == isTransient) {
+ isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class);
+ }
+ }
+
+ return isTransient;
+ }
+
+ /**
+ * 字段和Getter方法是否为Transient关键字修饰的
+ *
+ * @return 是否为Transient关键字修饰的
+ * @since 5.3.11
+ */
+ private boolean isTransientForSet() {
+ boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT);
+
+ // 检查Getter方法
+ if (false == isTransient && null != this.setter) {
+ isTransient = ModifierUtil.hasModifier(this.setter, ModifierUtil.ModifierType.TRANSIENT);
+
+ // 检查注解
+ if (false == isTransient) {
+ isTransient = AnnotationUtil.hasAnnotation(this.setter, Transient.class);
+ }
+ }
+
+ return isTransient;
+ }
+ //------------------------------------------------------------------------------------ Private method end
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
index 4cb9792e6..2203d7730 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
@@ -1,6 +1,5 @@
package cn.hutool.core.bean.copier;
-import cn.hutool.core.bean.BeanDesc.PropDesc;
import cn.hutool.core.bean.BeanException;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.DynaBean;
@@ -9,17 +8,11 @@ import cn.hutool.core.bean.copier.provider.DynaBeanValueProvider;
import cn.hutool.core.bean.copier.provider.MapValueProvider;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.copier.Copier;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.ModifierUtil;
-import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import java.io.Serializable;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.lang.reflect.Type;
-import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
@@ -164,43 +157,45 @@ public class BeanCopier implements Copier, Serializable {
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void beanToMap(Object bean, Map targetMap) {
- final Collection props = BeanUtil.getBeanDesc(bean.getClass()).getProps();
final HashSet ignoreSet = (null != copyOptions.ignoreProperties) ? CollUtil.newHashSet(copyOptions.ignoreProperties) : null;
final CopyOptions copyOptions = this.copyOptions;
- String key;
- Object value;
- for (PropDesc prop : props) {
- // 忽略注解的字段
- if(prop.isIgnoreGet()){
- continue;
+ BeanUtil.descForEach(bean.getClass(), (prop)->{
+ if(false == prop.isReadable(copyOptions.isTransientSupport())){
+ // 忽略的属性跳过之
+ return;
}
- key = prop.getFieldName();
+ String key = prop.getFieldName();
if (CollUtil.contains(ignoreSet, key)) {
// 目标属性值被忽略或值提供者无此key时跳过
- continue;
+ return;
}
+
+ Object value;
try {
value = prop.getValue(bean);
} catch (Exception e) {
if (copyOptions.ignoreError) {
- continue;// 忽略反射失败
+ return;// 忽略反射失败
} else {
throw new BeanException(e, "Get value of [{}] error!", prop.getFieldName());
}
}
- if (null == value && copyOptions.ignoreNullValue) {
- continue;// 当允许跳过空时,跳过
+ if ((null == value && copyOptions.ignoreNullValue) || bean == value) {
+ // 当允许跳过空时,跳过
+ //值不能为bean本身,防止循环引用,此类也跳过
+ return;
}
- if (bean.equals(value)) {
- continue;// 值不能为bean本身,防止循环引用
- }
- targetMap.put(mappingKey(copyOptions.fieldMapping, key), value);
- }
+
+ // 对key做映射
+ key = copyOptions.editFieldName(copyOptions.getMappedFieldName(key, false));
+ targetMap.put(key, value);
+ });
}
/**
- * 值提供器转Bean
+ * 值提供器转Bean
+ * 此方法通过遍历目标Bean的字段,从ValueProvider查找对应值
*
* @param valueProvider 值提供器
* @param bean Bean
@@ -220,73 +215,38 @@ public class BeanCopier implements Copier, Serializable {
actualEditable = copyOptions.editable;
}
final HashSet ignoreSet = (null != copyOptions.ignoreProperties) ? CollUtil.newHashSet(copyOptions.ignoreProperties) : null;
- final Map fieldReverseMapping = copyOptions.getReversedMapping();
- final Collection props = BeanUtil.getBeanDesc(actualEditable).getProps();
- Field field;
- String fieldName;
- Object value;
- Method setterMethod;
- Class> propClass;
- for (PropDesc prop : props) {
- // 获取值
- field = prop.getField();
- fieldName = prop.getFieldName();
+ BeanUtil.descForEach(actualEditable, (prop)->{
+ if(false == prop.isWritable(this.copyOptions.isTransientSupport())){
+ // 字段不可写,跳过之
+ return;
+ }
+
+ // 检查属性名
+ String fieldName = prop.getFieldName();
if (CollUtil.contains(ignoreSet, fieldName)) {
// 目标属性值被忽略或值提供者无此key时跳过
- continue;
+ return;
}
- // 在支持情况下,忽略transient修饰(包括修饰和注解)
- if(copyOptions.isTransientSupport() && prop.isTransient()){
- continue;
- }
-
- // @Ignore修饰的字段和setXXX方法忽略之
- if(prop.isIgnoreSet()){
- continue;
- }
-
- final String providerKey = mappingKey(fieldReverseMapping, fieldName);
+ final String providerKey = copyOptions.getMappedFieldName(fieldName, true);
if (false == valueProvider.containsKey(providerKey)) {
// 无对应值可提供
- continue;
- }
- setterMethod = prop.getSetter();
- if (null == setterMethod && false == ModifierUtil.isPublic(field)) {
- // Setter方法不存在或者字段为非public跳过
- //5.1.0新增支持public字段注入支持
- continue;
+ return;
}
// 获取目标字段真实类型
final Type fieldType = TypeUtil.getActualType(this.destType ,prop.getFieldType());
- value = valueProvider.value(providerKey, fieldType);
- if (null == value && copyOptions.ignoreNullValue) {
- continue;// 当允许跳过空时,跳过
- }
- if (bean == value) {
+ // 获取属性值
+ Object value = valueProvider.value(providerKey, fieldType);
+ if ((null == value && copyOptions.ignoreNullValue) || bean == value) {
+ // 当允许跳过空时,跳过
// 值不能为bean本身,防止循环引用
- continue;
+ return;
}
- prop.setValueWithConvert(bean, value, copyOptions.ignoreNullValue, copyOptions.ignoreError);
- }
- }
-
- /**
- * 获取指定字段名对应的映射值
- *
- * @param mapping 反向映射Map
- * @param fieldName 字段名
- * @return 映射值,无对应值返回字段名
- * @since 4.1.10
- */
- private static String mappingKey(Map mapping, String fieldName) {
- if (MapUtil.isEmpty(mapping)) {
- return fieldName;
- }
- return ObjectUtil.defaultIfNull(mapping.get(fieldName), fieldName);
+ prop.setValue(bean, value, copyOptions.ignoreNullValue, copyOptions.ignoreError);
+ });
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java
index b588db654..812559f65 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java
@@ -1,6 +1,8 @@
package cn.hutool.core.bean.copier;
+import cn.hutool.core.lang.Editor;
import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
import java.io.Serializable;
import java.util.Map;
@@ -41,6 +43,14 @@ public class CopyOptions implements Serializable {
* 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
*/
protected Map fieldMapping;
+ /**
+ * 反向映射表,自动生成用于反向查找
+ */
+ private Map reversedFieldMapping;
+ /**
+ * 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
+ */
+ protected Editor fieldNameEditor;
/**
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
*/
@@ -182,6 +192,19 @@ public class CopyOptions implements Serializable {
return this;
}
+ /**
+ * 设置字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
+ * 此转换器只针对源端的字段做转换,请确认转换后与目标端字段一致
+ *
+ * @param fieldNameEditor 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
+ * @return CopyOptions
+ * @since 5.4.2
+ */
+ public CopyOptions setFieldNameEditor(Editor fieldNameEditor) {
+ this.fieldNameEditor = fieldNameEditor;
+ return this;
+ }
+
/**
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
*
@@ -204,13 +227,45 @@ public class CopyOptions implements Serializable {
return this;
}
+ /**
+ * 获得映射后的字段名
+ * 当非反向,则根据源字段名获取目标字段名,反之根据目标字段名获取源字段名。
+ *
+ * @param fieldName 字段名
+ * @param reversed 是否反向映射
+ * @return 映射后的字段名
+ */
+ protected String getMappedFieldName(String fieldName, boolean reversed){
+ Map mapping = reversed ? getReversedMapping() : this.fieldMapping;
+ if(MapUtil.isEmpty(mapping)){
+ return fieldName;
+ }
+ return ObjectUtil.defaultIfNull(mapping.get(fieldName), fieldName);
+ }
+
+ /**
+ * 转换字段名为编辑后的字段名
+ * @param fieldName 字段名
+ * @return 编辑后的字段名
+ * @since 5.4.2
+ */
+ protected String editFieldName(String fieldName){
+ return (null != this.fieldNameEditor) ? this.fieldNameEditor.edit(fieldName) : fieldName;
+ }
+
/**
* 获取反转之后的映射
*
* @return 反转映射
* @since 4.1.10
*/
- protected Map getReversedMapping() {
- return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
+ private Map getReversedMapping() {
+ if(null == this.fieldMapping){
+ return null;
+ }
+ if(null == this.reversedFieldMapping){
+ reversedFieldMapping = MapUtil.reverse(this.fieldMapping);
+ }
+ return reversedFieldMapping;
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java
index bdbae6880..2ca6ce471 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java
@@ -1,7 +1,7 @@
package cn.hutool.core.bean.copier.provider;
-import cn.hutool.core.bean.BeanDesc.PropDesc;
import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.bean.copier.ValueProvider;
import cn.hutool.core.util.StrUtil;
@@ -38,7 +38,7 @@ public class BeanValueProvider implements ValueProvider {
Object result = null;
if (null != sourcePd) {
- result = sourcePd.getValueWithConvert(this.source, valueType, this.ignoreError);
+ result = sourcePd.getValue(this.source, valueType, this.ignoreError);
}
return result;
}
@@ -48,7 +48,7 @@ public class BeanValueProvider implements ValueProvider {
final PropDesc sourcePd = getPropDesc(key, null);
// 字段描述不存在或忽略读的情况下,表示不存在
- return null != sourcePd && false == sourcePd.isIgnoreGet();
+ return null != sourcePd && false == sourcePd.isReadable(false);
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/comparator/PropertyComparator.java b/hutool-core/src/main/java/cn/hutool/core/comparator/PropertyComparator.java
index 73ce5d25c..40cc501cf 100644
--- a/hutool-core/src/main/java/cn/hutool/core/comparator/PropertyComparator.java
+++ b/hutool-core/src/main/java/cn/hutool/core/comparator/PropertyComparator.java
@@ -1,11 +1,11 @@
package cn.hutool.core.comparator;
-import java.io.Serializable;
-import java.util.Comparator;
-
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
+import java.io.Serializable;
+import java.util.Comparator;
+
/**
* Bean属性排序器
* 支持读取Bean多层次下的属性
@@ -53,8 +53,8 @@ public class PropertyComparator implements Comparator, Serializable {
Comparable> v1;
Comparable> v2;
try {
- v1 = (Comparable>) BeanUtil.getProperty(o1, property);
- v2 = (Comparable>) BeanUtil.getProperty(o2, property);
+ v1 = BeanUtil.getProperty(o1, property);
+ v2 = BeanUtil.getProperty(o2, property);
} catch (Exception e) {
throw new ComparatorException(e);
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanDescTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanDescTest.java
index 5139c758b..353b9b91a 100644
--- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanDescTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/bean/BeanDescTest.java
@@ -3,8 +3,6 @@ package cn.hutool.core.bean;
import org.junit.Assert;
import org.junit.Test;
-import cn.hutool.core.bean.BeanDesc.PropDesc;
-
/**
* {@link BeanDesc} 单元测试类
*
diff --git a/hutool-db/src/main/java/cn/hutool/db/handler/HandleHelper.java b/hutool-db/src/main/java/cn/hutool/db/handler/HandleHelper.java
index e9762bebc..5fb86e344 100644
--- a/hutool-db/src/main/java/cn/hutool/db/handler/HandleHelper.java
+++ b/hutool-db/src/main/java/cn/hutool/db/handler/HandleHelper.java
@@ -1,7 +1,7 @@
package cn.hutool.db.handler;
-import cn.hutool.core.bean.BeanDesc.PropDesc;
import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java
index 011cbef69..0f0117c8a 100644
--- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java
+++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java
@@ -1,8 +1,9 @@
package cn.hutool.json;
-import cn.hutool.core.bean.BeanDesc.PropDesc;
import cn.hutool.core.bean.BeanPath;
import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.copier.BeanCopier;
+import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.CaseInsensitiveLinkedMap;
@@ -19,7 +20,6 @@ import cn.hutool.json.serialize.JSONSerializer;
import java.io.IOException;
import java.io.Writer;
-import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
@@ -620,41 +620,12 @@ public class JSONObject implements JSON, JSONGetter, Map
* @param bean Bean对象
*/
private void populateMap(Object bean) {
- final Collection props = BeanUtil.getBeanDesc(bean.getClass()).getProps();
-
- Method getter;
- Object value;
- for (PropDesc prop : props) {
- if(this.config.isTransientSupport() && prop.isTransient()){
- // 忽略Transient字段和方法
- continue;
- }
-
- // 得到property对应的getter方法
- getter = prop.getGetter();
- if (null == getter) {
- // 无Getter跳过
- continue;
- }
-
- // 只读取有getter方法的属性
- try {
- value = getter.invoke(bean);
- } catch (Exception ignore) {
- // 忽略读取失败的属性
- continue;
- }
-
- if (ObjectUtil.isNull(value) && this.config.isIgnoreNullValue()) {
- // 值为null且用户定义跳过则跳过
- continue;
- }
-
- if (value != bean) {
- // 防止循环引用
- this.put(prop.getFieldName(), value);
- }
- }
+ BeanCopier.create(bean, this,
+ CopyOptions.create()
+ .setIgnoreCase(config.isIgnoreCase())
+ .setIgnoreError(true)
+ .setIgnoreNullValue(config.isIgnoreNullValue())
+ ).copy();
}
/**