diff --git a/CHANGELOG.md b/CHANGELOG.md index 9605617b6..969d45491 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### 新特性 * 【core 】 新增WatchServer(issue#440@Github) * 【core 】 ReflectUtil.getFieldValue支持static(issue#662@Github) +* 【core 】 改进Bean判断和注入逻辑:支持public字段注入(issue#I1689L@Gitee) ### Bug修复 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 72134cc1d..27dbf4728 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 @@ -20,12 +20,14 @@ import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.bean.copier.ValueProvider; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Console; import cn.hutool.core.lang.Editor; import cn.hutool.core.lang.Filter; import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ModifierUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; @@ -42,15 +44,19 @@ import cn.hutool.core.util.StrUtil; public class BeanUtil { /** - * 判断是否为Bean对象
- * 判定方法是是否存在只有一个参数的setXXX方法 + * 判断是否为Bean对象,判定方法是: + * + *
+	 *     1、是否存在只有一个参数的setXXX方法
+	 *     2、是否存在public类型的字段
+	 * 
* * @param clazz 待测试类 * @return 是否为Bean对象 * @see #hasSetter(Class) */ public static boolean isBean(Class clazz) { - return hasSetter(clazz); + return hasSetter(clazz) || hasPublicField(clazz); } /** @@ -84,8 +90,7 @@ public class BeanUtil { */ public static boolean hasGetter(Class clazz) { if (ClassUtil.isNormalClass(clazz)) { - final Method[] methods = clazz.getMethods(); - for (Method method : methods) { + for (Method method : clazz.getMethods()) { if (method.getParameterTypes().length == 0) { if (method.getName().startsWith("get") || method.getName().startsWith("is")) { return true; @@ -96,6 +101,25 @@ public class BeanUtil { return false; } + /** + * 指定类中是否有public类型字段(static字段除外) + * + * @param clazz 待测试类 + * @return 是否有public类型字段 + * @since 5.1.0 + */ + public static boolean hasPublicField(Class clazz) { + if (ClassUtil.isNormalClass(clazz)) { + for (Field field : clazz.getFields()) { + if (ModifierUtil.isPublic(field) && false == ModifierUtil.isStatic(field)) { + //非static的public字段 + return true; + } + } + } + return false; + } + /** * 创建动态Bean * diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java index 3d477dbc8..ffc561205 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/BeanCopier.java @@ -1,14 +1,5 @@ package cn.hutool.core.bean.copier; -import java.io.Serializable; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; - import cn.hutool.core.bean.BeanDesc.PropDesc; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.provider.BeanValueProvider; @@ -20,10 +11,22 @@ import cn.hutool.core.lang.ParameterizedTypeImpl; import cn.hutool.core.lang.copier.Copier; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ModifierUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; 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.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; + /** * Bean拷贝 * @@ -214,12 +217,14 @@ public class BeanCopier implements Copier, Serializable { final Map fieldReverseMapping = copyOptions.getReversedMapping(); final Collection 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时跳过 @@ -231,30 +236,31 @@ public class BeanCopier implements Copier, Serializable { continue; } setterMethod = prop.getSetter(); - if (null == setterMethod) { - // Setter方法不存在跳过 + if (null == setterMethod && false == ModifierUtil.isPublic(field)) { + // Setter方法不存在或者字段为非public跳过 + //5.1.0新增支持public字段注入支持 continue; } - Type firstParamType = TypeUtil.getFirstParamType(setterMethod); - if (firstParamType instanceof ParameterizedType) { + Type valueType = (null == setterMethod) ? TypeUtil.getType(field) : TypeUtil.getFirstParamType(setterMethod); + if (valueType instanceof ParameterizedType) { // 参数为泛型参数类型,解析对应泛型类型为真实类型 - ParameterizedType tmp = (ParameterizedType) firstParamType; + ParameterizedType tmp = (ParameterizedType) valueType; Type[] actualTypeArguments = tmp.getActualTypeArguments(); if (TypeUtil.hasTypeVeriable(actualTypeArguments)) { // 泛型对象中含有未被转换的泛型变量 - actualTypeArguments = TypeUtil.getActualTypes(this.destType, setterMethod.getDeclaringClass(), tmp.getActualTypeArguments()); + actualTypeArguments = TypeUtil.getActualTypes(this.destType, field.getDeclaringClass(), tmp.getActualTypeArguments()); if (ArrayUtil.isNotEmpty(actualTypeArguments)) { // 替换泛型变量为实际类型 - firstParamType = new ParameterizedTypeImpl(actualTypeArguments, tmp.getOwnerType(), tmp.getRawType()); + valueType = new ParameterizedTypeImpl(actualTypeArguments, tmp.getOwnerType(), tmp.getRawType()); } } - } else if (firstParamType instanceof TypeVariable) { + } else if (valueType instanceof TypeVariable) { // 参数为泛型,查找其真实类型(适用于泛型方法定义于泛型父类) - firstParamType = TypeUtil.getActualType(this.destType, setterMethod.getDeclaringClass(), firstParamType); + valueType = TypeUtil.getActualType(this.destType, field.getDeclaringClass(), valueType); } - value = valueProvider.value(providerKey, firstParamType); + value = valueProvider.value(providerKey, valueType); if (null == value && copyOptions.ignoreNullValue) { continue;// 当允许跳过空时,跳过 } @@ -272,8 +278,13 @@ public class BeanCopier implements Copier, Serializable { } } - // 执行set方法注入值 - setterMethod.invoke(bean, value); + if(null == setterMethod){ + // 直接注入值 + ReflectUtil.setFieldValue(bean, field, value); + } else{ + // 执行set方法注入值 + setterMethod.invoke(bean, value); + } } catch (Exception e) { if (false ==copyOptions.ignoreError) { throw new UtilException(e, "Inject [{}] error!", prop.getFieldName()); diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java index 0e482d01d..2ab2a7e58 100644 --- a/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/copier/provider/MapValueProvider.java @@ -1,13 +1,13 @@ package cn.hutool.core.bean.copier.provider; -import java.lang.reflect.Type; -import java.util.Map; - import cn.hutool.core.bean.copier.ValueProvider; import cn.hutool.core.convert.Convert; import cn.hutool.core.map.CaseInsensitiveMap; import cn.hutool.core.util.StrUtil; +import java.lang.reflect.Type; +import java.util.Map; + /** * Map值提供者 * @@ -41,7 +41,7 @@ public class MapValueProvider implements ValueProvider { //检查下划线模式 value = map.get(StrUtil.toUnderlineCase(key)); } - + return Convert.convert(valueType, 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 3ec58fc6c..8e1179866 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 @@ -1,8 +1,5 @@ package cn.hutool.core.convert.impl; -import java.lang.reflect.Type; -import java.util.Map; - import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.BeanCopier; import cn.hutool.core.bean.copier.CopyOptions; @@ -12,6 +9,9 @@ import cn.hutool.core.map.MapProxy; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.TypeUtil; +import java.lang.reflect.Type; +import java.util.Map; + /** * Bean转换器,支持: *
@@ -69,7 +69,7 @@ public class BeanConverter extends AbstractConverter {
 				// 将Map动态代理为Bean
 				return MapProxy.create((Map)value).toProxyBean(this.beanClass);
 			}
-			
+
 			//限定被转换对象类型
 			return BeanCopier.create(value, ReflectUtil.newInstanceIfPossible(this.beanClass), this.beanType, this.copyOptions).copy();
 		}
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 ec613194b..97ea9081c 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
@@ -3,7 +3,6 @@ package cn.hutool.core.bean;
 import cn.hutool.core.bean.copier.CopyOptions;
 import cn.hutool.core.bean.copier.ValueProvider;
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Console;
 import cn.hutool.core.map.MapUtil;
 import lombok.Getter;
 import lombok.Setter;
@@ -101,6 +100,20 @@ public class BeanUtilTest {
 		Assert.assertEquals(12, person.getAge());
 	}
 
+	/**
+	 * 测试public类型的字段注入是否成功
+	 */
+	@Test
+	public void mapToBeanTest2() {
+		HashMap map = CollUtil.newHashMap();
+		map.put("name", "Joe");
+		map.put("age", 12);
+
+		Person2 person = BeanUtil.mapToBean(map, Person2.class, CopyOptions.create());
+		Assert.assertEquals("Joe", person.name);
+		Assert.assertEquals(12, person.age);
+	}
+
 	@Test
 	public void beanToMapTest() {
 		SubPerson person = new SubPerson();
@@ -264,4 +277,10 @@ public class BeanUtilTest {
 		private int age;
 		private String openid;
 	}
+
+	public static class Person2 {
+		public String name;
+		public int age;
+		public String openid;
+	}
 }
diff --git a/hutool-core/src/test/java/cn/hutool/core/convert/ConvertToBeanTest.java b/hutool-core/src/test/java/cn/hutool/core/convert/ConvertToBeanTest.java
index 65e3f725f..b9ed303d9 100644
--- a/hutool-core/src/test/java/cn/hutool/core/convert/ConvertToBeanTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/convert/ConvertToBeanTest.java
@@ -80,4 +80,11 @@ public class ConvertToBeanTest {
 		Assert.assertEquals("测试A11", subPerson.getName());
 		Assert.assertEquals("11213232", subPerson.getOpenid());
 	}
+
+	@Test
+	public void nullStrToBeanTest(){
+		String nullStr = "null";
+		final SubPerson subPerson = Convert.convert(SubPerson.class, nullStr);
+		Assert.assertNull(subPerson);
+	}
 }