mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-24 18:04:54 +08:00
fix bug
This commit is contained in:
parent
bdeae6f02e
commit
6ed92bfacf
@ -3,10 +3,11 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# 5.7.20 (2022-01-07)
|
||||
# 5.7.20 (2022-01-08)
|
||||
|
||||
### 🐣新特性
|
||||
### 🐞Bug修复
|
||||
* 【core 】 修复setter重载导致匹配错误(issue#2082@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.7.19 (2022-01-07)
|
||||
|
@ -140,14 +140,12 @@ public class BeanDesc implements Serializable {
|
||||
* @return this
|
||||
*/
|
||||
private BeanDesc init() {
|
||||
final Method[] methods = ReflectUtil.getMethods(this.beanClass);
|
||||
final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase);
|
||||
PropDesc prop;
|
||||
for (Field field : ReflectUtil.getFields(this.beanClass)) {
|
||||
if (false == ModifierUtil.isStatic(field) &&
|
||||
// 排除对象子类
|
||||
false == "this$0".equals(field.getName())) {
|
||||
//只针对非static属性
|
||||
prop = createProp(field, methods);
|
||||
// 排除静态属性和对象子类
|
||||
if (false == ModifierUtil.isStatic(field) && false == ReflectUtil.isOuterClassField(field)) {
|
||||
prop = createProp(field, gettersAndSetters);
|
||||
// 只有不存在时才放入,防止父类属性覆盖子类属性
|
||||
this.propMap.putIfAbsent(prop.getFieldName(), prop);
|
||||
}
|
||||
@ -190,12 +188,12 @@ public class BeanDesc implements Serializable {
|
||||
/**
|
||||
* 查找字段对应的Getter和Setter方法
|
||||
*
|
||||
* @param field 字段
|
||||
* @param methods 类中所有的方法
|
||||
* @param ignoreCase 是否忽略大小写匹配
|
||||
* @param field 字段
|
||||
* @param gettersOrSetters 类中所有的Getter或Setter方法
|
||||
* @param ignoreCase 是否忽略大小写匹配
|
||||
* @return PropDesc
|
||||
*/
|
||||
private PropDesc findProp(Field field, Method[] methods, boolean ignoreCase) {
|
||||
private PropDesc findProp(Field field, Method[] gettersOrSetters, boolean ignoreCase) {
|
||||
final String fieldName = field.getName();
|
||||
final Class<?> fieldType = field.getType();
|
||||
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
|
||||
@ -203,24 +201,19 @@ public class BeanDesc implements Serializable {
|
||||
Method getter = null;
|
||||
Method setter = null;
|
||||
String methodName;
|
||||
Class<?>[] parameterTypes;
|
||||
for (Method method : methods) {
|
||||
parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length > 1) {
|
||||
// 多于1个参数说明非Getter或Setter
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Method method : gettersOrSetters) {
|
||||
methodName = method.getName();
|
||||
if (parameterTypes.length == 0) {
|
||||
if (method.getParameterCount() == 0) {
|
||||
// 无参数,可能为Getter方法
|
||||
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||
// 方法名与字段名匹配,则为Getter方法
|
||||
getter = method;
|
||||
}
|
||||
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||
// 只有一个参数的情况下方法名与字段名对应匹配,则为Setter方法
|
||||
setter = method;
|
||||
// setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类
|
||||
if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){
|
||||
setter = method;
|
||||
}
|
||||
}
|
||||
if (null != getter && null != setter) {
|
||||
// 如果Getter和Setter方法都找到了,不再继续寻找
|
||||
@ -261,15 +254,6 @@ public class BeanDesc implements Serializable {
|
||||
handledFieldName = StrUtil.upperFirst(fieldName);
|
||||
}
|
||||
|
||||
if (false == methodName.startsWith("get") && false == methodName.startsWith("is")) {
|
||||
// 非标准Getter方法
|
||||
return false;
|
||||
}
|
||||
if ("getclass".equals(methodName)) {
|
||||
//跳过getClass方法
|
||||
return false;
|
||||
}
|
||||
|
||||
// 针对Boolean类型特殊检查
|
||||
if (isBooleanField) {
|
||||
if (fieldName.startsWith("is")) {
|
||||
|
@ -88,9 +88,8 @@ public class BeanUtil {
|
||||
*/
|
||||
public static boolean hasSetter(Class<?> clazz) {
|
||||
if (ClassUtil.isNormalClass(clazz)) {
|
||||
final Method[] methods = clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getParameterTypes().length == 1 && method.getName().startsWith("set")) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterCount() == 1 && method.getName().startsWith("set")) {
|
||||
// 检测包含标准的setXXX方法即视为标准的JavaBean
|
||||
return true;
|
||||
}
|
||||
@ -110,7 +109,7 @@ public class BeanUtil {
|
||||
public static boolean hasGetter(Class<?> clazz) {
|
||||
if (ClassUtil.isNormalClass(clazz)) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
if (method.getParameterCount() == 0) {
|
||||
if (method.getName().startsWith("get") || method.getName().startsWith("is")) {
|
||||
return true;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 方法句柄{@link MethodHandle}封装工具类<br>
|
||||
* 方法句柄是一个有类型的,可以直接执行的指向底层方法、构造器、field等的引用,可以简单理解为函数指针,它是一种更加底层的查找、调整和调用方法的机制。
|
||||
* 参考:
|
||||
* <ul>
|
||||
* <li>https://stackoverflow.com/questions/22614746/how-do-i-invoke-java-8-default-methods-reflectively</li>
|
||||
@ -113,7 +114,7 @@ public class MethodHandleUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行接口或对象中的方法<br>
|
||||
* 执行接口或对象中的特殊方法(private、static等)<br>
|
||||
*
|
||||
* <pre class="code">
|
||||
* interface Duck {
|
||||
@ -159,7 +160,7 @@ public class MethodHandleUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行接口或对象中的方法<br>
|
||||
* 执行接口或对象中的特殊方法(private、static等)<br>
|
||||
*
|
||||
* <pre class="code">
|
||||
* interface Duck {
|
||||
|
@ -344,6 +344,17 @@ public class ReflectUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为父类引用字段<br>
|
||||
* 当字段所在类是对象子类时(对象中定义的非static的class),会自动生成一个以"this$0"为名称的字段,指向父类对象
|
||||
* @param field 字段
|
||||
* @return 是否为父类引用字段
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static boolean isOuterClassField(Field field){
|
||||
return "this$0".equals(field.getName());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------- method
|
||||
|
||||
/**
|
||||
@ -673,11 +684,12 @@ public class ReflectUtil {
|
||||
* @return 是否为equals方法
|
||||
*/
|
||||
public static boolean isEqualsMethod(Method method) {
|
||||
if (method == null || false == "equals".equals(method.getName())) {
|
||||
if (method == null ||
|
||||
1 != method.getParameterCount() ||
|
||||
false == "equals".equals(method.getName())) {
|
||||
return false;
|
||||
}
|
||||
final Class<?>[] paramTypes = method.getParameterTypes();
|
||||
return (1 == paramTypes.length && paramTypes[0] == Object.class);
|
||||
return (method.getParameterTypes()[0] == Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -712,9 +724,67 @@ public class ReflectUtil {
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public static boolean isEmptyParam(Method method) {
|
||||
return method.getParameterTypes().length == 0;
|
||||
return method.getParameterCount() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定方法是否为Getter或者Setter方法,规则为:<br>
|
||||
* <ul>
|
||||
* <li>方法参数必须为0个或1个</li>
|
||||
* <li>如果是无参方法,则判断是否以“get”或“is”开头</li>
|
||||
* <li>如果方法参数1个,则判断是否以“set”开头</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param method 方法
|
||||
* @return 是否为Getter或者Setter方法
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static boolean isGetterOrSetterIgnoreCase(Method method) {
|
||||
return isGetterOrSetter(method, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定方法是否为Getter或者Setter方法,规则为:<br>
|
||||
* <ul>
|
||||
* <li>方法参数必须为0个或1个</li>
|
||||
* <li>方法名称不能是getClass</li>
|
||||
* <li>如果是无参方法,则判断是否以“get”或“is”开头</li>
|
||||
* <li>如果方法参数1个,则判断是否以“set”开头</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param method 方法
|
||||
* @param ignoreCase 是否忽略方法名的大小写
|
||||
* @return 是否为Getter或者Setter方法
|
||||
* @since 5.7.20
|
||||
*/
|
||||
public static boolean isGetterOrSetter(Method method, boolean ignoreCase) {
|
||||
if (null == method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 参数个数必须为0或1
|
||||
final int parameterCount = method.getParameterCount();
|
||||
if (parameterCount > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = method.getName();
|
||||
// 跳过getClass这个特殊方法
|
||||
if("getClass".equals(name)){
|
||||
return false;
|
||||
}
|
||||
if(ignoreCase){
|
||||
name = name.toLowerCase();
|
||||
}
|
||||
switch (parameterCount) {
|
||||
case 0:
|
||||
return name.startsWith("get") || name.startsWith("is");
|
||||
case 1:
|
||||
return name.startsWith("set");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------------------- newInstance
|
||||
|
||||
/**
|
||||
|
33
hutool-core/src/test/java/cn/hutool/core/bean/Issue2082Test.java
Executable file
33
hutool-core/src/test/java/cn/hutool/core/bean/Issue2082Test.java
Executable file
@ -0,0 +1,33 @@
|
||||
package cn.hutool.core.bean;
|
||||
|
||||
import lombok.Data;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* https://github.com/dromara/hutool/issues/2082<br>
|
||||
* 当setXXX有重载方法的时候,BeanDesc中会匹配到重载方法,增加类型检查来规避之
|
||||
*/
|
||||
public class Issue2082Test {
|
||||
|
||||
@Test
|
||||
public void toBeanTest() {
|
||||
TestBean2 testBean2 = new TestBean2();
|
||||
TestBean test = BeanUtil.toBean(testBean2, TestBean.class);
|
||||
Assert.assertNull(test.getId());
|
||||
}
|
||||
|
||||
@Data
|
||||
static class TestBean {
|
||||
private Long id;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = Long.valueOf(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
static class TestBean2 {
|
||||
private String id;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user