mirror of
https://gitee.com/dromara/hutool.git
synced 2025-05-04 12:47:59 +08:00
fix bug
This commit is contained in:
parent
c075b30858
commit
f914faa0de
@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
# 5.7.20 (2022-01-07)
|
# 5.7.20 (2022-01-08)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
|
* 【core 】 修复setter重载导致匹配错误(issue#2082@Github)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.7.19 (2022-01-07)
|
# 5.7.19 (2022-01-07)
|
||||||
|
@ -140,14 +140,12 @@ public class BeanDesc implements Serializable {
|
|||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
private BeanDesc init() {
|
private BeanDesc init() {
|
||||||
final Method[] methods = ReflectUtil.getMethods(this.beanClass);
|
final Method[] gettersAndSetters = ReflectUtil.getMethods(this.beanClass, ReflectUtil::isGetterOrSetterIgnoreCase);
|
||||||
PropDesc prop;
|
PropDesc prop;
|
||||||
for (Field field : ReflectUtil.getFields(this.beanClass)) {
|
for (Field field : ReflectUtil.getFields(this.beanClass)) {
|
||||||
if (false == ModifierUtil.isStatic(field) &&
|
// 排除静态属性和对象子类
|
||||||
// 排除对象子类
|
if (false == ModifierUtil.isStatic(field) && false == ReflectUtil.isOuterClassField(field)) {
|
||||||
false == "this$0".equals(field.getName())) {
|
prop = createProp(field, gettersAndSetters);
|
||||||
//只针对非static属性
|
|
||||||
prop = createProp(field, methods);
|
|
||||||
// 只有不存在时才放入,防止父类属性覆盖子类属性
|
// 只有不存在时才放入,防止父类属性覆盖子类属性
|
||||||
this.propMap.putIfAbsent(prop.getFieldName(), prop);
|
this.propMap.putIfAbsent(prop.getFieldName(), prop);
|
||||||
}
|
}
|
||||||
@ -190,12 +188,12 @@ public class BeanDesc implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 查找字段对应的Getter和Setter方法
|
* 查找字段对应的Getter和Setter方法
|
||||||
*
|
*
|
||||||
* @param field 字段
|
* @param field 字段
|
||||||
* @param methods 类中所有的方法
|
* @param gettersOrSetters 类中所有的Getter或Setter方法
|
||||||
* @param ignoreCase 是否忽略大小写匹配
|
* @param ignoreCase 是否忽略大小写匹配
|
||||||
* @return PropDesc
|
* @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 String fieldName = field.getName();
|
||||||
final Class<?> fieldType = field.getType();
|
final Class<?> fieldType = field.getType();
|
||||||
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
|
final boolean isBooleanField = BooleanUtil.isBoolean(fieldType);
|
||||||
@ -203,24 +201,19 @@ public class BeanDesc implements Serializable {
|
|||||||
Method getter = null;
|
Method getter = null;
|
||||||
Method setter = null;
|
Method setter = null;
|
||||||
String methodName;
|
String methodName;
|
||||||
Class<?>[] parameterTypes;
|
for (Method method : gettersOrSetters) {
|
||||||
for (Method method : methods) {
|
|
||||||
parameterTypes = method.getParameterTypes();
|
|
||||||
if (parameterTypes.length > 1) {
|
|
||||||
// 多于1个参数说明非Getter或Setter
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
methodName = method.getName();
|
methodName = method.getName();
|
||||||
if (parameterTypes.length == 0) {
|
if (method.getParameterCount() == 0) {
|
||||||
// 无参数,可能为Getter方法
|
// 无参数,可能为Getter方法
|
||||||
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
if (isMatchGetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||||
// 方法名与字段名匹配,则为Getter方法
|
// 方法名与字段名匹配,则为Getter方法
|
||||||
getter = method;
|
getter = method;
|
||||||
}
|
}
|
||||||
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
} else if (isMatchSetter(methodName, fieldName, isBooleanField, ignoreCase)) {
|
||||||
// 只有一个参数的情况下方法名与字段名对应匹配,则为Setter方法
|
// setter方法的参数类型和字段类型必须一致,或参数类型是字段类型的子类
|
||||||
setter = method;
|
if(fieldType.isAssignableFrom(method.getParameterTypes()[0])){
|
||||||
|
setter = method;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (null != getter && null != setter) {
|
if (null != getter && null != setter) {
|
||||||
// 如果Getter和Setter方法都找到了,不再继续寻找
|
// 如果Getter和Setter方法都找到了,不再继续寻找
|
||||||
@ -261,15 +254,6 @@ public class BeanDesc implements Serializable {
|
|||||||
handledFieldName = StrUtil.upperFirst(fieldName);
|
handledFieldName = StrUtil.upperFirst(fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false == methodName.startsWith("get") && false == methodName.startsWith("is")) {
|
|
||||||
// 非标准Getter方法
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ("getclass".equals(methodName)) {
|
|
||||||
//跳过getClass方法
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 针对Boolean类型特殊检查
|
// 针对Boolean类型特殊检查
|
||||||
if (isBooleanField) {
|
if (isBooleanField) {
|
||||||
if (fieldName.startsWith("is")) {
|
if (fieldName.startsWith("is")) {
|
||||||
|
@ -88,9 +88,8 @@ public class BeanUtil {
|
|||||||
*/
|
*/
|
||||||
public static boolean hasSetter(Class<?> clazz) {
|
public static boolean hasSetter(Class<?> clazz) {
|
||||||
if (ClassUtil.isNormalClass(clazz)) {
|
if (ClassUtil.isNormalClass(clazz)) {
|
||||||
final Method[] methods = clazz.getMethods();
|
for (Method method : clazz.getMethods()) {
|
||||||
for (Method method : methods) {
|
if (method.getParameterCount() == 1 && method.getName().startsWith("set")) {
|
||||||
if (method.getParameterTypes().length == 1 && method.getName().startsWith("set")) {
|
|
||||||
// 检测包含标准的setXXX方法即视为标准的JavaBean
|
// 检测包含标准的setXXX方法即视为标准的JavaBean
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -110,7 +109,7 @@ public class BeanUtil {
|
|||||||
public static boolean hasGetter(Class<?> clazz) {
|
public static boolean hasGetter(Class<?> clazz) {
|
||||||
if (ClassUtil.isNormalClass(clazz)) {
|
if (ClassUtil.isNormalClass(clazz)) {
|
||||||
for (Method method : clazz.getMethods()) {
|
for (Method method : clazz.getMethods()) {
|
||||||
if (method.getParameterTypes().length == 0) {
|
if (method.getParameterCount() == 0) {
|
||||||
if (method.getName().startsWith("get") || method.getName().startsWith("is")) {
|
if (method.getName().startsWith("get") || method.getName().startsWith("is")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import java.lang.reflect.Method;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 方法句柄{@link MethodHandle}封装工具类<br>
|
* 方法句柄{@link MethodHandle}封装工具类<br>
|
||||||
|
* 方法句柄是一个有类型的,可以直接执行的指向底层方法、构造器、field等的引用,可以简单理解为函数指针,它是一种更加底层的查找、调整和调用方法的机制。
|
||||||
* 参考:
|
* 参考:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>https://stackoverflow.com/questions/22614746/how-do-i-invoke-java-8-default-methods-reflectively</li>
|
* <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">
|
* <pre class="code">
|
||||||
* interface Duck {
|
* interface Duck {
|
||||||
@ -159,7 +160,7 @@ public class MethodHandleUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行接口或对象中的方法<br>
|
* 执行接口或对象中的特殊方法(private、static等)<br>
|
||||||
*
|
*
|
||||||
* <pre class="code">
|
* <pre class="code">
|
||||||
* interface Duck {
|
* 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
|
// --------------------------------------------------------------------------------------------------------- method
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -673,11 +684,12 @@ public class ReflectUtil {
|
|||||||
* @return 是否为equals方法
|
* @return 是否为equals方法
|
||||||
*/
|
*/
|
||||||
public static boolean isEqualsMethod(Method method) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
final Class<?>[] paramTypes = method.getParameterTypes();
|
return (method.getParameterTypes()[0] == Object.class);
|
||||||
return (1 == paramTypes.length && paramTypes[0] == Object.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -712,9 +724,67 @@ public class ReflectUtil {
|
|||||||
* @since 5.1.1
|
* @since 5.1.1
|
||||||
*/
|
*/
|
||||||
public static boolean isEmptyParam(Method method) {
|
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
|
// --------------------------------------------------------------------------------------------------------- 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