This commit is contained in:
Looly 2020-09-09 14:23:21 +08:00
parent 5aa10c06ee
commit 0c3a1ea298
11 changed files with 513 additions and 465 deletions

View File

@ -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
/**
* 属性描述包括了字段gettersetter和相应的方法执行
*
* @author looly
*/
public static class PropDesc {
/**
* 字段
*/
private final Field field;
/**
* Getter方法
*/
private Method getter;
/**
* Setter方法
*/
private Method setter;
/**
* 构造<br>
* 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;
}
/**
* 获得字段类型<br>
* 先获取字段的类型如果字段不存在则获取Getter方法的返回类型否则获取Setter的第一个参数类型
*
* @return 字段类型
*/
public Type getFieldType() {
if (null != this.field) {
return TypeUtil.getType(this.field);
}
return findPropType(getter, setter);
}
/**
* 获得字段类型<br>
* 先获取字段的类型如果字段不存在则获取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;
}
/**
* 获取属性值<br>
* 首先调用字段对应的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;
}
/**
* 获取属性值自动转换属性值类型<br>
* 首先调用字段对应的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的字段值<br>
* 首先调用字段对应的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} 注解完成规则为
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在getXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @return 是否忽略读
* @since 5.4.2
*/
public boolean isIgnoreGet() {
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|| AnnotationUtil.hasAnnotation(this.getter, PropIgnore.class);
}
/**
* 检查字段是否被忽略写通过{@link PropIgnore} 注解完成规则为
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在setXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @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
}
}

View File

@ -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<PropDesc> 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

View File

@ -82,7 +82,7 @@ public class DynaBean extends CloneSupport<DynaBean> 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<DynaBean> 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);
}

View File

@ -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;
/**
* 属性描述包括了字段gettersetter和相应的方法执行
*
* @author looly
*/
public class PropDesc {
/**
* 字段
*/
final Field field;
/**
* Getter方法
*/
protected Method getter;
/**
* Setter方法
*/
protected Method setter;
/**
* 构造<br>
* 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;
}
/**
* 获得字段类型<br>
* 先获取字段的类型如果字段不存在则获取Getter方法的返回类型否则获取Setter的第一个参数类型
*
* @return 字段类型
*/
public Type getFieldType() {
if (null != this.field) {
return TypeUtil.getType(this.field);
}
return findPropType(getter, setter);
}
/**
* 获得字段类型<br>
* 先获取字段的类型如果字段不存在则获取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();
}
/**
* 获取属性值<br>
* 首先调用字段对应的Getter方法获取值如果Getter方法不存在则判断字段如果为public则直接获取字段值<br>
* 此方法不检查任何注解使用前需调用 {@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;
}
/**
* 获取属性值自动转换属性值类型<br>
* 首先调用字段对应的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的字段值<br>
* 首先调用字段对应的Setter方法如果Setter方法不存在则判断字段如果为public则直接赋值字段值<br>
* 此方法不检查任何注解使用前需调用 {@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} 注解完成规则为
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在setXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @return 是否忽略写
* @since 5.4.2
*/
private boolean isIgnoreSet() {
return AnnotationUtil.hasAnnotation(this.field, PropIgnore.class)
|| AnnotationUtil.hasAnnotation(this.setter, PropIgnore.class);
}
/**
* 检查字段是否被忽略读通过{@link PropIgnore} 注解完成规则为
* <pre>
* 1. 在字段上有{@link PropIgnore} 注解
* 2. 在getXXX方法上有{@link PropIgnore} 注解
* </pre>
*
* @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
}

View File

@ -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<T> implements Copier<T>, Serializable {
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void beanToMap(Object bean, Map targetMap) {
final Collection<PropDesc> props = BeanUtil.getBeanDesc(bean.getClass()).getProps();
final HashSet<String> 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<br>
* 此方法通过遍历目标Bean的字段从ValueProvider查找对应值
*
* @param valueProvider 值提供器
* @param bean Bean
@ -220,73 +215,38 @@ public class BeanCopier<T> implements Copier<T>, Serializable {
actualEditable = copyOptions.editable;
}
final HashSet<String> ignoreSet = (null != copyOptions.ignoreProperties) ? CollUtil.newHashSet(copyOptions.ignoreProperties) : null;
final Map<String, String> fieldReverseMapping = copyOptions.getReversedMapping();
final Collection<PropDesc> 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<String, String> mapping, String fieldName) {
if (MapUtil.isEmpty(mapping)) {
return fieldName;
}
return ObjectUtil.defaultIfNull(mapping.get(fieldName), fieldName);
prop.setValue(bean, value, copyOptions.ignoreNullValue, copyOptions.ignoreError);
});
}
}

View File

@ -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<String, String> fieldMapping;
/**
* 反向映射表自动生成用于反向查找
*/
private Map<String, String> reversedFieldMapping;
/**
* 字段属性编辑器用于自定义属性转换规则例如驼峰转下划线等
*/
protected Editor<String> fieldNameEditor;
/**
* 是否支持transient关键字修饰和@Transient注解如果支持被修饰的字段或方法对应的字段将被忽略
*/
@ -182,6 +192,19 @@ public class CopyOptions implements Serializable {
return this;
}
/**
* 设置字段属性编辑器用于自定义属性转换规则例如驼峰转下划线等<br>
* 此转换器只针对源端的字段做转换请确认转换后与目标端字段一致
*
* @param fieldNameEditor 字段属性编辑器用于自定义属性转换规则例如驼峰转下划线等
* @return CopyOptions
* @since 5.4.2
*/
public CopyOptions setFieldNameEditor(Editor<String> fieldNameEditor) {
this.fieldNameEditor = fieldNameEditor;
return this;
}
/**
* 是否支持transient关键字修饰和@Transient注解如果支持被修饰的字段或方法对应的字段将被忽略
*
@ -204,13 +227,45 @@ public class CopyOptions implements Serializable {
return this;
}
/**
* 获得映射后的字段名<br>
* 当非反向则根据源字段名获取目标字段名反之根据目标字段名获取源字段名
*
* @param fieldName 字段名
* @param reversed 是否反向映射
* @return 映射后的字段名
*/
protected String getMappedFieldName(String fieldName, boolean reversed){
Map<String, String> 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<String, String> getReversedMapping() {
return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
private Map<String, String> getReversedMapping() {
if(null == this.fieldMapping){
return null;
}
if(null == this.reversedFieldMapping){
reversedFieldMapping = MapUtil.reverse(this.fieldMapping);
}
return reversedFieldMapping;
}
}

View File

@ -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<String> {
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<String> {
final PropDesc sourcePd = getPropDesc(key, null);
// 字段描述不存在或忽略读的情况下表示不存在
return null != sourcePd && false == sourcePd.isIgnoreGet();
return null != sourcePd && false == sourcePd.isReadable(false);
}
/**

View File

@ -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属性排序器<br>
* 支持读取Bean多层次下的属性
@ -53,8 +53,8 @@ public class PropertyComparator<T> implements Comparator<T>, 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);
}

View File

@ -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} 单元测试类
*

View File

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

View File

@ -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<String>, Map<String, Object>
* @param bean Bean对象
*/
private void populateMap(Object bean) {
final Collection<PropDesc> 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();
}
/**