diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28c91769b..ab6e618d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@
* 【core 】 TypeUtil增加getActualType,增加ActualTypeMapperPool类(issue#I1TBWH@Gitee)
* 【extra 】 QRConfig中添加qrVersion属性(pr#1068@Github)
* 【core 】 ArrayUtil增加equals方法
+* 【core 】 BeanDesc增加方法
### Bug修复
* 【core 】 重新整理农历节假日,解决一个pr过来的玩笑导致的问题
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
index 773aca748..ad07b7f73 100644
--- a/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java
@@ -61,6 +61,18 @@ public class AnnotationUtil {
return (null == annotationEle) ? null : toCombination(annotationEle).getAnnotation(annotationType);
}
+ /**
+ * 检查是否包含指定注解指定注解
+ *
+ * @param annotationEle {@link AnnotatedElement},可以是Class、Method、Field、Constructor、ReflectPermission
+ * @param annotationType 注解类型
+ * @return 是否包含指定注解
+ * @since 5.4.2
+ */
+ public static boolean hasAnnotation(AnnotatedElement annotationEle, Class extends Annotation> annotationType) {
+ return null != getAnnotation(annotationEle, annotationType);
+ }
+
/**
* 获取指定注解默认值
* 如果无指定的属性方法返回null
diff --git a/hutool-core/src/main/java/cn/hutool/core/annotation/Ignore.java b/hutool-core/src/main/java/cn/hutool/core/annotation/Ignore.java
new file mode 100644
index 000000000..5c9b05aff
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/annotation/Ignore.java
@@ -0,0 +1,20 @@
+package cn.hutool.core.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 忽略注解,使用此注解的字段等会被忽略,主要用于Bean拷贝、Bean转Map等
+ *
+ * @author Looly
+ * @since 5.4.2
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
+public @interface Ignore {
+
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
index 62fc4d268..74d1e761e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java
@@ -1,6 +1,8 @@
package cn.hutool.core.bean;
import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.annotation.Ignore;
+import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.util.BooleanUtil;
@@ -439,7 +441,7 @@ public class BeanDesc implements Serializable {
}
/**
- * 获取字段值
+ * 获取属性值
* 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
*
* @param bean Bean对象
@@ -455,12 +457,42 @@ public class BeanDesc implements Serializable {
return null;
}
+ /**
+ * 获取属性值,自动转换属性值类型
+ * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
+ *
+ * @param bean Bean对象
+ * @param valueType 返回属性值类型,null表示不转换
+ * @param ignoreError 是否忽略错误,包括转换错误和注入错误
+ * @return this
+ * @since 5.4.2
+ */
+ public Object getValueWithConvert(Object bean, Type valueType, boolean ignoreError) {
+ Object result = null;
+ try {
+ result = getValue(bean);
+ } catch (Exception e) {
+ if (false == ignoreError) {
+ throw new BeanException(e, "Get value of [{}] error!", getFieldName());
+ }
+ }
+
+ if (null != result && null != valueType) {
+ // 尝试将结果转换为目标类型,如果转换失败,返回原类型。
+ final Object convertValue = Convert.convertWithCheck(valueType, result, null, ignoreError);
+ if (null != convertValue) {
+ result = convertValue;
+ }
+ }
+ return result;
+ }
+
/**
* 设置Bean的字段值
* 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值
*
* @param bean Bean对象
- * @param value 值
+ * @param value 值,必须与字段值类型匹配
* @return this
* @since 4.0.5
*/
@@ -473,6 +505,44 @@ public class BeanDesc implements Serializable {
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关键字修饰的
*
@@ -488,13 +558,43 @@ public class BeanDesc implements Serializable {
// 检查注解
if (false == isTransient) {
- isTransient = null != AnnotationUtil.getAnnotation(this.getter, Transient.class);
+ isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class);
}
}
return isTransient;
}
+ /**
+ * 检查字段是否被忽略读,通过{@link Ignore} 注解完成,规则为:
+ *
+ * 1. 在字段上有{@link Ignore} 注解
+ * 2. 在getXXX方法上有{@link Ignore} 注解
+ *
+ *
+ * @return 是否忽略读
+ * @since 5.4.2
+ */
+ public boolean isIgnoreGet() {
+ return AnnotationUtil.hasAnnotation(this.field, Ignore.class)
+ || AnnotationUtil.hasAnnotation(this.getter, Ignore.class);
+ }
+
+ /**
+ * 检查字段是否被忽略写,通过{@link Ignore} 注解完成,规则为:
+ *
+ * 1. 在字段上有{@link Ignore} 注解
+ * 2. 在setXXX方法上有{@link Ignore} 注解
+ *
+ *
+ * @return 是否忽略写
+ * @since 5.4.2
+ */
+ public boolean isIgnoreSet() {
+ return AnnotationUtil.hasAnnotation(this.field, Ignore.class)
+ || AnnotationUtil.hasAnnotation(this.setter, Ignore.class);
+ }
+
//------------------------------------------------------------------------------------ Private method start
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java
index c60999232..e7cf37cc4 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java
@@ -1,6 +1,7 @@
package cn.hutool.core.bean;
import cn.hutool.core.lang.SimpleCache;
+import cn.hutool.core.lang.func.Func0;
/**
* Bean属性缓存
@@ -16,18 +17,11 @@ public enum BeanDescCache {
/**
* 获得属性名和{@link BeanDesc}Map映射
* @param beanClass Bean的类
+ * @param supplier 对象不存在时创建对象的函数
* @return 属性名和{@link BeanDesc}映射
+ * @since 5.4.2
*/
- public BeanDesc getBeanDesc(Class> beanClass){
- return bdCache.get(beanClass);
- }
-
- /**
- * 加入缓存
- * @param beanClass Bean的类
- * @param BeanDesc 属性名和{@link BeanDesc}映射
- */
- public void putBeanDesc(Class> beanClass, BeanDesc BeanDesc){
- bdCache.put(beanClass, BeanDesc);
+ public BeanDesc getBeanDesc(Class> beanClass, Func0 supplier){
+ return bdCache.get(beanClass, supplier);
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
index 4aa6f8047..f7e68e8bc 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
@@ -166,12 +166,7 @@ public class BeanUtil {
* @since 3.1.2
*/
public static BeanDesc getBeanDesc(Class> clazz) {
- BeanDesc beanDesc = BeanDescCache.INSTANCE.getBeanDesc(clazz);
- if (null == beanDesc) {
- beanDesc = new BeanDesc(clazz);
- BeanDescCache.INSTANCE.putBeanDesc(clazz, beanDesc);
- }
- return beanDesc;
+ return BeanDescCache.INSTANCE.getBeanDesc(clazz, ()-> new BeanDesc(clazz));
}
// --------------------------------------------------------------------------------------------------------- PropertyDescriptor
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
index 35d88a75f..756c260f4 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java
@@ -1,12 +1,11 @@
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.copier.provider.BeanValueProvider;
import cn.hutool.core.bean.copier.provider.MapValueProvider;
import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.copier.Copier;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ModifierUtil;
@@ -23,7 +22,14 @@ import java.util.HashSet;
import java.util.Map;
/**
- * Bean拷贝
+ * Bean拷贝,提供:
+ *
+ *
+ * 1. Bean 转 Bean
+ * 2. Bean 转 Map
+ * 3. Map 转 Bean
+ * 4. Map 转 Map
+ *
*
* @author looly
*
@@ -158,36 +164,33 @@ public class BeanCopier implements Copier, Serializable {
final CopyOptions copyOptions = this.copyOptions;
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 e) {
- if (copyOptions.ignoreError) {
- continue;// 忽略反射失败
- } else {
- throw new UtilException(e, "Get value of [{}] error!", prop.getFieldName());
- }
- }
- if (CollUtil.contains(ignoreSet, key)) {
- // 目标属性值被忽略或值提供者无此key时跳过
- continue;
- }
- if (null == value && copyOptions.ignoreNullValue) {
- continue;// 当允许跳过空时,跳过
- }
- if (bean.equals(value)) {
- continue;// 值不能为bean本身,防止循环引用
- }
- targetMap.put(mappingKey(copyOptions.fieldMapping, key), value);
+ // 忽略注解的字段
+ if(prop.isIgnoreGet()){
+ continue;
}
+ key = prop.getFieldName();
+ if (CollUtil.contains(ignoreSet, key)) {
+ // 目标属性值被忽略或值提供者无此key时跳过
+ continue;
+ }
+ try {
+ value = prop.getValue(bean);
+ } catch (Exception e) {
+ if (copyOptions.ignoreError) {
+ continue;// 忽略反射失败
+ } else {
+ throw new BeanException(e, "Get value of [{}] error!", prop.getFieldName());
+ }
+ }
+ if (null == value && copyOptions.ignoreNullValue) {
+ continue;// 当允许跳过空时,跳过
+ }
+ if (bean.equals(value)) {
+ continue;// 值不能为bean本身,防止循环引用
+ }
+ targetMap.put(mappingKey(copyOptions.fieldMapping, key), value);
}
}
@@ -228,6 +231,17 @@ public class BeanCopier implements Copier, Serializable {
// 目标属性值被忽略或值提供者无此key时跳过
continue;
}
+
+ // 在支持情况下,忽略transient修饰(包括修饰和注解)
+ if(copyOptions.isTransientSupport() && prop.isTransient()){
+ continue;
+ }
+
+ // @Ignore修饰的字段和setXXX方法忽略之
+ if(prop.isIgnoreSet()){
+ continue;
+ }
+
final String providerKey = mappingKey(fieldReverseMapping, fieldName);
if (false == valueProvider.containsKey(providerKey)) {
// 无对应值可提供
@@ -241,34 +255,18 @@ public class BeanCopier implements Copier, Serializable {
}
// 获取目标字段真实类型
- Type fieldType = (null == setterMethod) ? TypeUtil.getType(field) : TypeUtil.getFirstParamType(setterMethod);
- fieldType = TypeUtil.getActualType(this.destType ,fieldType);
+ final Type fieldType = TypeUtil.getActualType(this.destType ,prop.getFieldType());
value = valueProvider.value(providerKey, fieldType);
if (null == value && copyOptions.ignoreNullValue) {
continue;// 当允许跳过空时,跳过
}
if (bean == value) {
- continue;// 值不能为bean本身,防止循环引用
+ // 值不能为bean本身,防止循环引用
+ continue;
}
- try {
- // valueProvider在没有对值做转换且当类型不匹配的时候,执行默认转换
- propClass = prop.getFieldClass();
- if (false ==propClass.isInstance(value)) {
- value = Convert.convertWithCheck(propClass, value, null, copyOptions.ignoreError);
- if (null == value && copyOptions.ignoreNullValue) {
- continue;// 当允许跳过空时,跳过
- }
- }
-
- prop.setValue(bean, value);
- } catch (Exception e) {
- if (false ==copyOptions.ignoreError) {
- throw new UtilException(e, "Inject [{}] error!", prop.getFieldName());
- }
- // 忽略注入失败
- }
+ prop.setValueWithConvert(bean, value, copyOptions.ignoreNullValue, copyOptions.ignoreError);
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java
index 7ffebd75c..b588db654 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java
@@ -1,38 +1,54 @@
package cn.hutool.core.bean.copier;
+import cn.hutool.core.map.MapUtil;
+
import java.io.Serializable;
import java.util.Map;
-import cn.hutool.core.map.MapUtil;
-
/**
* 属性拷贝选项
* 包括:
* 1、限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
* 2、是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
* 3、忽略的属性列表,设置一个属性列表,不拷贝这些属性值
- *
+ *
* @author Looly
*/
-public class CopyOptions implements Serializable{
+public class CopyOptions implements Serializable {
private static final long serialVersionUID = 1L;
-
- /** 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类 */
+
+ /**
+ * 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
+ */
protected Class> editable;
- /** 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null */
+ /**
+ * 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
+ */
protected boolean ignoreNullValue;
- /** 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值 */
+ /**
+ * 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
+ */
protected String[] ignoreProperties;
- /** 是否忽略字段注入错误 */
+ /**
+ * 是否忽略字段注入错误
+ */
protected boolean ignoreError;
- /** 是否忽略字段大小写 */
+ /**
+ * 是否忽略字段大小写
+ */
protected boolean ignoreCase;
- /** 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用 */
+ /**
+ * 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
+ */
protected Map fieldMapping;
+ /**
+ * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
+ */
+ private boolean transientSupport = true;
/**
* 创建拷贝选项
- *
+ *
* @return 拷贝选项
*/
public static CopyOptions create() {
@@ -41,9 +57,9 @@ public class CopyOptions implements Serializable{
/**
* 创建拷贝选项
- *
- * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
- * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
+ *
+ * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
+ * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
* @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值
* @return 拷贝选项
*/
@@ -59,9 +75,9 @@ public class CopyOptions implements Serializable{
/**
* 构造拷贝选项
- *
- * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
- * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
+ *
+ * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
+ * @param ignoreNullValue 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
* @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
*/
public CopyOptions(Class> editable, boolean ignoreNullValue, String... ignoreProperties) {
@@ -72,7 +88,7 @@ public class CopyOptions implements Serializable{
/**
* 设置限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
- *
+ *
* @param editable 限制的类或接口
* @return CopyOptions
*/
@@ -83,7 +99,7 @@ public class CopyOptions implements Serializable{
/**
* 设置是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
- *
+ *
* @param ignoreNullVall 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
* @return CopyOptions
*/
@@ -91,10 +107,10 @@ public class CopyOptions implements Serializable{
this.ignoreNullValue = ignoreNullVall;
return this;
}
-
+
/**
* 设置忽略空值,当源对象的值为null时,忽略而不注入此值
- *
+ *
* @return CopyOptions
* @since 4.5.7
*/
@@ -104,7 +120,7 @@ public class CopyOptions implements Serializable{
/**
* 设置忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
- *
+ *
* @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
* @return CopyOptions
*/
@@ -115,7 +131,7 @@ public class CopyOptions implements Serializable{
/**
* 设置是否忽略字段的注入错误
- *
+ *
* @param ignoreError 是否忽略注入错误
* @return CopyOptions
*/
@@ -123,20 +139,20 @@ public class CopyOptions implements Serializable{
this.ignoreError = ignoreError;
return this;
}
-
+
/**
* 设置忽略字段的注入错误
- *
+ *
* @return CopyOptions
* @since 4.5.7
*/
public CopyOptions ignoreError() {
return setIgnoreError(true);
}
-
+
/**
* 设置是否忽略字段的大小写
- *
+ *
* @param ignoreCase 是否忽略大小写
* @return CopyOptions
*/
@@ -144,10 +160,10 @@ public class CopyOptions implements Serializable{
this.ignoreCase = ignoreCase;
return this;
}
-
+
/**
* 设置忽略字段的大小写
- *
+ *
* @return CopyOptions
* @since 4.5.7
*/
@@ -157,7 +173,7 @@ public class CopyOptions implements Serializable{
/**
* 设置拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
- *
+ *
* @param fieldMapping 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
* @return CopyOptions
*/
@@ -165,13 +181,36 @@ public class CopyOptions implements Serializable{
this.fieldMapping = fieldMapping;
return this;
}
-
+
+ /**
+ * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
+ *
+ * @return 是否支持
+ * @since 5.4.2
+ */
+ public boolean isTransientSupport() {
+ return this.transientSupport;
+ }
+
+ /**
+ * 设置是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
+ *
+ * @param transientSupport 是否支持
+ * @return this
+ * @since 5.4.2
+ */
+ public CopyOptions setTransientSupport(boolean transientSupport) {
+ this.transientSupport = transientSupport;
+ return this;
+ }
+
/**
* 获取反转之后的映射
+ *
* @return 反转映射
* @since 4.1.10
*/
protected Map getReversedMapping() {
- return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
+ return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java
index 38bab46ed..d92c82641 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java
@@ -3,11 +3,8 @@ 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.copier.ValueProvider;
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.util.StrUtil;
-import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
@@ -46,29 +43,32 @@ public class BeanValueProvider implements ValueProvider {
Object result = null;
if (null != sourcePd) {
- final Method getter = sourcePd.getGetter();
- if (null != getter) {
- try {
- result = getter.invoke(source);
- } catch (Exception e) {
- if (false == ignoreError) {
- throw new UtilException(e, "Inject [{}] error!", key);
- }
- }
-
- // 尝试将结果转换为目标类型,如果转换失败,返回原类型。
- final Object convertValue = Convert.convertWithCheck(valueType,result, null, ignoreError);
- if(null != convertValue){
- result = convertValue;
- }
- }
+ result = sourcePd.getValueWithConvert(this.source, valueType, this.ignoreError);
}
return result;
}
@Override
public boolean containsKey(String key) {
- return sourcePdMap.containsKey(key) || sourcePdMap.containsKey(StrUtil.upperFirstAndAddPre(key, "is"));
+ PropDesc sourcePd = getPropDesc(key);
+
+ // 字段描述不存在或忽略读的情况下,表示不存在
+ return null != sourcePd && false == sourcePd.isIgnoreGet();
}
+ /**
+ * 获得属性描述
+ *
+ * @param key 字段名
+ * @return 属性描述
+ */
+ private PropDesc getPropDesc(String key){
+ PropDesc sourcePd = sourcePdMap.get(key);
+ if(null == sourcePd) {
+ //boolean类型字段字段名支持两种方式
+ sourcePd = sourcePdMap.get(StrUtil.upperFirstAndAddPre(key, "is"));
+ }
+
+ return sourcePd;
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java
index 2007776b6..3ec4851df 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java
@@ -9,10 +9,10 @@ import java.lang.reflect.Type;
import java.util.Map;
/**
- * Map值提供者
- *
- * @author looly
+ * Map值提供者,支持驼峰和下划线的key兼容。
+ * 假设目标属性为firstName,则Map中为firstName或first_name都可以对应到值。
*
+ * @author looly
*/
public class MapValueProvider implements ValueProvider {
@@ -22,7 +22,7 @@ public class MapValueProvider implements ValueProvider {
/**
* 构造
*
- * @param map Map
+ * @param map Map
* @param ignoreCase 是否忽略key的大小写
*/
public MapValueProvider(Map, ?> map, boolean ignoreCase) {
@@ -31,17 +31,17 @@ public class MapValueProvider implements ValueProvider {
/**
* 构造
- *
- * @param map Map
- * @param ignoreCase 是否忽略key的大小写
+ *
+ * @param map Map
+ * @param ignoreCase 是否忽略key的大小写
* @param ignoreError 是否忽略错误
* @since 5.3.2
*/
public MapValueProvider(Map, ?> map, boolean ignoreCase, boolean ignoreError) {
- if(false == ignoreCase || map instanceof CaseInsensitiveMap) {
+ if (false == ignoreCase || map instanceof CaseInsensitiveMap) {
//不忽略大小写或者提供的Map本身为CaseInsensitiveMap则无需转换
this.map = map;
- }else {
+ } else {
//转换为大小写不敏感的Map
this.map = new CaseInsensitiveMap<>(map);
}
@@ -51,7 +51,7 @@ public class MapValueProvider implements ValueProvider {
@Override
public Object value(String key, Type valueType) {
Object value = map.get(key);
- if(null == value) {
+ if (null == value) {
//检查下划线模式
value = map.get(StrUtil.toUnderlineCase(key));
}
@@ -61,9 +61,9 @@ public class MapValueProvider implements ValueProvider {
@Override
public boolean containsKey(String key) {
- if(map.containsKey(key)) {
+ if (map.containsKey(key)) {
return true;
- }else {
+ } else {
//检查下划线模式
return map.containsKey(StrUtil.toUnderlineCase(key));
}
diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java
index 7c1d49a0c..fd558ae82 100644
--- a/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java
+++ b/hutool-json/src/main/java/cn/hutool/json/JSONConfig.java
@@ -32,9 +32,9 @@ public class JSONConfig implements Serializable {
*/
private boolean ignoreNullValue = true;
/**
- * 是否忽略transient关键字修饰的字段
+ * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
*/
- private boolean ignoreTransient = true;
+ private boolean transientSupport = true;
/**
* 创建默认的配置项
@@ -150,9 +150,11 @@ public class JSONConfig implements Serializable {
*
* @return 是否忽略transient关键字修饰的字段
* @since 5.3.11
+ * @deprecated 此方法名称有二义性,请使用{@link #isTransientSupport()}
*/
+ @Deprecated
public boolean isIgnoreTransient() {
- return this.ignoreTransient;
+ return isTransientSupport();
}
/**
@@ -161,9 +163,32 @@ public class JSONConfig implements Serializable {
* @param ignoreTransient 是否忽略transient关键字修饰的字段
* @return this
* @since 5.3.11
+ * @deprecated 此方法名称有二义性,请使用{@link #setTransientSupport(boolean)}
*/
+ @Deprecated
public JSONConfig setIgnoreTransient(boolean ignoreTransient) {
- this.ignoreTransient = ignoreTransient;
+ return setTransientSupport(ignoreTransient);
+ }
+
+ /**
+ * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
+ *
+ * @return 是否支持
+ * @since 5.4.2
+ */
+ public boolean isTransientSupport() {
+ return this.transientSupport;
+ }
+
+ /**
+ * 设置是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
+ *
+ * @param transientSupport 是否支持
+ * @return this
+ * @since 5.4.2
+ */
+ public JSONConfig setTransientSupport(boolean transientSupport) {
+ this.transientSupport = transientSupport;
return this;
}
}
diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java
index 580a7c417..011cbef69 100644
--- a/hutool-json/src/main/java/cn/hutool/json/JSONObject.java
+++ b/hutool-json/src/main/java/cn/hutool/json/JSONObject.java
@@ -625,7 +625,7 @@ public class JSONObject implements JSON, JSONGetter, Map
Method getter;
Object value;
for (PropDesc prop : props) {
- if(this.config.isIgnoreTransient() && prop.isTransient()){
+ if(this.config.isTransientSupport() && prop.isTransient()){
// 忽略Transient字段和方法
continue;
}