Browse Source

add public field support

Looly 6 years ago
parent
commit
dc94761c43

+ 1 - 0
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修复

+ 29 - 5
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对象<br>
-	 * 判定方法是是否存在只有一个参数的setXXX方法
+	 * 判断是否为Bean对象,判定方法是:
+	 *
+	 * <pre>
+	 *     1、是否存在只有一个参数的setXXX方法
+	 *     2、是否存在public类型的字段
+	 * </pre>
 	 *
 	 * @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;
@@ -97,6 +102,25 @@ public class BeanUtil {
 	}
 
 	/**
+	 * 指定类中是否有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
 	 *
 	 * @param bean 普通Bean或Map

+ 32 - 21
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<T> implements Copier<T>, Serializable {
 		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时跳过
@@ -231,30 +236,31 @@ public class BeanCopier<T> implements Copier<T>, 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<T> implements Copier<T>, 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());

+ 4 - 4
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<String> {
 			//检查下划线模式
 			value = map.get(StrUtil.toUnderlineCase(key));
 		}
-		
+
 		return Convert.convert(valueType, value);
 	}
 

+ 4 - 4
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转换器,支持:
  * <pre>
@@ -69,7 +69,7 @@ public class BeanConverter<T> extends AbstractConverter<T> {
 				// 将Map动态代理为Bean
 				return MapProxy.create((Map<?, ?>)value).toProxyBean(this.beanClass);
 			}
-			
+
 			//限定被转换对象类型
 			return BeanCopier.create(value, ReflectUtil.newInstanceIfPossible(this.beanClass), this.beanType, this.copyOptions).copy();
 		}

+ 20 - 1
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<String, Object> 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;
+	}
 }

+ 7 - 0
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);
+	}
 }