Browse Source

add methods

Looly 5 years ago
parent
commit
46764f02b8

+ 1 - 0
CHANGELOG.md

@@ -18,6 +18,7 @@
 * 【core  】     TypeUtil增加getActualType,增加ActualTypeMapperPool类(issue#I1TBWH@Gitee)
 * 【core  】     TypeUtil增加getActualType,增加ActualTypeMapperPool类(issue#I1TBWH@Gitee)
 * 【extra 】     QRConfig中添加qrVersion属性(pr#1068@Github)
 * 【extra 】     QRConfig中添加qrVersion属性(pr#1068@Github)
 * 【core  】     ArrayUtil增加equals方法
 * 【core  】     ArrayUtil增加equals方法
+* 【core  】     BeanDesc增加方法
 
 
 ### Bug修复
 ### Bug修复
 * 【core  】     重新整理农历节假日,解决一个pr过来的玩笑导致的问题
 * 【core  】     重新整理农历节假日,解决一个pr过来的玩笑导致的问题

+ 12 - 0
hutool-core/src/main/java/cn/hutool/core/annotation/AnnotationUtil.java

@@ -62,6 +62,18 @@ public class AnnotationUtil {
 	}
 	}
 
 
 	/**
 	/**
+	 * 检查是否包含指定注解指定注解
+	 *
+	 * @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);
+	}
+
+	/**
 	 * 获取指定注解默认值<br>
 	 * 获取指定注解默认值<br>
 	 * 如果无指定的属性方法返回null
 	 * 如果无指定的属性方法返回null
 	 *
 	 *

+ 20 - 0
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 {
+
+}

+ 103 - 3
hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java

@@ -1,6 +1,8 @@
 package cn.hutool.core.bean;
 package cn.hutool.core.bean;
 
 
 import cn.hutool.core.annotation.AnnotationUtil;
 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.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;
@@ -439,7 +441,7 @@ public class BeanDesc implements Serializable {
 		}
 		}
 
 
 		/**
 		/**
-		 * 获取字段值<br>
+		 * 获取属性值<br>
 		 * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
 		 * 首先调用字段对应的Getter方法获取值,如果Getter方法不存在,则判断字段如果为public,则直接获取字段值
 		 *
 		 *
 		 * @param bean Bean对象
 		 * @param bean Bean对象
@@ -456,11 +458,41 @@ public class BeanDesc implements Serializable {
 		}
 		}
 
 
 		/**
 		/**
+		 * 获取属性值,自动转换属性值类型<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>
 		 * 设置Bean的字段值<br>
 		 * 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值
 		 * 首先调用字段对应的Setter方法,如果Setter方法不存在,则判断字段如果为public,则直接赋值字段值
 		 *
 		 *
 		 * @param bean  Bean对象
 		 * @param bean  Bean对象
-		 * @param value 值
+		 * @param value 值,必须与字段值类型匹配
 		 * @return this
 		 * @return this
 		 * @since 4.0.5
 		 * @since 4.0.5
 		 */
 		 */
@@ -474,6 +506,44 @@ public class BeanDesc implements Serializable {
 		}
 		}
 
 
 		/**
 		/**
+		 * 设置属性值,可以自动转换字段类型为目标类型
+		 *
+		 * @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关键字修饰的
 		 * 字段和Getter方法是否为Transient关键字修饰的
 		 *
 		 *
 		 * @return 是否为Transient关键字修饰的
 		 * @return 是否为Transient关键字修饰的
@@ -488,13 +558,43 @@ public class BeanDesc implements Serializable {
 
 
 				// 检查注解
 				// 检查注解
 				if (false == isTransient) {
 				if (false == isTransient) {
-					isTransient = null != AnnotationUtil.getAnnotation(this.getter, Transient.class);
+					isTransient = AnnotationUtil.hasAnnotation(this.getter, Transient.class);
 				}
 				}
 			}
 			}
 
 
 			return isTransient;
 			return isTransient;
 		}
 		}
 
 
+		/**
+		 * 检查字段是否被忽略读,通过{@link Ignore} 注解完成,规则为:
+		 * <pre>
+		 *     1. 在字段上有{@link Ignore} 注解
+		 *     2. 在getXXX方法上有{@link Ignore} 注解
+		 * </pre>
+		 *
+		 * @return 是否忽略读
+		 * @since 5.4.2
+		 */
+		public boolean isIgnoreGet() {
+			return AnnotationUtil.hasAnnotation(this.field, Ignore.class)
+					|| AnnotationUtil.hasAnnotation(this.getter, Ignore.class);
+		}
+
+		/**
+		 * 检查字段是否被忽略写,通过{@link Ignore} 注解完成,规则为:
+		 * <pre>
+		 *     1. 在字段上有{@link Ignore} 注解
+		 *     2. 在setXXX方法上有{@link Ignore} 注解
+		 * </pre>
+		 *
+		 * @return 是否忽略写
+		 * @since 5.4.2
+		 */
+		public boolean isIgnoreSet() {
+			return AnnotationUtil.hasAnnotation(this.field, Ignore.class)
+					|| AnnotationUtil.hasAnnotation(this.setter, Ignore.class);
+		}
+
 		//------------------------------------------------------------------------------------ Private method start
 		//------------------------------------------------------------------------------------ Private method start
 
 
 		/**
 		/**

+ 5 - 11
hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java

@@ -1,6 +1,7 @@
 package cn.hutool.core.bean;
 package cn.hutool.core.bean;
 
 
 import cn.hutool.core.lang.SimpleCache;
 import cn.hutool.core.lang.SimpleCache;
+import cn.hutool.core.lang.func.Func0;
 
 
 /**
 /**
  * Bean属性缓存<br>
  * Bean属性缓存<br>
@@ -16,18 +17,11 @@ public enum BeanDescCache {
 	/**
 	/**
 	 * 获得属性名和{@link BeanDesc}Map映射
 	 * 获得属性名和{@link BeanDesc}Map映射
 	 * @param beanClass Bean的类
 	 * @param beanClass Bean的类
+	 * @param supplier 对象不存在时创建对象的函数
 	 * @return 属性名和{@link BeanDesc}映射
 	 * @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<BeanDesc> supplier){
+		return bdCache.get(beanClass, supplier);
 	}
 	}
 }
 }

+ 1 - 6
hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java

@@ -166,12 +166,7 @@ public class BeanUtil {
 	 * @since 3.1.2
 	 * @since 3.1.2
 	 */
 	 */
 	public static BeanDesc getBeanDesc(Class<?> clazz) {
 	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
 	// --------------------------------------------------------------------------------------------------------- PropertyDescriptor

+ 46 - 48
hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java

@@ -1,12 +1,11 @@
 package cn.hutool.core.bean.copier;
 package cn.hutool.core.bean.copier;
 
 
 import cn.hutool.core.bean.BeanDesc.PropDesc;
 import cn.hutool.core.bean.BeanDesc.PropDesc;
+import cn.hutool.core.bean.BeanException;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.copier.provider.BeanValueProvider;
 import cn.hutool.core.bean.copier.provider.BeanValueProvider;
 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.convert.Convert;
-import cn.hutool.core.exceptions.UtilException;
 import cn.hutool.core.lang.copier.Copier;
 import cn.hutool.core.lang.copier.Copier;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ModifierUtil;
 import cn.hutool.core.util.ModifierUtil;
@@ -23,7 +22,14 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
- * Bean拷贝
+ * Bean拷贝,提供:
+ *
+ * <pre>
+ *     1. Bean 转 Bean
+ *     2. Bean 转 Map
+ *     3. Map  转 Bean
+ *     4. Map  转 Map
+ * </pre>
  * 
  * 
  * @author looly
  * @author looly
  *
  *
@@ -158,36 +164,33 @@ public class BeanCopier<T> implements Copier<T>, Serializable {
 		final CopyOptions copyOptions = this.copyOptions;
 		final CopyOptions copyOptions = this.copyOptions;
 
 
 		String key;
 		String key;
-		Method getter;
 		Object value;
 		Object value;
 		for (PropDesc prop : props) {
 		for (PropDesc prop : props) {
+			// 忽略注解的字段
+			if(prop.isIgnoreGet()){
+				continue;
+			}
 			key = prop.getFieldName();
 			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本身,防止循环引用
+			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());
 				}
 				}
-				targetMap.put(mappingKey(copyOptions.fieldMapping, key), value);
 			}
 			}
+			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<T> implements Copier<T>, Serializable {
 				// 目标属性值被忽略或值提供者无此key时跳过
 				// 目标属性值被忽略或值提供者无此key时跳过
 				continue;
 				continue;
 			}
 			}
+
+			// 在支持情况下,忽略transient修饰(包括修饰和注解)
+			if(copyOptions.isTransientSupport() && prop.isTransient()){
+				continue;
+			}
+
+			// @Ignore修饰的字段和setXXX方法忽略之
+			if(prop.isIgnoreSet()){
+				continue;
+			}
+
 			final String providerKey = mappingKey(fieldReverseMapping, fieldName);
 			final String providerKey = mappingKey(fieldReverseMapping, fieldName);
 			if (false == valueProvider.containsKey(providerKey)) {
 			if (false == valueProvider.containsKey(providerKey)) {
 				// 无对应值可提供
 				// 无对应值可提供
@@ -241,34 +255,18 @@ public class BeanCopier<T> implements Copier<T>, 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);
 			value = valueProvider.value(providerKey, fieldType);
 			if (null == value && copyOptions.ignoreNullValue) {
 			if (null == value && copyOptions.ignoreNullValue) {
 				continue;// 当允许跳过空时,跳过
 				continue;// 当允许跳过空时,跳过
 			}
 			}
 			if (bean == value) {
 			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);
 		}
 		}
 	}
 	}
 
 

+ 72 - 33
hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java

@@ -1,38 +1,54 @@
 package cn.hutool.core.bean.copier;
 package cn.hutool.core.bean.copier;
 
 
+import cn.hutool.core.map.MapUtil;
+
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.Map;
 import java.util.Map;
 
 
-import cn.hutool.core.map.MapUtil;
-
 /**
 /**
  * 属性拷贝选项<br>
  * 属性拷贝选项<br>
  * 包括:<br>
  * 包括:<br>
  * 1、限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类<br>
  * 1、限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类<br>
  * 2、是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null<br>
  * 2、是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null<br>
  * 3、忽略的属性列表,设置一个属性列表,不拷贝这些属性值<br>
  * 3、忽略的属性列表,设置一个属性列表,不拷贝这些属性值<br>
- * 
+ *
  * @author Looly
  * @author Looly
  */
  */
-public class CopyOptions implements Serializable{
+public class CopyOptions implements Serializable {
 	private static final long serialVersionUID = 1L;
 	private static final long serialVersionUID = 1L;
-	
-	/** 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类 */
+
+	/**
+	 * 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父类
+	 */
 	protected Class<?> editable;
 	protected Class<?> editable;
-	/** 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null */
+	/**
+	 * 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
+	 */
 	protected boolean ignoreNullValue;
 	protected boolean ignoreNullValue;
-	/** 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值 */
+	/**
+	 * 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
+	 */
 	protected String[] ignoreProperties;
 	protected String[] ignoreProperties;
-	/** 是否忽略字段注入错误 */
+	/**
+	 * 是否忽略字段注入错误
+	 */
 	protected boolean ignoreError;
 	protected boolean ignoreError;
-	/** 是否忽略字段大小写 */
+	/**
+	 * 是否忽略字段大小写
+	 */
 	protected boolean ignoreCase;
 	protected boolean ignoreCase;
-	/** 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用 */
+	/**
+	 * 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
+	 */
 	protected Map<String, String> fieldMapping;
 	protected Map<String, String> fieldMapping;
+	/**
+	 * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
+	 */
+	private boolean transientSupport = true;
 
 
 	/**
 	/**
 	 * 创建拷贝选项
 	 * 创建拷贝选项
-	 * 
+	 *
 	 * @return 拷贝选项
 	 * @return 拷贝选项
 	 */
 	 */
 	public static CopyOptions create() {
 	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 忽略的属性列表,设置一个属性列表,不拷贝这些属性值
 	 * @param ignoreProperties 忽略的属性列表,设置一个属性列表,不拷贝这些属性值
 	 * @return 拷贝选项
 	 * @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 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
 	 * @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
 	 */
 	 */
 	public CopyOptions(Class<?> editable, boolean ignoreNullValue, String... ignoreProperties) {
 	public CopyOptions(Class<?> editable, boolean ignoreNullValue, String... ignoreProperties) {
@@ -72,7 +88,7 @@ public class CopyOptions implements Serializable{
 
 
 	/**
 	/**
 	 * 设置限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
 	 * 设置限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性
-	 * 
+	 *
 	 * @param editable 限制的类或接口
 	 * @param editable 限制的类或接口
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 */
 	 */
@@ -83,7 +99,7 @@ public class CopyOptions implements Serializable{
 
 
 	/**
 	/**
 	 * 设置是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
 	 * 设置是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
-	 * 
+	 *
 	 * @param ignoreNullVall 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
 	 * @param ignoreNullVall 是否忽略空值,当源对象的值为null时,true: 忽略而不注入此值,false: 注入null
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 */
 	 */
@@ -91,10 +107,10 @@ public class CopyOptions implements Serializable{
 		this.ignoreNullValue = ignoreNullVall;
 		this.ignoreNullValue = ignoreNullVall;
 		return this;
 		return this;
 	}
 	}
-	
+
 	/**
 	/**
 	 * 设置忽略空值,当源对象的值为null时,忽略而不注入此值
 	 * 设置忽略空值,当源对象的值为null时,忽略而不注入此值
-	 * 
+	 *
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 * @since 4.5.7
 	 * @since 4.5.7
 	 */
 	 */
@@ -104,7 +120,7 @@ public class CopyOptions implements Serializable{
 
 
 	/**
 	/**
 	 * 设置忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
 	 * 设置忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
-	 * 
+	 *
 	 * @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
 	 * @param ignoreProperties 忽略的目标对象中属性列表,设置一个属性列表,不拷贝这些属性值
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 */
 	 */
@@ -115,7 +131,7 @@ public class CopyOptions implements Serializable{
 
 
 	/**
 	/**
 	 * 设置是否忽略字段的注入错误
 	 * 设置是否忽略字段的注入错误
-	 * 
+	 *
 	 * @param ignoreError 是否忽略注入错误
 	 * @param ignoreError 是否忽略注入错误
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 */
 	 */
@@ -123,20 +139,20 @@ public class CopyOptions implements Serializable{
 		this.ignoreError = ignoreError;
 		this.ignoreError = ignoreError;
 		return this;
 		return this;
 	}
 	}
-	
+
 	/**
 	/**
 	 * 设置忽略字段的注入错误
 	 * 设置忽略字段的注入错误
-	 * 
+	 *
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 * @since 4.5.7
 	 * @since 4.5.7
 	 */
 	 */
 	public CopyOptions ignoreError() {
 	public CopyOptions ignoreError() {
 		return setIgnoreError(true);
 		return setIgnoreError(true);
 	}
 	}
-	
+
 	/**
 	/**
 	 * 设置是否忽略字段的大小写
 	 * 设置是否忽略字段的大小写
-	 * 
+	 *
 	 * @param ignoreCase 是否忽略大小写
 	 * @param ignoreCase 是否忽略大小写
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 */
 	 */
@@ -144,10 +160,10 @@ public class CopyOptions implements Serializable{
 		this.ignoreCase = ignoreCase;
 		this.ignoreCase = ignoreCase;
 		return this;
 		return this;
 	}
 	}
-	
+
 	/**
 	/**
 	 * 设置忽略字段的大小写
 	 * 设置忽略字段的大小写
-	 * 
+	 *
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 * @since 4.5.7
 	 * @since 4.5.7
 	 */
 	 */
@@ -157,7 +173,7 @@ public class CopyOptions implements Serializable{
 
 
 	/**
 	/**
 	 * 设置拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
 	 * 设置拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
-	 * 
+	 *
 	 * @param fieldMapping 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
 	 * @param fieldMapping 拷贝属性的字段映射,用于不同的属性之前拷贝做对应表用
 	 * @return CopyOptions
 	 * @return CopyOptions
 	 */
 	 */
@@ -165,13 +181,36 @@ public class CopyOptions implements Serializable{
 		this.fieldMapping = fieldMapping;
 		this.fieldMapping = fieldMapping;
 		return this;
 		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 反转映射
 	 * @return 反转映射
 	 * @since 4.1.10
 	 * @since 4.1.10
 	 */
 	 */
 	protected Map<String, String> getReversedMapping() {
 	protected Map<String, String> getReversedMapping() {
-		 return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
+		return (null != this.fieldMapping) ? MapUtil.reverse(this.fieldMapping) : null;
 	}
 	}
 }
 }

+ 20 - 20
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.BeanDesc.PropDesc;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.copier.ValueProvider;
 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 cn.hutool.core.util.StrUtil;
 
 
-import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.lang.reflect.Type;
 import java.util.Map;
 import java.util.Map;
 
 
@@ -46,29 +43,32 @@ public class BeanValueProvider implements ValueProvider<String> {
 
 
 		Object result = null;
 		Object result = null;
 		if (null != sourcePd) {
 		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;
 		return result;
 	}
 	}
 
 
 	@Override
 	@Override
 	public boolean containsKey(String key) {
 	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;
+	}
 }
 }

+ 12 - 12
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;
 import java.util.Map;
 
 
 /**
 /**
- * Map值提供者
- * 
- * @author looly
+ * Map值提供者,支持驼峰和下划线的key兼容。<br>
+ * 假设目标属性为firstName,则Map中为firstName或first_name都可以对应到值。
  *
  *
+ * @author looly
  */
  */
 public class MapValueProvider implements ValueProvider<String> {
 public class MapValueProvider implements ValueProvider<String> {
 
 
@@ -22,7 +22,7 @@ public class MapValueProvider implements ValueProvider<String> {
 	/**
 	/**
 	 * 构造
 	 * 构造
 	 *
 	 *
-	 * @param map Map
+	 * @param map        Map
 	 * @param ignoreCase 是否忽略key的大小写
 	 * @param ignoreCase 是否忽略key的大小写
 	 */
 	 */
 	public MapValueProvider(Map<?, ?> map, boolean ignoreCase) {
 	public MapValueProvider(Map<?, ?> map, boolean ignoreCase) {
@@ -31,17 +31,17 @@ public class MapValueProvider implements ValueProvider<String> {
 
 
 	/**
 	/**
 	 * 构造
 	 * 构造
-	 * 
-	 * @param map Map
-	 * @param ignoreCase 是否忽略key的大小写
+	 *
+	 * @param map         Map
+	 * @param ignoreCase  是否忽略key的大小写
 	 * @param ignoreError 是否忽略错误
 	 * @param ignoreError 是否忽略错误
 	 * @since 5.3.2
 	 * @since 5.3.2
 	 */
 	 */
 	public MapValueProvider(Map<?, ?> map, boolean ignoreCase, boolean ignoreError) {
 	public MapValueProvider(Map<?, ?> map, boolean ignoreCase, boolean ignoreError) {
-		if(false == ignoreCase || map instanceof CaseInsensitiveMap) {
+		if (false == ignoreCase || map instanceof CaseInsensitiveMap) {
 			//不忽略大小写或者提供的Map本身为CaseInsensitiveMap则无需转换
 			//不忽略大小写或者提供的Map本身为CaseInsensitiveMap则无需转换
 			this.map = map;
 			this.map = map;
-		}else {
+		} else {
 			//转换为大小写不敏感的Map
 			//转换为大小写不敏感的Map
 			this.map = new CaseInsensitiveMap<>(map);
 			this.map = new CaseInsensitiveMap<>(map);
 		}
 		}
@@ -51,7 +51,7 @@ public class MapValueProvider implements ValueProvider<String> {
 	@Override
 	@Override
 	public Object value(String key, Type valueType) {
 	public Object value(String key, Type valueType) {
 		Object value = map.get(key);
 		Object value = map.get(key);
-		if(null == value) {
+		if (null == value) {
 			//检查下划线模式
 			//检查下划线模式
 			value = map.get(StrUtil.toUnderlineCase(key));
 			value = map.get(StrUtil.toUnderlineCase(key));
 		}
 		}
@@ -61,9 +61,9 @@ public class MapValueProvider implements ValueProvider<String> {
 
 
 	@Override
 	@Override
 	public boolean containsKey(String key) {
 	public boolean containsKey(String key) {
-		if(map.containsKey(key)) {
+		if (map.containsKey(key)) {
 			return true;
 			return true;
-		}else {
+		} else {
 			//检查下划线模式
 			//检查下划线模式
 			return map.containsKey(StrUtil.toUnderlineCase(key));
 			return map.containsKey(StrUtil.toUnderlineCase(key));
 		}
 		}

+ 29 - 4
hutool-json/src/main/java/cn/hutool/json/JSONConfig.java

@@ -32,9 +32,9 @@ public class JSONConfig implements Serializable {
 	 */
 	 */
 	private boolean ignoreNullValue = true;
 	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关键字修饰的字段
 	 * @return 是否忽略transient关键字修饰的字段
 	 * @since 5.3.11
 	 * @since 5.3.11
+	 * @deprecated 此方法名称有二义性,请使用{@link #isTransientSupport()}
 	 */
 	 */
+	@Deprecated
 	public boolean isIgnoreTransient() {
 	public boolean isIgnoreTransient() {
-		return this.ignoreTransient;
+		return isTransientSupport();
 	}
 	}
 
 
 	/**
 	/**
@@ -161,9 +163,32 @@ public class JSONConfig implements Serializable {
 	 * @param ignoreTransient 是否忽略transient关键字修饰的字段
 	 * @param ignoreTransient 是否忽略transient关键字修饰的字段
 	 * @return this
 	 * @return this
 	 * @since 5.3.11
 	 * @since 5.3.11
+	 * @deprecated 此方法名称有二义性,请使用{@link #setTransientSupport(boolean)}
 	 */
 	 */
+	@Deprecated
 	public JSONConfig setIgnoreTransient(boolean ignoreTransient) {
 	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;
 		return this;
 	}
 	}
 }
 }

+ 1 - 1
hutool-json/src/main/java/cn/hutool/json/JSONObject.java

@@ -625,7 +625,7 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
 		Method getter;
 		Method getter;
 		Object value;
 		Object value;
 		for (PropDesc prop : props) {
 		for (PropDesc prop : props) {
-			if(this.config.isIgnoreTransient() && prop.isTransient()){
+			if(this.config.isTransientSupport() && prop.isTransient()){
 				// 忽略Transient字段和方法
 				// 忽略Transient字段和方法
 				continue;
 				continue;
 			}
 			}