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;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 构造<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;
|
||||
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
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;
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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} 单元测试类
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user