Looly 5 年 前
コミット
d07feb9065

+ 3 - 0
CHANGELOG.md

@@ -9,8 +9,11 @@
 * 【socket】     对NioServer和NioClient改造(pr#992@Github)
 * 【core  】     StrUtil增加filter方法(pr#149@Gitee)
 * 【core  】     DateUtil增加beginOfWeek重载
+* 【core  】     将有歧义的BeanUtil.mapToBean方法置为过期(使用toBean方法)
 
 ### Bug修复#
+* 【core  】     修复原始类型转换时,转换失败没有抛出异常的问题
+* 【core  】     修复BeanUtil.mapToBean中bean的class非空构造无法实例化问题
 
 -------------------------------------------------------------------------------------------------------------
 

+ 63 - 12
hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java

@@ -52,7 +52,7 @@ public class BeanUtil {
 	 *
 	 * @param clazz 待测试类
 	 * @return 是否为可读的Bean对象
-	 * @see #hasGetter(Class) 
+	 * @see #hasGetter(Class)
 	 * @see #hasPublicField(Class)
 	 */
 	public static boolean isReadableBean(Class<?> clazz) {
@@ -342,9 +342,11 @@ public class BeanUtil {
 	 * @param beanClass     Bean Class
 	 * @param isIgnoreError 是否忽略注入错误
 	 * @return Bean
+	 * @deprecated 请使用 {@link #toBean(Object, Class)} 或 {@link #toBeanIgnoreError(Object, Class)}
 	 */
+	@Deprecated
 	public static <T> T mapToBean(Map<?, ?> map, Class<T> beanClass, boolean isIgnoreError) {
-		return fillBeanWithMap(map, ReflectUtil.newInstance(beanClass), isIgnoreError);
+		return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), isIgnoreError);
 	}
 
 	/**
@@ -356,9 +358,11 @@ public class BeanUtil {
 	 * @param beanClass     Bean Class
 	 * @param isIgnoreError 是否忽略注入错误
 	 * @return Bean
+	 * @deprecated 请使用 {@link #toBeanIgnoreCase(Object, Class, boolean)}
 	 */
+	@Deprecated
 	public static <T> T mapToBeanIgnoreCase(Map<?, ?> map, Class<T> beanClass, boolean isIgnoreError) {
-		return fillBeanWithMapIgnoreCase(map, ReflectUtil.newInstance(beanClass), isIgnoreError);
+		return fillBeanWithMapIgnoreCase(map, ReflectUtil.newInstanceIfPossible(beanClass), isIgnoreError);
 	}
 
 	/**
@@ -369,9 +373,25 @@ public class BeanUtil {
 	 * @param beanClass   Bean Class
 	 * @param copyOptions 转Bean选项
 	 * @return Bean
+	 * @deprecated 请使用 {@link #toBean(Object, Class, CopyOptions)}
 	 */
+	@Deprecated
 	public static <T> T mapToBean(Map<?, ?> map, Class<T> beanClass, CopyOptions copyOptions) {
-		return fillBeanWithMap(map, ReflectUtil.newInstance(beanClass), copyOptions);
+		return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), copyOptions);
+	}
+
+	/**
+	 * Map转换为Bean对象
+	 *
+	 * @param <T>           Bean类型
+	 * @param map           {@link Map}
+	 * @param beanClass     Bean Class
+	 * @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格
+	 * @param copyOptions   转Bean选项
+	 * @return Bean
+	 */
+	public static <T> T mapToBean(Map<?, ?> map, Class<T> beanClass, boolean isToCamelCase, CopyOptions copyOptions) {
+		return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), isToCamelCase, copyOptions);
 	}
 
 	// --------------------------------------------------------------------------------------------- fillBeanWithMap
@@ -447,7 +467,8 @@ public class BeanUtil {
 		if (isToCamelCase) {
 			map = MapUtil.toCamelCaseMap(map);
 		}
-		return BeanCopier.create(map, bean, copyOptions).copy();
+		copyProperties(map, bean, copyOptions);
+		return bean;
 	}
 
 	// --------------------------------------------------------------------------------------------- fillBean
@@ -466,6 +487,36 @@ public class BeanUtil {
 	}
 
 	/**
+	 * 对象或Map转Bean,忽略字段转换时发生的异常
+	 *
+	 * @param <T>    转换的Bean类型
+	 * @param source Bean对象或Map
+	 * @param clazz  目标的Bean类型
+	 * @return Bean对象
+	 * @since 5.4.0
+	 */
+	public static <T> T toBeanIgnoreError(Object source, Class<T> clazz) {
+		return toBean(source, clazz, CopyOptions.create().setIgnoreError(true));
+	}
+
+	/**
+	 * 对象或Map转Bean,忽略字段转换时发生的异常
+	 *
+	 * @param <T>         转换的Bean类型
+	 * @param source      Bean对象或Map
+	 * @param clazz       目标的Bean类型
+	 * @param ignoreError 是否忽略注入错误
+	 * @return Bean对象
+	 * @since 5.4.0
+	 */
+	public static <T> T toBeanIgnoreCase(Object source, Class<T> clazz, boolean ignoreError) {
+		return toBean(source, clazz,
+				CopyOptions.create()
+						.setIgnoreCase(true)
+						.setIgnoreError(ignoreError));
+	}
+
+	/**
 	 * 对象或Map转Bean
 	 *
 	 * @param <T>     转换的Bean类型
@@ -491,7 +542,7 @@ public class BeanUtil {
 	 * @return Bean
 	 */
 	public static <T> T toBean(Class<T> beanClass, ValueProvider<String> valueProvider, CopyOptions copyOptions) {
-		return fillBean(ReflectUtil.newInstance(beanClass), valueProvider, copyOptions);
+		return fillBean(ReflectUtil.newInstanceIfPossible(beanClass), valueProvider, copyOptions);
 	}
 
 	/**
@@ -608,9 +659,9 @@ public class BeanUtil {
 	/**
 	 * 按照Bean对象属性创建对应的Class对象,并忽略某些属性
 	 *
-	 * @param <T>    对象类型
-	 * @param source 源Bean对象
-	 * @param tClass 目标Class
+	 * @param <T>              对象类型
+	 * @param source           源Bean对象
+	 * @param tClass           目标Class
 	 * @param ignoreProperties 不拷贝的的属性列表
 	 * @return 目标对象
 	 */
@@ -690,7 +741,7 @@ public class BeanUtil {
 
 		final Field[] fields = ReflectUtil.getFields(bean.getClass());
 		for (Field field : fields) {
-			if(ModifierUtil.isStatic(field)){
+			if (ModifierUtil.isStatic(field)) {
 				continue;
 			}
 			if (ignoreFields != null && ArrayUtil.containsIgnoreCase(ignoreFields, field.getName())) {
@@ -737,7 +788,7 @@ public class BeanUtil {
 	public static boolean isEmpty(Object bean, String... ignoreFiledNames) {
 		if (null != bean) {
 			for (Field field : ReflectUtil.getFields(bean.getClass())) {
-				if(ModifierUtil.isStatic(field)){
+				if (ModifierUtil.isStatic(field)) {
 					continue;
 				}
 				if ((false == ArrayUtil.contains(ignoreFiledNames, field.getName()))
@@ -763,7 +814,7 @@ public class BeanUtil {
 			return true;
 		}
 		for (Field field : ReflectUtil.getFields(bean.getClass())) {
-			if(ModifierUtil.isStatic(field)){
+			if (ModifierUtil.isStatic(field)) {
 				continue;
 			}
 			if ((false == ArrayUtil.contains(ignoreFiledNames, field.getName()))//

+ 1 - 2
hutool-core/src/main/java/cn/hutool/core/convert/Convert.java

@@ -609,9 +609,8 @@ public class Convert {
 	 * @since 4.0.7
 	 * @throws ConvertException 转换器不存在
 	 */
-	@SuppressWarnings("unchecked")
 	public static <T> T convertByClassName(String className, Object value) throws ConvertException{
-		return (T) convert(ClassUtil.loadClass(className), value);
+		return convert(ClassUtil.loadClass(className), value);
 	}
 	
 	/**

+ 3 - 1
hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java

@@ -5,6 +5,7 @@ import cn.hutool.core.bean.copier.BeanCopier;
 import cn.hutool.core.bean.copier.CopyOptions;
 import cn.hutool.core.bean.copier.ValueProvider;
 import cn.hutool.core.convert.AbstractConverter;
+import cn.hutool.core.convert.ConvertException;
 import cn.hutool.core.map.MapProxy;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReflectUtil;
@@ -79,7 +80,8 @@ public class BeanConverter<T> extends AbstractConverter<T> {
 			// 尝试反序列化
 			return ObjectUtil.deserialize((byte[])value);
 		}
-		return null;
+
+		throw new ConvertException("Unsupported source type: {}", value.getClass());
 	}
 
 	@Override

+ 107 - 109
hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java

@@ -1,6 +1,7 @@
 package cn.hutool.core.convert.impl;
 
 import cn.hutool.core.convert.AbstractConverter;
+import cn.hutool.core.convert.ConvertException;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.BooleanUtil;
 import cn.hutool.core.util.NumberUtil;
@@ -48,117 +49,114 @@ public class PrimitiveConverter extends AbstractConverter<Object> {
 
 	@Override
 	protected Object convertInternal(Object value) {
-		try {
-			if (byte.class == this.targetType) {
-				if (value instanceof Number) {
-					return ((Number) value).byteValue();
-				} else if (value instanceof Boolean) {
-					return BooleanUtil.toByte((Boolean) value);
-				}
-				final String valueStr = convertToStr(value);
-				if (StrUtil.isBlank(valueStr)) {
-					return 0;
-				}
-				return Byte.parseByte(valueStr);
-
-			} else if (short.class == this.targetType) {
-				if (value instanceof Number) {
-					return ((Number) value).shortValue();
-				} else if (value instanceof Boolean) {
-					return BooleanUtil.toShort((Boolean) value);
-				}
-				final String valueStr = convertToStr(value);
-				if (StrUtil.isBlank(valueStr)) {
-					return 0;
-				}
-				return Short.parseShort(valueStr);
-
-			} else if (int.class == this.targetType) {
-				if (value instanceof Number) {
-					return ((Number) value).intValue();
-				} else if (value instanceof Boolean) {
-					return BooleanUtil.toInt((Boolean) value);
-				} else if (value instanceof Date) {
-					return ((Date) value).getTime();
-				} else if (value instanceof Calendar) {
-					return ((Calendar) value).getTimeInMillis();
-				} else if (value instanceof TemporalAccessor) {
-					return DateUtil.toInstant((TemporalAccessor) value).toEpochMilli();
-				}
-
-				final String valueStr = convertToStr(value);
-				if (StrUtil.isBlank(valueStr)) {
-					return 0;
-				}
-				return NumberUtil.parseInt(valueStr);
-
-			} else if (long.class == this.targetType) {
-				if (value instanceof Number) {
-					return ((Number) value).longValue();
-				} else if (value instanceof Boolean) {
-					return BooleanUtil.toLong((Boolean) value);
-				} else if (value instanceof Date) {
-					return ((Date) value).getTime();
-				} else if (value instanceof Calendar) {
-					return ((Calendar) value).getTimeInMillis();
-				} else if (value instanceof TemporalAccessor) {
-					return DateUtil.toInstant((TemporalAccessor) value).toEpochMilli();
-				}
-
-				final String valueStr = convertToStr(value);
-				if (StrUtil.isBlank(valueStr)) {
-					return 0;
-				}
-				return NumberUtil.parseLong(valueStr);
-
-			} else if (float.class == this.targetType) {
-				if (value instanceof Number) {
-					return ((Number) value).floatValue();
-				} else if (value instanceof Boolean) {
-					return BooleanUtil.toFloat((Boolean) value);
-				}
-				final String valueStr = convertToStr(value);
-				if (StrUtil.isBlank(valueStr)) {
-					return 0;
-				}
-				return Float.parseFloat(valueStr);
-
-			} else if (double.class == this.targetType) {
-				if (value instanceof Number) {
-					return ((Number) value).doubleValue();
-				} else if (value instanceof Boolean) {
-					return BooleanUtil.toDouble((Boolean) value);
-				}
-				final String valueStr = convertToStr(value);
-				if (StrUtil.isBlank(valueStr)) {
-					return 0;
-				}
-				return Double.parseDouble(valueStr);
-
-			} else if (char.class == this.targetType) {
-				if (value instanceof Character) {
-					//noinspection UnnecessaryUnboxing
-					return ((Character) value).charValue();
-				} else if (value instanceof Boolean) {
-					return BooleanUtil.toChar((Boolean) value);
-				}
-				final String valueStr = convertToStr(value);
-				if (StrUtil.isBlank(valueStr)) {
-					return 0;
-				}
-				return valueStr.charAt(0);
-			} else if (boolean.class == this.targetType) {
-				if (value instanceof Boolean) {
-					//noinspection UnnecessaryUnboxing
-					return ((Boolean) value).booleanValue();
-				}
-				String valueStr = convertToStr(value);
-				return BooleanUtil.toBoolean(valueStr);
+		if (byte.class == this.targetType) {
+			if (value instanceof Number) {
+				return ((Number) value).byteValue();
+			} else if (value instanceof Boolean) {
+				return BooleanUtil.toByte((Boolean) value);
 			}
-		} catch (Exception e) {
-			// Ignore Exception
+			final String valueStr = convertToStr(value);
+			if (StrUtil.isBlank(valueStr)) {
+				return 0;
+			}
+			return Byte.parseByte(valueStr);
+
+		} else if (short.class == this.targetType) {
+			if (value instanceof Number) {
+				return ((Number) value).shortValue();
+			} else if (value instanceof Boolean) {
+				return BooleanUtil.toShort((Boolean) value);
+			}
+			final String valueStr = convertToStr(value);
+			if (StrUtil.isBlank(valueStr)) {
+				return 0;
+			}
+			return Short.parseShort(valueStr);
+
+		} else if (int.class == this.targetType) {
+			if (value instanceof Number) {
+				return ((Number) value).intValue();
+			} else if (value instanceof Boolean) {
+				return BooleanUtil.toInt((Boolean) value);
+			} else if (value instanceof Date) {
+				return ((Date) value).getTime();
+			} else if (value instanceof Calendar) {
+				return ((Calendar) value).getTimeInMillis();
+			} else if (value instanceof TemporalAccessor) {
+				return DateUtil.toInstant((TemporalAccessor) value).toEpochMilli();
+			}
+
+			final String valueStr = convertToStr(value);
+			if (StrUtil.isBlank(valueStr)) {
+				return 0;
+			}
+			return NumberUtil.parseInt(valueStr);
+
+		} else if (long.class == this.targetType) {
+			if (value instanceof Number) {
+				return ((Number) value).longValue();
+			} else if (value instanceof Boolean) {
+				return BooleanUtil.toLong((Boolean) value);
+			} else if (value instanceof Date) {
+				return ((Date) value).getTime();
+			} else if (value instanceof Calendar) {
+				return ((Calendar) value).getTimeInMillis();
+			} else if (value instanceof TemporalAccessor) {
+				return DateUtil.toInstant((TemporalAccessor) value).toEpochMilli();
+			}
+
+			final String valueStr = convertToStr(value);
+			if (StrUtil.isBlank(valueStr)) {
+				return 0;
+			}
+			return NumberUtil.parseLong(valueStr);
+
+		} else if (float.class == this.targetType) {
+			if (value instanceof Number) {
+				return ((Number) value).floatValue();
+			} else if (value instanceof Boolean) {
+				return BooleanUtil.toFloat((Boolean) value);
+			}
+			final String valueStr = convertToStr(value);
+			if (StrUtil.isBlank(valueStr)) {
+				return 0;
+			}
+			return Float.parseFloat(valueStr);
+
+		} else if (double.class == this.targetType) {
+			if (value instanceof Number) {
+				return ((Number) value).doubleValue();
+			} else if (value instanceof Boolean) {
+				return BooleanUtil.toDouble((Boolean) value);
+			}
+			final String valueStr = convertToStr(value);
+			if (StrUtil.isBlank(valueStr)) {
+				return 0;
+			}
+			return Double.parseDouble(valueStr);
+
+		} else if (char.class == this.targetType) {
+			if (value instanceof Character) {
+				//noinspection UnnecessaryUnboxing
+				return ((Character) value).charValue();
+			} else if (value instanceof Boolean) {
+				return BooleanUtil.toChar((Boolean) value);
+			}
+			final String valueStr = convertToStr(value);
+			if (StrUtil.isBlank(valueStr)) {
+				return 0;
+			}
+			return valueStr.charAt(0);
+		} else if (boolean.class == this.targetType) {
+			if (value instanceof Boolean) {
+				//noinspection UnnecessaryUnboxing
+				return ((Boolean) value).booleanValue();
+			}
+			final String valueStr = convertToStr(value);
+			return BooleanUtil.toBoolean(valueStr);
 		}
-		return 0;
+
+		throw new ConvertException("Unsupported target type: {}", this.targetType);
 	}
 
 	@Override

+ 2 - 2
hutool-core/src/main/java/cn/hutool/core/lang/Dict.java

@@ -178,7 +178,7 @@ public class Dict extends LinkedHashMap<String, Object> implements BasicTypeGett
 	 * @return vo
 	 */
 	public <T> T toBean(Class<T> clazz) {
-		return BeanUtil.mapToBean(this, clazz, false);
+		return BeanUtil.toBean(this, clazz);
 	}
 
 	/**
@@ -189,7 +189,7 @@ public class Dict extends LinkedHashMap<String, Object> implements BasicTypeGett
 	 * @return vo
 	 */
 	public <T> T toBeanIgnoreCase(Class<T> clazz) {
-		return BeanUtil.mapToBeanIgnoreCase(this, clazz, false);
+		return BeanUtil.toBeanIgnoreCase(this, clazz, false);
 	}
 
 	/**

+ 4 - 4
hutool-core/src/main/java/cn/hutool/core/text/replacer/LookupReplacer.java

@@ -1,12 +1,12 @@
 package cn.hutool.core.text.replacer;
 
+import cn.hutool.core.text.StrBuilder;
+
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-import cn.hutool.core.text.StrBuilder;
-
 /**
  * 查找替换器,通过查找指定关键字,替换对应的值
  * 
@@ -27,8 +27,8 @@ public class LookupReplacer extends StrReplacer {
 	 * @param lookup 被查找的键值对
 	 */
 	public LookupReplacer(String[]... lookup) {
-		this.lookupMap = new HashMap<String, String>();
-		this.prefixSet = new HashSet<Character>();
+		this.lookupMap = new HashMap<>();
+		this.prefixSet = new HashSet<>();
 
 		int minLength = Integer.MAX_VALUE;
 		int maxLength = 0;

+ 38 - 4
hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java

@@ -92,13 +92,29 @@ public class BeanUtilTest {
 		Assert.assertFalse(map.containsKey("SUBNAME"));
 	}
 
+	/**
+	 * 忽略转换错误测试
+	 */
+	@Test
+	public void toBeanIgnoreErrorTest(){
+		HashMap<String, Object> map = CollUtil.newHashMap();
+		map.put("name", "Joe");
+		// 错误的类型,此处忽略
+		map.put("age", "aaaaaa");
+
+		Person person = BeanUtil.toBeanIgnoreError(map, Person.class);
+		Assert.assertEquals("Joe", person.getName());
+		// 错误的类型,不copy这个字段,使用对象创建的默认值
+		Assert.assertEquals(0, person.getAge());
+	}
+
 	@Test
 	public void mapToBeanIgnoreCaseTest() {
 		HashMap<String, Object> map = CollUtil.newHashMap();
 		map.put("Name", "Joe");
 		map.put("aGe", 12);
 
-		Person person = BeanUtil.mapToBeanIgnoreCase(map, Person.class, false);
+		Person person = BeanUtil.toBeanIgnoreCase(map, Person.class, false);
 		Assert.assertEquals("Joe", person.getName());
 		Assert.assertEquals(12, person.getAge());
 	}
@@ -114,7 +130,7 @@ public class BeanUtilTest {
 		mapping.put("a_name", "name");
 		mapping.put("b_age", "age");
 
-		Person person = BeanUtil.mapToBean(map, Person.class, CopyOptions.create().setFieldMapping(mapping));
+		Person person = BeanUtil.toBean(map, Person.class, CopyOptions.create().setFieldMapping(mapping));
 		Assert.assertEquals("Joe", person.getName());
 		Assert.assertEquals(12, person.getAge());
 	}
@@ -128,11 +144,22 @@ public class BeanUtilTest {
 		map.put("name", "Joe");
 		map.put("age", 12);
 
-		Person2 person = BeanUtil.mapToBean(map, Person2.class, CopyOptions.create());
+		// 非空构造也可以实例化成功
+		Person2 person = BeanUtil.toBean(map, Person2.class, CopyOptions.create());
 		Assert.assertEquals("Joe", person.name);
 		Assert.assertEquals(12, person.age);
 	}
 
+	/**
+	 * 测试在不忽略错误情况下,转换失败需要报错。
+	 */
+	@Test(expected = NumberFormatException.class)
+	public void mapToBeanWinErrorTest() {
+		Map<String, String> map = new HashMap<>();
+		map.put("age", "哈哈");
+		Person user = BeanUtil.toBean(map, Person.class);
+	}
+
 	@Test
 	public void beanToMapTest() {
 		SubPerson person = new SubPerson();
@@ -181,7 +208,7 @@ public class BeanUtilTest {
 		map.put("aliasSubName", "sub名字");
 		map.put("slow", true);
 
-		final SubPersonWithAlias subPersonWithAlias = BeanUtil.mapToBean(map, SubPersonWithAlias.class, false);
+		final SubPersonWithAlias subPersonWithAlias = BeanUtil.toBean(map, SubPersonWithAlias.class);
 		Assert.assertEquals("sub名字", subPersonWithAlias.getSubName());
 	}
 
@@ -345,6 +372,13 @@ public class BeanUtilTest {
 	}
 
 	public static class Person2 {
+
+		public Person2(String name, int age, String openid) {
+			this.name = name;
+			this.age = age;
+			this.openid = openid;
+		}
+
 		public String name;
 		public int age;
 		public String openid;

+ 18 - 0
hutool-core/src/test/java/cn/hutool/core/convert/PrimitiveConvertTest.java

@@ -0,0 +1,18 @@
+package cn.hutool.core.convert;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PrimitiveConvertTest {
+
+	@Test
+	public void toIntTest(){
+		final int convert = Convert.convert(int.class, "123");
+		Assert.assertEquals(123, convert);
+	}
+
+	@Test(expected = NumberFormatException.class)
+	public void toIntErrorTest(){
+		final int convert = Convert.convert(int.class, "aaaa");
+	}
+}