Looly 5 年之前
父节点
当前提交
0c3a1ea298

+ 0 - 308
hutool-core/src/main/java/cn/hutool/core/bean/BeanDesc.java

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

+ 17 - 27
hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java

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

+ 2 - 2
hutool-core/src/main/java/cn/hutool/core/bean/DynaBean.java

@@ -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 - 0
hutool-core/src/main/java/cn/hutool/core/bean/PropDesc.java

@@ -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
+}

+ 38 - 78
hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java

@@ -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 (bean.equals(value)) {
-				continue;// 值不能为bean本身,防止循环引用
+			if ((null == value && copyOptions.ignoreNullValue) || bean == value) {
+				// 当允许跳过空时,跳过
+				//值不能为bean本身,防止循环引用,此类也跳过
+				return;
 			}
-			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();
-			if (CollUtil.contains(ignoreSet, fieldName)) {
-				// 目标属性值被忽略或值提供者无此key时跳过
-				continue;
-			}
 
-			// 在支持情况下,忽略transient修饰(包括修饰和注解)
-			if(copyOptions.isTransientSupport() && prop.isTransient()){
-				continue;
+		BeanUtil.descForEach(actualEditable, (prop)->{
+			if(false == prop.isWritable(this.copyOptions.isTransientSupport())){
+				// 字段不可写,跳过之
+				return;
 			}
 
-			// @Ignore修饰的字段和setXXX方法忽略之
-			if(prop.isIgnoreSet()){
-				continue;
+			// 检查属性名
+			String fieldName = prop.getFieldName();
+			if (CollUtil.contains(ignoreSet, fieldName)) {
+				// 目标属性值被忽略或值提供者无此key时跳过
+				return;
 			}
 
-			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);
+		});
 	}
 }

+ 57 - 2
hutool-core/src/main/java/cn/hutool/core/bean/copier/CopyOptions.java

@@ -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;
@@ -42,6 +44,14 @@ public class CopyOptions implements Serializable {
 	 */
 	protected Map<String, String> fieldMapping;
 	/**
+	 * 反向映射表,自动生成用于反向查找
+	 */
+	private Map<String, String> reversedFieldMapping;
+	/**
+	 * 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
+	 */
+	protected Editor<String> fieldNameEditor;
+	/**
 	 * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
 	 */
 	private boolean transientSupport = true;
@@ -183,6 +193,19 @@ public class CopyOptions implements Serializable {
 	}
 
 	/**
+	 * 设置字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等<br>
+	 * 此转换器只针对源端的字段做转换,请确认转换后与目标端字段一致
+	 *
+	 * @param fieldNameEditor 字段属性编辑器,用于自定义属性转换规则,例如驼峰转下划线等
+	 * @return CopyOptions
+	 * @since 5.4.2
+	 */
+	public CopyOptions setFieldNameEditor(Editor<String> fieldNameEditor) {
+		this.fieldNameEditor = fieldNameEditor;
+		return this;
+	}
+
+	/**
 	 * 是否支持transient关键字修饰和@Transient注解,如果支持,被修饰的字段或方法对应的字段将被忽略。
 	 *
 	 * @return 是否支持
@@ -205,12 +228,44 @@ public class CopyOptions implements Serializable {
 	}
 
 	/**
+	 * 获得映射后的字段名<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;
 	}
 }

+ 3 - 3
hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/BeanValueProvider.java

@@ -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);
 	}
 
 	/**

+ 5 - 5
hutool-core/src/main/java/cn/hutool/core/comparator/PropertyComparator.java

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

+ 0 - 2
hutool-core/src/test/java/cn/hutool/core/bean/BeanDescTest.java

@@ -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 - 1
hutool-db/src/main/java/cn/hutool/db/handler/HandleHelper.java

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

+ 8 - 37
hutool-json/src/main/java/cn/hutool/json/JSONObject.java

@@ -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();
 	}
 
 	/**