mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix bean
This commit is contained in:
parent
5aa10c06ee
commit
0c3a1ea298
@ -1,22 +1,15 @@
|
|||||||
package cn.hutool.core.bean;
|
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.lang.Assert;
|
||||||
import cn.hutool.core.map.CaseInsensitiveMap;
|
import cn.hutool.core.map.CaseInsensitiveMap;
|
||||||
import cn.hutool.core.util.BooleanUtil;
|
import cn.hutool.core.util.BooleanUtil;
|
||||||
import cn.hutool.core.util.ClassUtil;
|
|
||||||
import cn.hutool.core.util.ModifierUtil;
|
import cn.hutool.core.util.ModifierUtil;
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.core.util.TypeUtil;
|
|
||||||
|
|
||||||
import java.beans.Transient;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -333,305 +326,4 @@ public class BeanDesc implements Serializable {
|
|||||||
return methodName.equals("set" + fieldName);
|
return methodName.equals("set" + fieldName);
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------------------ Private method end
|
// ------------------------------------------------------------------------------------------------------ Private method end
|
||||||
|
|
||||||
/**
|
|
||||||
* 属性描述,包括了字段、getter、setter和相应的方法执行
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package cn.hutool.core.bean;
|
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.BeanCopier;
|
||||||
import cn.hutool.core.bean.copier.CopyOptions;
|
import cn.hutool.core.bean.copier.CopyOptions;
|
||||||
import cn.hutool.core.bean.copier.ValueProvider;
|
import cn.hutool.core.bean.copier.ValueProvider;
|
||||||
@ -29,6 +28,7 @@ import java.util.HashMap;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean工具类
|
* Bean工具类
|
||||||
@ -169,6 +169,17 @@ public class BeanUtil {
|
|||||||
return BeanDescCache.INSTANCE.getBeanDesc(clazz, ()-> new BeanDesc(clazz));
|
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
|
// --------------------------------------------------------------------------------------------------------- PropertyDescriptor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -616,32 +627,11 @@ public class BeanUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Collection<PropDesc> props = BeanUtil.getBeanDesc(bean.getClass()).getProps();
|
return BeanCopier.create(bean, targetMap,
|
||||||
|
CopyOptions.create()
|
||||||
String key;
|
.setIgnoreNullValue(ignoreNullValue)
|
||||||
Method getter;
|
.setFieldNameEditor(keyEditor)
|
||||||
Object value;
|
).copy();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------------- copyProperties
|
// --------------------------------------------------------------------------------------------- copyProperties
|
||||||
|
@ -82,7 +82,7 @@ public class DynaBean extends CloneSupport<DynaBean> implements Serializable {
|
|||||||
if (Map.class.isAssignableFrom(beanClass)) {
|
if (Map.class.isAssignableFrom(beanClass)) {
|
||||||
return (T) ((Map<?, ?>) bean).get(fieldName);
|
return (T) ((Map<?, ?>) bean).get(fieldName);
|
||||||
} else {
|
} else {
|
||||||
final BeanDesc.PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||||
if(null == prop){
|
if(null == prop){
|
||||||
throw new BeanException("No public field or get method for {}", fieldName);
|
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)) {
|
if (Map.class.isAssignableFrom(beanClass)) {
|
||||||
((Map) bean).put(fieldName, value);
|
((Map) bean).put(fieldName, value);
|
||||||
} else {
|
} else {
|
||||||
final BeanDesc.PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
final PropDesc prop = BeanUtil.getBeanDesc(beanClass).getProp(fieldName);
|
||||||
if(null == prop){
|
if(null == prop){
|
||||||
throw new BeanException("No public field or set method for {}", fieldName);
|
throw new BeanException("No public field or set method for {}", fieldName);
|
||||||
}
|
}
|
||||||
|
382
hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java
Normal file
382
hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java
Normal 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性描述,包括了字段、getter、setter和相应的方法执行
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package cn.hutool.core.bean.copier;
|
package cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanDesc.PropDesc;
|
|
||||||
import cn.hutool.core.bean.BeanException;
|
import cn.hutool.core.bean.BeanException;
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.bean.DynaBean;
|
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.bean.copier.provider.MapValueProvider;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.copier.Copier;
|
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.StrUtil;
|
||||||
import cn.hutool.core.util.TypeUtil;
|
import cn.hutool.core.util.TypeUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -164,43 +157,45 @@ public class BeanCopier<T> implements Copier<T>, Serializable {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
private void beanToMap(Object bean, Map targetMap) {
|
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 HashSet<String> ignoreSet = (null != copyOptions.ignoreProperties) ? CollUtil.newHashSet(copyOptions.ignoreProperties) : null;
|
||||||
final CopyOptions copyOptions = this.copyOptions;
|
final CopyOptions copyOptions = this.copyOptions;
|
||||||
|
|
||||||
String key;
|
BeanUtil.descForEach(bean.getClass(), (prop)->{
|
||||||
Object value;
|
if(false == prop.isReadable(copyOptions.isTransientSupport())){
|
||||||
for (PropDesc prop : props) {
|
// 忽略的属性跳过之
|
||||||
// 忽略注解的字段
|
return;
|
||||||
if(prop.isIgnoreGet()){
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
key = prop.getFieldName();
|
String key = prop.getFieldName();
|
||||||
if (CollUtil.contains(ignoreSet, key)) {
|
if (CollUtil.contains(ignoreSet, key)) {
|
||||||
// 目标属性值被忽略或值提供者无此key时跳过
|
// 目标属性值被忽略或值提供者无此key时跳过
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object value;
|
||||||
try {
|
try {
|
||||||
value = prop.getValue(bean);
|
value = prop.getValue(bean);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (copyOptions.ignoreError) {
|
if (copyOptions.ignoreError) {
|
||||||
continue;// 忽略反射失败
|
return;// 忽略反射失败
|
||||||
} else {
|
} else {
|
||||||
throw new BeanException(e, "Get value of [{}] error!", prop.getFieldName());
|
throw new BeanException(e, "Get value of [{}] error!", prop.getFieldName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (null == value && copyOptions.ignoreNullValue) {
|
if ((null == value && copyOptions.ignoreNullValue) || bean == value) {
|
||||||
continue;// 当允许跳过空时,跳过
|
// 当允许跳过空时,跳过
|
||||||
}
|
//值不能为bean本身,防止循环引用,此类也跳过
|
||||||
if (bean.equals(value)) {
|
return;
|
||||||
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 valueProvider 值提供器
|
||||||
* @param bean Bean
|
* @param bean Bean
|
||||||
@ -220,73 +215,38 @@ public class BeanCopier<T> implements Copier<T>, Serializable {
|
|||||||
actualEditable = copyOptions.editable;
|
actualEditable = copyOptions.editable;
|
||||||
}
|
}
|
||||||
final HashSet<String> ignoreSet = (null != copyOptions.ignoreProperties) ? CollUtil.newHashSet(copyOptions.ignoreProperties) : null;
|
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();
|
BeanUtil.descForEach(actualEditable, (prop)->{
|
||||||
Field field;
|
if(false == prop.isWritable(this.copyOptions.isTransientSupport())){
|
||||||
String fieldName;
|
// 字段不可写,跳过之
|
||||||
Object value;
|
return;
|
||||||
Method setterMethod;
|
}
|
||||||
Class<?> propClass;
|
|
||||||
for (PropDesc prop : props) {
|
// 检查属性名
|
||||||
// 获取值
|
String fieldName = prop.getFieldName();
|
||||||
field = prop.getField();
|
|
||||||
fieldName = prop.getFieldName();
|
|
||||||
if (CollUtil.contains(ignoreSet, fieldName)) {
|
if (CollUtil.contains(ignoreSet, fieldName)) {
|
||||||
// 目标属性值被忽略或值提供者无此key时跳过
|
// 目标属性值被忽略或值提供者无此key时跳过
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在支持情况下,忽略transient修饰(包括修饰和注解)
|
final String providerKey = copyOptions.getMappedFieldName(fieldName, true);
|
||||||
if(copyOptions.isTransientSupport() && prop.isTransient()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Ignore修饰的字段和setXXX方法忽略之
|
|
||||||
if(prop.isIgnoreSet()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String providerKey = mappingKey(fieldReverseMapping, fieldName);
|
|
||||||
if (false == valueProvider.containsKey(providerKey)) {
|
if (false == valueProvider.containsKey(providerKey)) {
|
||||||
// 无对应值可提供
|
// 无对应值可提供
|
||||||
continue;
|
return;
|
||||||
}
|
|
||||||
setterMethod = prop.getSetter();
|
|
||||||
if (null == setterMethod && false == ModifierUtil.isPublic(field)) {
|
|
||||||
// Setter方法不存在或者字段为非public跳过
|
|
||||||
//5.1.0新增支持public字段注入支持
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取目标字段真实类型
|
// 获取目标字段真实类型
|
||||||
final Type fieldType = TypeUtil.getActualType(this.destType ,prop.getFieldType());
|
final Type fieldType = TypeUtil.getActualType(this.destType ,prop.getFieldType());
|
||||||
|
|
||||||
value = valueProvider.value(providerKey, fieldType);
|
// 获取属性值
|
||||||
if (null == value && copyOptions.ignoreNullValue) {
|
Object value = valueProvider.value(providerKey, fieldType);
|
||||||
continue;// 当允许跳过空时,跳过
|
if ((null == value && copyOptions.ignoreNullValue) || bean == value) {
|
||||||
}
|
// 当允许跳过空时,跳过
|
||||||
if (bean == value) {
|
|
||||||
// 值不能为bean本身,防止循环引用
|
// 值不能为bean本身,防止循环引用
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
prop.setValueWithConvert(bean, value, copyOptions.ignoreNullValue, copyOptions.ignoreError);
|
prop.setValue(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package cn.hutool.core.bean.copier;
|
package cn.hutool.core.bean.copier;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Editor;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -41,6 +43,14 @@ public class CopyOptions implements Serializable {
|
|||||||
* 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
|
* 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
|
||||||
*/
|
*/
|
||||||
protected Map<String, String> fieldMapping;
|
protected Map<String, String> fieldMapping;
|
||||||
|
/**
|
||||||
|
* 反向映射表,自动生成用于反向查找
|
||||||
|
*/
|
||||||
|
private Map<String, String> reversedFieldMapping;
|
||||||
|
/**
|
||||||
|
* 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
|
||||||
|
*/
|
||||||
|
protected Editor<String> fieldNameEditor;
|
||||||
/**
|
/**
|
||||||
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
||||||
*/
|
*/
|
||||||
@ -182,6 +192,19 @@ public class CopyOptions implements Serializable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等<br>
|
||||||
|
* 此转换器只针对源端的字段做转换,请确认转换后与目标端字段一致
|
||||||
|
*
|
||||||
|
* @param fieldNameEditor 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
|
||||||
|
* @return CopyOptions
|
||||||
|
* @since 5.4.2
|
||||||
|
*/
|
||||||
|
public CopyOptions setFieldNameEditor(Editor<String> fieldNameEditor) {
|
||||||
|
this.fieldNameEditor = fieldNameEditor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
* 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
|
||||||
*
|
*
|
||||||
@ -204,13 +227,45 @@ public class CopyOptions implements Serializable {
|
|||||||
return this;
|
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 反转映射
|
* @return 反转映射
|
||||||
* @since 4.1.10
|
* @since 4.1.10
|
||||||
*/
|
*/
|
||||||
protected Map<String, String> getReversedMapping() {
|
private Map<String, String> getReversedMapping() {
|
||||||
return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
|
if(null == this.fieldMapping){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(null == this.reversedFieldMapping){
|
||||||
|
reversedFieldMapping = MapUtil.reverse(this.fieldMapping);
|
||||||
|
}
|
||||||
|
return reversedFieldMapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cn.hutool.core.bean.copier.provider;
|
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.BeanUtil;
|
||||||
|
import cn.hutool.core.bean.PropDesc;
|
||||||
import cn.hutool.core.bean.copier.ValueProvider;
|
import cn.hutool.core.bean.copier.ValueProvider;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ public class BeanValueProvider implements ValueProvider<String> {
|
|||||||
|
|
||||||
Object result = null;
|
Object result = null;
|
||||||
if (null != sourcePd) {
|
if (null != sourcePd) {
|
||||||
result = sourcePd.getValueWithConvert(this.source, valueType, this.ignoreError);
|
result = sourcePd.getValue(this.source, valueType, this.ignoreError);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ public class BeanValueProvider implements ValueProvider<String> {
|
|||||||
final PropDesc sourcePd = getPropDesc(key, null);
|
final PropDesc sourcePd = getPropDesc(key, null);
|
||||||
|
|
||||||
// 字段描述不存在或忽略读的情况下,表示不存在
|
// 字段描述不存在或忽略读的情况下,表示不存在
|
||||||
return null != sourcePd && false == sourcePd.isIgnoreGet();
|
return null != sourcePd && false == sourcePd.isReadable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package cn.hutool.core.comparator;
|
package cn.hutool.core.comparator;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bean属性排序器<br>
|
* Bean属性排序器<br>
|
||||||
* 支持读取Bean多层次下的属性
|
* 支持读取Bean多层次下的属性
|
||||||
@ -53,8 +53,8 @@ public class PropertyComparator<T> implements Comparator<T>, Serializable {
|
|||||||
Comparable<?> v1;
|
Comparable<?> v1;
|
||||||
Comparable<?> v2;
|
Comparable<?> v2;
|
||||||
try {
|
try {
|
||||||
v1 = (Comparable<?>) BeanUtil.getProperty(o1, property);
|
v1 = BeanUtil.getProperty(o1, property);
|
||||||
v2 = (Comparable<?>) BeanUtil.getProperty(o2, property);
|
v2 = BeanUtil.getProperty(o2, property);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ComparatorException(e);
|
throw new ComparatorException(e);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@ package cn.hutool.core.bean;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanDesc.PropDesc;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanDesc} 单元测试类
|
* {@link BeanDesc} 单元测试类
|
||||||
*
|
*
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cn.hutool.db.handler;
|
package cn.hutool.db.handler;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanDesc.PropDesc;
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.bean.PropDesc;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package cn.hutool.json;
|
package cn.hutool.json;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanDesc.PropDesc;
|
|
||||||
import cn.hutool.core.bean.BeanPath;
|
import cn.hutool.core.bean.BeanPath;
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
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.collection.CollectionUtil;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.map.CaseInsensitiveLinkedMap;
|
import cn.hutool.core.map.CaseInsensitiveLinkedMap;
|
||||||
@ -19,7 +20,6 @@ import cn.hutool.json.serialize.JSONSerializer;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -620,41 +620,12 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
|
|||||||
* @param bean Bean对象
|
* @param bean Bean对象
|
||||||
*/
|
*/
|
||||||
private void populateMap(Object bean) {
|
private void populateMap(Object bean) {
|
||||||
final Collection<PropDesc> props = BeanUtil.getBeanDesc(bean.getClass()).getProps();
|
BeanCopier.create(bean, this,
|
||||||
|
CopyOptions.create()
|
||||||
Method getter;
|
.setIgnoreCase(config.isIgnoreCase())
|
||||||
Object value;
|
.setIgnoreError(true)
|
||||||
for (PropDesc prop : props) {
|
.setIgnoreNullValue(config.isIgnoreNullValue())
|
||||||
if(this.config.isTransientSupport() && prop.isTransient()){
|
).copy();
|
||||||
// 忽略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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user