add public field support

This commit is contained in:
Looly 2019-12-09 17:02:27 +08:00
parent bddb97704e
commit dc94761c43
7 changed files with 97 additions and 35 deletions

View File

@ -8,6 +8,7 @@
### 新特性
* 【core 】 新增WatchServerissue#440@Github
* 【core 】 ReflectUtil.getFieldValue支持staticissue#662@Github
* 【core 】 改进Bean判断和注入逻辑支持public字段注入issue#I1689L@Gitee
### Bug修复

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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