From d07feb90659a5f182f9e7313f62d235084a67c3c Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 2 Aug 2020 19:26:24 +0800 Subject: [PATCH] fix methoda --- CHANGELOG.md | 3 + .../java/cn/hutool/core/bean/BeanUtil.java | 75 +++++- .../java/cn/hutool/core/convert/Convert.java | 3 +- .../core/convert/impl/BeanConverter.java | 4 +- .../core/convert/impl/PrimitiveConverter.java | 216 +++++++++--------- .../main/java/cn/hutool/core/lang/Dict.java | 4 +- .../core/text/replacer/LookupReplacer.java | 8 +- .../cn/hutool/core/bean/BeanUtilTest.java | 42 +++- .../core/convert/PrimitiveConvertTest.java | 18 ++ 9 files changed, 239 insertions(+), 134 deletions(-) create mode 100644 hutool-core/src/test/java/cn/hutool/core/convert/PrimitiveConvertTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec047bfb..39ef56c3c 100644 --- a/CHANGELOG.md +++ b/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非空构造无法实例化问题 ------------------------------------------------------------------------------------------------------------- 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 ec4577ce3..5b71af0e2 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 @@ -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 mapToBean(Map map, Class 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 mapToBeanIgnoreCase(Map map, Class 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 mapToBean(Map map, Class beanClass, CopyOptions copyOptions) { - return fillBeanWithMap(map, ReflectUtil.newInstance(beanClass), copyOptions); + return fillBeanWithMap(map, ReflectUtil.newInstanceIfPossible(beanClass), copyOptions); + } + + /** + * Map转换为Bean对象 + * + * @param Bean类型 + * @param map {@link Map} + * @param beanClass Bean Class + * @param isToCamelCase 是否将Map中的下划线风格key转换为驼峰风格 + * @param copyOptions 转Bean选项 + * @return Bean + */ + public static T mapToBean(Map map, Class 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 @@ -465,6 +486,36 @@ public class BeanUtil { return toBean(source, clazz, null); } + /** + * 对象或Map转Bean,忽略字段转换时发生的异常 + * + * @param 转换的Bean类型 + * @param source Bean对象或Map + * @param clazz 目标的Bean类型 + * @return Bean对象 + * @since 5.4.0 + */ + public static T toBeanIgnoreError(Object source, Class clazz) { + return toBean(source, clazz, CopyOptions.create().setIgnoreError(true)); + } + + /** + * 对象或Map转Bean,忽略字段转换时发生的异常 + * + * @param 转换的Bean类型 + * @param source Bean对象或Map + * @param clazz 目标的Bean类型 + * @param ignoreError 是否忽略注入错误 + * @return Bean对象 + * @since 5.4.0 + */ + public static T toBeanIgnoreCase(Object source, Class clazz, boolean ignoreError) { + return toBean(source, clazz, + CopyOptions.create() + .setIgnoreCase(true) + .setIgnoreError(ignoreError)); + } + /** * 对象或Map转Bean * @@ -491,7 +542,7 @@ public class BeanUtil { * @return Bean */ public static T toBean(Class beanClass, ValueProvider 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 对象类型 - * @param source 源Bean对象 - * @param tClass 目标Class + * @param 对象类型 + * @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()))// diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java index d06fed1e8..052ae30e7 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java +++ b/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 convertByClassName(String className, Object value) throws ConvertException{ - return (T) convert(ClassUtil.loadClass(className), value); + return convert(ClassUtil.loadClass(className), value); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java index 72513ef59..2749d07cb 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/BeanConverter.java +++ b/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 extends AbstractConverter { // 尝试反序列化 return ObjectUtil.deserialize((byte[])value); } - return null; + + throw new ConvertException("Unsupported source type: {}", value.getClass()); } @Override diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java index a8dd0b987..d039056cd 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/PrimitiveConverter.java +++ b/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 { @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 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java b/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java index e3734747c..27f74da5a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Dict.java @@ -178,7 +178,7 @@ public class Dict extends LinkedHashMap implements BasicTypeGett * @return vo */ public T toBean(Class clazz) { - return BeanUtil.mapToBean(this, clazz, false); + return BeanUtil.toBean(this, clazz); } /** @@ -189,7 +189,7 @@ public class Dict extends LinkedHashMap implements BasicTypeGett * @return vo */ public T toBeanIgnoreCase(Class clazz) { - return BeanUtil.mapToBeanIgnoreCase(this, clazz, false); + return BeanUtil.toBeanIgnoreCase(this, clazz, false); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/text/replacer/LookupReplacer.java b/hutool-core/src/main/java/cn/hutool/core/text/replacer/LookupReplacer.java index 661726706..2ed09653d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/replacer/LookupReplacer.java +++ b/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(); - this.prefixSet = new HashSet(); + this.lookupMap = new HashMap<>(); + this.prefixSet = new HashSet<>(); int minLength = Integer.MAX_VALUE; int maxLength = 0; diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java index d0a8909d4..f079292e3 100644 --- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanUtilTest.java +++ b/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 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 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 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; diff --git a/hutool-core/src/test/java/cn/hutool/core/convert/PrimitiveConvertTest.java b/hutool-core/src/test/java/cn/hutool/core/convert/PrimitiveConvertTest.java new file mode 100644 index 000000000..2e4e4ffb4 --- /dev/null +++ b/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"); + } +}