diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java index af7a4c167..00ec4c5df 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 looly(loolly@aliyun.com) + * Copyright (c) 2024. looly(loolly@aliyun.com) * Hutool is licensed under Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: @@ -12,95 +12,31 @@ package org.dromara.hutool.core.bean; -import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.map.CaseInsensitiveMap; -import org.dromara.hutool.core.reflect.FieldUtil; -import org.dromara.hutool.core.reflect.ModifierUtil; -import org.dromara.hutool.core.reflect.method.MethodUtil; -import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.util.BooleanUtil; - import java.io.Serializable; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.Map; /** - * Bean信息描述做为BeanInfo替代方案,此对象持有JavaBean中的setters和getters等相关信息描述
- * 查找Getter和Setter方法时会: + * Bean描述,通过反射等方式获取Bean的setter、getter、字段等信息 * - *
- * 1. 忽略字段和方法名的大小写
- * 2. Getter查找getXXX、isXXX、getIsXXX
- * 3. Setter查找setXXX、setIsXXX
- * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
- * 
- * - * @author looly - * @since 3.1.2 + * @author Looly + * @since 6.0.0 */ -public class BeanDesc implements Serializable { - private static final long serialVersionUID = 1L; - - /** - * Bean类 - */ - private final Class beanClass; - /** - * 属性Map - */ - private final Map propMap = new LinkedHashMap<>(); - - /** - * 构造 - * - * @param beanClass Bean类 - */ - public BeanDesc(final Class beanClass) { - Assert.notNull(beanClass); - this.beanClass = beanClass; - init(); - } - - /** - * 获取Bean的全类名 - * - * @return Bean的类名 - */ - public String getName() { - return this.beanClass.getName(); - } - - /** - * 获取Bean的简单类名 - * - * @return Bean的类名 - */ - public String getSimpleName() { - return this.beanClass.getSimpleName(); - } - +public interface BeanDesc extends Serializable { /** * 获取字段名-字段属性Map * * @param ignoreCase 是否忽略大小写,true为忽略,false不忽略 * @return 字段名-字段属性Map */ - public Map getPropMap(final boolean ignoreCase) { - return ignoreCase ? new CaseInsensitiveMap<>(1, this.propMap) : this.propMap; - } + Map getPropMap(final boolean ignoreCase); /** * 获取字段属性列表 * * @return {@link PropDesc} 列表 */ - public Collection getProps() { - return this.propMap.values(); - } + Collection getProps(); /** * 获取属性,如果不存在返回null @@ -108,272 +44,5 @@ public class BeanDesc implements Serializable { * @param fieldName 字段名 * @return {@link PropDesc} */ - public PropDesc getProp(final String fieldName) { - return this.propMap.get(fieldName); - } - - /** - * 获得字段名对应的字段对象,如果不存在返回null - * - * @param fieldName 字段名 - * @return 字段值 - */ - public Field getField(final String fieldName) { - final PropDesc desc = this.propMap.get(fieldName); - return null == desc ? null : desc.getField(); - } - - /** - * 获取Getter方法,如果不存在返回null - * - * @param fieldName 字段名 - * @return Getter方法 - */ - public Method getGetter(final String fieldName) { - final PropDesc desc = this.propMap.get(fieldName); - return null == desc ? null : desc.getGetter(); - } - - /** - * 获取Setter方法,如果不存在返回null - * - * @param fieldName 字段名 - * @return Setter方法 - */ - public Method getSetter(final String fieldName) { - final PropDesc desc = this.propMap.get(fieldName); - return null == desc ? null : desc.getSetter(); - } - - // ------------------------------------------------------------------------------------------------------ Private method start - - /** - * 初始化
- * 只有与属性关联的相关Getter和Setter方法才会被读取,无关的getXXX和setXXX都被忽略 - */ - private void init() { - if (RecordUtil.isRecord(this.beanClass)) { - initForRecord(); - } else{ - initForBean(); - } - } - - /** - * 针对Record类的反射初始化 - */ - private void initForRecord() { - final Method[] getters = MethodUtil.getPublicMethods(this.beanClass, method -> 0 == method.getParameterCount()); - // 排除静态属性和对象子类 - final Field[] fields = FieldUtil.getFields(this.beanClass, field -> !ModifierUtil.isStatic(field) && !FieldUtil.isOuterClassField(field)); - for (final Field field : fields) { - for (final Method getter : getters) { - if (field.getName().equals(getter.getName())) { - //record对象,getter方法与字段同名 - final PropDesc prop = new PropDesc(field, getter, null); - this.propMap.putIfAbsent(prop.getFieldName(), prop); - } - } - } - } - - /** - * 普通Bean初始化 - */ - private void initForBean() { - final Method[] gettersAndSetters = MethodUtil.getPublicMethods(this.beanClass, MethodUtil::isGetterOrSetterIgnoreCase); - // 排除静态属性和对象子类 - final Field[] fields = FieldUtil.getFields(this.beanClass, field -> !ModifierUtil.isStatic(field) && !FieldUtil.isOuterClassField(field)); - PropDesc prop; - for (final Field field : fields) { - prop = createProp(field, gettersAndSetters); - // 只有不存在时才放入,防止父类属性覆盖子类属性 - this.propMap.putIfAbsent(prop.getFieldName(), prop); - } - } - - /** - * 根据字段创建属性描述
- * 查找Getter和Setter方法时会: - * - *
-	 * 1. 忽略字段和方法名的大小写
-	 * 2. Getter查找getXXX、isXXX、getIsXXX
-	 * 3. Setter查找setXXX、setIsXXX
-	 * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
-	 * 
- * - * @param field 字段 - * @param methods 类中所有的方法 - * @return {@link PropDesc} - * @since 4.0.2 - */ - private PropDesc createProp(final Field field, final Method[] methods) { - final PropDesc prop = findProp(field, methods, false); - // 忽略大小写重新匹配一次 - if (null == prop.getter || null == prop.setter) { - final PropDesc propIgnoreCase = findProp(field, methods, true); - if (null == prop.getter) { - prop.getter = propIgnoreCase.getter; - } - if (null == prop.setter) { - prop.setter = propIgnoreCase.setter; - } - } - - return prop; - } - - /** - * 查找字段对应的Getter和Setter方法 - * - * @param field 字段 - * @param gettersOrSetters 类中所有的Getter或Setter方法 - * @param ignoreCase 是否忽略大小写匹配 - * @return PropDesc - */ - private PropDesc findProp(final Field field, final Method[] gettersOrSetters, final boolean ignoreCase) { - final String fieldName = field.getName(); - final Class fieldType = field.getType(); - final boolean isBooleanField = BooleanUtil.isBoolean(fieldType); - - // Getter: name -> getName, Setter: name -> setName - final Method[] getterAndSetter = findGetterAndSetter(fieldName, fieldType, gettersOrSetters, ignoreCase); - - if (isBooleanField) { - if (null == getterAndSetter[0]) { - // isName -> isName or isIsName - // name -> isName - getterAndSetter[0] = findGetterForBoolean(fieldName, gettersOrSetters, ignoreCase); - } - if (null == getterAndSetter[1]) { - // isName -> setName - getterAndSetter[1] = findSetterForBoolean(fieldName, gettersOrSetters, ignoreCase); - } - } - - return new PropDesc(field, getterAndSetter[0], getterAndSetter[1]); - } - - /** - * 查找字段对应的Getter和Setter方法
- * 此方法不区分是否为boolean字段,查找规则为: - *
    - *
  • Getter要求无参数且返回值是字段类型或字段的父类
  • - *
  • Getter中,如果字段为name,匹配getName
  • - *
  • Setter要求一个参数且参数必须为字段类型或字段的子类
  • - *
  • Setter中,如果字段为name,匹配setName
  • - *
- * - * @param fieldName 字段名 - * @param fieldType 字段类型 - * @param gettersOrSetters 类中所有的Getter或Setter方法 - * @return PropDesc - */ - private Method[] findGetterAndSetter(final String fieldName, final Class fieldType, - final Method[] gettersOrSetters, final boolean ignoreCase) { - Method getter = null; - Method setter = null; - String methodName; - for (final Method method : gettersOrSetters) { - methodName = method.getName(); - if (0 == method.getParameterCount()) { - // 无参数,可能为Getter方法 - if (StrUtil.equals(methodName, StrUtil.genGetter(fieldName), ignoreCase) && - method.getReturnType().isAssignableFrom(fieldType)) { - // getter的返回类型必须为字段类型或字段的父类 - getter = method; - } - } else if (StrUtil.equals(methodName, StrUtil.genSetter(fieldName), ignoreCase) && - fieldType.isAssignableFrom(method.getParameterTypes()[0])) { - // setter方法的参数必须为字段类型或字段的子类 - setter = method; - } - if (null != getter && null != setter) { - // 如果Getter和Setter方法都找到了,不再继续寻找 - break; - } - } - - return new Method[]{getter, setter}; - } - - /** - * 针对Boolean或boolean类型字段,查找其对应的Getter方法,规则为: - *
    - *
  • 方法必须无参数且返回boolean或Boolean
  • - *
  • 如果字段为isName, 匹配isName、isIsName方法,两个方法均存在,则按照提供的方法数组优先匹配。
  • - *
  • 如果字段为name, 匹配isName方法
  • - *
- *

- * 需要注意的是,以下两种格式不匹配,由{@link #findGetterAndSetter(String, Class, Method[], boolean)}完成: - *

    - *
  • 如果字段为name, 匹配getName
  • - *
  • 如果字段为isName, 匹配getIsName
  • - *
- * - * @param fieldName 字段名 - * @param gettersOrSetters 所有方法 - * @param ignoreCase 是否忽略大小写 - * @return 查找到的方法,{@code null}表示未找到 - */ - private Method findGetterForBoolean(final String fieldName, final Method[] gettersOrSetters, final boolean ignoreCase) { - // 查找isXXX - return ArrayUtil.get(gettersOrSetters, m -> { - if (0 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getReturnType())) { - // getter方法要求无参数且返回boolean或Boolean - return false; - } - - if (StrUtil.startWith(fieldName, "is", ignoreCase)) { - // isName -》 isName - if (StrUtil.equals(fieldName, m.getName(), ignoreCase)) { - return true; - } - } - - // name -》 isName - // isName -》 isIsName - return StrUtil.equals(StrUtil.upperFirstAndAddPre(fieldName, "is"), m.getName(), ignoreCase); - }); - } - - /** - * 针对Boolean或boolean类型字段,查找其对应的Setter方法,规则为: - *
    - *
  • 方法必须为1个boolean或Boolean参数
  • - *
  • 如果字段为isName,匹配setName
  • - *
- *

- * 需要注意的是,以下两种格式不匹配,由{@link #findGetterAndSetter(String, Class, Method[], boolean)}完成: - *

    - *
  • 如果字段为name, 匹配setName
  • - *
  • 如果字段为isName, 匹配setIsName
  • - *
- * - * @param fieldName 字段名 - * @param gettersOrSetters 所有方法 - * @param ignoreCase 是否忽略大小写 - * @return 查找到的方法,{@code null}表示未找到 - */ - private Method findSetterForBoolean(final String fieldName, final Method[] gettersOrSetters, final boolean ignoreCase) { - // 查找isXXX - return ArrayUtil.get(gettersOrSetters, m -> { - if (1 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getParameterTypes()[0])) { - // setter方法要求1个boolean或Boolean参数 - return false; - } - - if (StrUtil.startWith(fieldName, "is", ignoreCase)) { - // isName -》 setName - return StrUtil.equals( - "set" + StrUtil.removePrefix(fieldName, "is", ignoreCase), - m.getName(), ignoreCase); - } - - // 其它不匹配 - return false; - }); - } - // ------------------------------------------------------------------------------------------------------ Private method end + PropDesc getProp(final String fieldName); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java index e026d873d..f8976b036 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanDescCache.java @@ -27,17 +27,17 @@ public enum BeanDescCache { */ INSTANCE; - private final WeakConcurrentMap, BeanDesc> bdCache = new WeakConcurrentMap<>(); + private final WeakConcurrentMap, StrictBeanDesc> bdCache = new WeakConcurrentMap<>(); /** - * 获得属性名和{@link BeanDesc}Map映射 + * 获得属性名和{@link StrictBeanDesc}Map映射 * * @param beanClass Bean的类 * @param supplier 对象不存在时创建对象的函数 - * @return 属性名和 {@link BeanDesc}映射 + * @return 属性名和 {@link StrictBeanDesc}映射 * @since 5.4.2 */ - public BeanDesc getBeanDesc(final Class beanClass, final SerSupplier supplier) { + public StrictBeanDesc getBeanDesc(final Class beanClass, final SerSupplier supplier) { return bdCache.computeIfAbsent(beanClass, (key) -> supplier.get()); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java index 13b380e12..0d28cdb29 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/BeanUtil.java @@ -76,14 +76,14 @@ public class BeanUtil { } /** - * 获取{@link BeanDesc} Bean描述信息 + * 获取{@link StrictBeanDesc} Bean描述信息 * * @param clazz Bean类 - * @return {@link BeanDesc} + * @return {@link StrictBeanDesc} * @since 3.1.2 */ - public static BeanDesc getBeanDesc(final Class clazz) { - return BeanDescCache.INSTANCE.getBeanDesc(clazz, () -> new BeanDesc(clazz)); + public static StrictBeanDesc getBeanDesc(final Class clazz) { + return BeanDescCache.INSTANCE.getBeanDesc(clazz, () -> new StrictBeanDesc(clazz)); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java index 02374287c..d5fab968d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/PropDesc.java @@ -16,10 +16,7 @@ import org.dromara.hutool.core.annotation.AnnotationUtil; import org.dromara.hutool.core.annotation.PropIgnore; import org.dromara.hutool.core.convert.Convert; import org.dromara.hutool.core.func.LambdaUtil; -import org.dromara.hutool.core.reflect.FieldUtil; -import org.dromara.hutool.core.reflect.ModifierUtil; -import org.dromara.hutool.core.reflect.ReflectUtil; -import org.dromara.hutool.core.reflect.TypeUtil; +import org.dromara.hutool.core.reflect.*; import org.dromara.hutool.core.reflect.method.MethodUtil; import java.beans.Transient; @@ -392,11 +389,11 @@ public class PropDesc { * @since 5.3.11 */ private boolean isTransientForGet() { - boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT); + boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierType.TRANSIENT); // 检查Getter方法 if (!isTransient && null != this.getter) { - isTransient = ModifierUtil.hasModifier(this.getter, ModifierUtil.ModifierType.TRANSIENT); + isTransient = ModifierUtil.hasModifier(this.getter, ModifierType.TRANSIENT); // 检查注解 if (!isTransient) { @@ -414,11 +411,11 @@ public class PropDesc { * @since 5.3.11 */ private boolean isTransientForSet() { - boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierUtil.ModifierType.TRANSIENT); + boolean isTransient = ModifierUtil.hasModifier(this.field, ModifierType.TRANSIENT); // 检查Getter方法 if (!isTransient && null != this.setter) { - isTransient = ModifierUtil.hasModifier(this.setter, ModifierUtil.ModifierType.TRANSIENT); + isTransient = ModifierUtil.hasModifier(this.setter, ModifierType.TRANSIENT); // 检查注解 if (!isTransient) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/StrictBeanDesc.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/StrictBeanDesc.java new file mode 100644 index 000000000..c277a3138 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/StrictBeanDesc.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.bean; + +import org.dromara.hutool.core.bean.path.AbstractBeanDesc; +import org.dromara.hutool.core.reflect.FieldUtil; +import org.dromara.hutool.core.reflect.ModifierUtil; +import org.dromara.hutool.core.reflect.method.MethodUtil; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.BooleanUtil; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; + +/** + * 严格的Bean信息描述做为BeanInfo替代方案,此对象持有JavaBean中的setters和getters等相关信息描述
+ * 查找Getter和Setter方法时会: + * + *
+ * 1. 忽略字段和方法名的大小写
+ * 2. Getter查找getXXX、isXXX、getIsXXX
+ * 3. Setter查找setXXX、setIsXXX
+ * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
+ * 
+ * + * @author looly + * @since 3.1.2 + */ +public class StrictBeanDesc extends AbstractBeanDesc { + private static final long serialVersionUID = 1L; + + /** + * 构造 + * + * @param beanClass Bean类 + */ + public StrictBeanDesc(final Class beanClass) { + super(beanClass); + init(); + } + + // ------------------------------------------------------------------------------------------------------ Private method start + + /** + * 初始化
+ * 只有与属性关联的相关Getter和Setter方法才会被读取,无关的getXXX和setXXX都被忽略 + */ + private void init() { + if (RecordUtil.isRecord(getBeanClass())) { + initForRecord(); + } else{ + initForBean(); + } + } + + /** + * 针对Record类的反射初始化 + */ + private void initForRecord() { + final Class beanClass = this.beanClass; + final Map propMap = this.propMap; + + final Method[] getters = MethodUtil.getPublicMethods(beanClass, method -> 0 == method.getParameterCount()); + // 排除静态属性和对象子类 + final Field[] fields = FieldUtil.getFields(beanClass, field -> !ModifierUtil.isStatic(field) && !FieldUtil.isOuterClassField(field)); + for (final Field field : fields) { + for (final Method getter : getters) { + if (field.getName().equals(getter.getName())) { + //record对象,getter方法与字段同名 + final PropDesc prop = new PropDesc(field, getter, null); + propMap.putIfAbsent(prop.getFieldName(), prop); + } + } + } + } + + /** + * 普通Bean初始化 + */ + private void initForBean() { + final Class beanClass = this.beanClass; + final Map propMap = this.propMap; + + final Method[] gettersAndSetters = MethodUtil.getPublicMethods(beanClass, MethodUtil::isGetterOrSetterIgnoreCase); + // 排除静态属性和对象子类 + final Field[] fields = FieldUtil.getFields(beanClass, field -> !ModifierUtil.isStatic(field) && !FieldUtil.isOuterClassField(field)); + PropDesc prop; + for (final Field field : fields) { + prop = createProp(field, gettersAndSetters); + // 只有不存在时才放入,防止父类属性覆盖子类属性 + propMap.putIfAbsent(prop.getFieldName(), prop); + } + } + + /** + * 根据字段创建属性描述
+ * 查找Getter和Setter方法时会: + * + *
+	 * 1. 忽略字段和方法名的大小写
+	 * 2. Getter查找getXXX、isXXX、getIsXXX
+	 * 3. Setter查找setXXX、setIsXXX
+	 * 4. Setter忽略参数值与字段值不匹配的情况,因此有多个参数类型的重载时,会调用首次匹配的
+	 * 
+ * + * @param field 字段 + * @param methods 类中所有的方法 + * @return {@link PropDesc} + * @since 4.0.2 + */ + private PropDesc createProp(final Field field, final Method[] methods) { + final PropDesc prop = findProp(field, methods, false); + // 忽略大小写重新匹配一次 + if (null == prop.getter || null == prop.setter) { + final PropDesc propIgnoreCase = findProp(field, methods, true); + if (null == prop.getter) { + prop.getter = propIgnoreCase.getter; + } + if (null == prop.setter) { + prop.setter = propIgnoreCase.setter; + } + } + + return prop; + } + + /** + * 查找字段对应的Getter和Setter方法 + * + * @param field 字段 + * @param gettersOrSetters 类中所有的Getter或Setter方法 + * @param ignoreCase 是否忽略大小写匹配 + * @return PropDesc + */ + private PropDesc findProp(final Field field, final Method[] gettersOrSetters, final boolean ignoreCase) { + final String fieldName = field.getName(); + final Class fieldType = field.getType(); + final boolean isBooleanField = BooleanUtil.isBoolean(fieldType); + + // Getter: name -> getName, Setter: name -> setName + final Method[] getterAndSetter = findGetterAndSetter(fieldName, fieldType, gettersOrSetters, ignoreCase); + + if (isBooleanField) { + if (null == getterAndSetter[0]) { + // isName -> isName or isIsName + // name -> isName + getterAndSetter[0] = getGetterForBoolean(gettersOrSetters, fieldName, ignoreCase); + } + if (null == getterAndSetter[1]) { + // isName -> setName + getterAndSetter[1] = getSetterForBoolean(gettersOrSetters, fieldName, ignoreCase); + } + } + + return new PropDesc(field, getterAndSetter[0], getterAndSetter[1]); + } + + /** + * 查找字段对应的Getter和Setter方法
+ * 此方法不区分是否为boolean字段,查找规则为: + *
    + *
  • Getter要求无参数且返回值是字段类型或字段的父类
  • + *
  • Getter中,如果字段为name,匹配getName
  • + *
  • Setter要求一个参数且参数必须为字段类型或字段的子类
  • + *
  • Setter中,如果字段为name,匹配setName
  • + *
+ * + * @param fieldName 字段名 + * @param fieldType 字段类型 + * @param gettersOrSetters 类中所有的Getter或Setter方法 + * @return PropDesc + */ + private Method[] findGetterAndSetter(final String fieldName, final Class fieldType, + final Method[] gettersOrSetters, final boolean ignoreCase) { + Method getter = null; + Method setter = null; + String methodName; + for (final Method method : gettersOrSetters) { + methodName = method.getName(); + if (0 == method.getParameterCount()) { + // 无参数,可能为Getter方法 + if (StrUtil.equals(methodName, StrUtil.genGetter(fieldName), ignoreCase) && + method.getReturnType().isAssignableFrom(fieldType)) { + // getter的返回类型必须为字段类型或字段的父类 + getter = method; + } + } else if (StrUtil.equals(methodName, StrUtil.genSetter(fieldName), ignoreCase) && + fieldType.isAssignableFrom(method.getParameterTypes()[0])) { + // setter方法的参数必须为字段类型或字段的子类 + setter = method; + } + if (null != getter && null != setter) { + // 如果Getter和Setter方法都找到了,不再继续寻找 + break; + } + } + + return new Method[]{getter, setter}; + } + + /** + * 针对Boolean或boolean类型字段,查找其对应的Getter方法,规则为: + *
    + *
  • 方法必须无参数且返回boolean或Boolean
  • + *
  • 如果字段为isName, 匹配isName、isIsName方法,两个方法均存在,则按照提供的方法数组优先匹配。
  • + *
  • 如果字段为name, 匹配isName方法
  • + *
+ * + * @param gettersOrSetters 所有方法 + * @param fieldName 字段名 + * @param ignoreCase 是否忽略大小写 + * @return 查找到的方法,{@code null}表示未找到 + */ + private static Method getGetterForBoolean(final Method[] gettersOrSetters, final String fieldName, final boolean ignoreCase) { + // 查找isXXX + return MethodUtil.getMethod(gettersOrSetters, m -> { + if (0 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getReturnType())) { + // getter方法要求无参数且返回boolean或Boolean + return false; + } + + if (StrUtil.startWith(fieldName, "is", ignoreCase)) { + // isName -》 isName + if (StrUtil.equals(fieldName, m.getName(), ignoreCase)) { + return true; + } + } + + // name -》 isName + // isName -》 isIsName + return StrUtil.equals(StrUtil.upperFirstAndAddPre(fieldName, "is"), m.getName(), ignoreCase); + }); + } + + /** + * 针对Boolean或boolean类型字段,查找其对应的Setter方法,规则为: + *
    + *
  • 方法必须为1个boolean或Boolean参数
  • + *
  • 如果字段为isName,匹配setName
  • + *
+ * + * @param fieldName 字段名 + * @param gettersOrSetters 所有方法 + * @param ignoreCase 是否忽略大小写 + * @return 查找到的方法,{@code null}表示未找到 + */ + private static Method getSetterForBoolean(final Method[] gettersOrSetters, final String fieldName, final boolean ignoreCase) { + // 查找isXXX + return MethodUtil.getMethod(gettersOrSetters, m -> { + if (1 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getParameterTypes()[0])) { + // setter方法要求1个boolean或Boolean参数 + return false; + } + + if (StrUtil.startWith(fieldName, "is", ignoreCase)) { + // isName -》 setName + return StrUtil.equals( + "set" + StrUtil.removePrefix(fieldName, "is", ignoreCase), + m.getName(), ignoreCase); + } + + // 其它不匹配 + return false; + }); + } + // ------------------------------------------------------------------------------------------------------ Private method end +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/provider/BeanValueProvider.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/provider/BeanValueProvider.java index 980f83eaa..7ae6a1a57 100755 --- a/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/provider/BeanValueProvider.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/copier/provider/BeanValueProvider.java @@ -12,7 +12,7 @@ package org.dromara.hutool.core.bean.copier.provider; -import org.dromara.hutool.core.bean.BeanDesc; +import org.dromara.hutool.core.bean.StrictBeanDesc; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.PropDesc; import org.dromara.hutool.core.bean.copier.ValueProvider; @@ -28,7 +28,7 @@ import java.lang.reflect.Type; public class BeanValueProvider implements ValueProvider { private final Object bean; - private final BeanDesc beanDesc; + private final StrictBeanDesc beanDesc; /** * 构造 diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/AbstractBeanDesc.java b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/AbstractBeanDesc.java new file mode 100644 index 000000000..c570ac78d --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/bean/path/AbstractBeanDesc.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.bean.path; + +import org.dromara.hutool.core.bean.BeanDesc; +import org.dromara.hutool.core.bean.PropDesc; +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.map.CaseInsensitiveMap; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Bean描述抽象类 + * + * @author Looly + * @since 6.0.0 + */ +public abstract class AbstractBeanDesc implements BeanDesc { + private static final long serialVersionUID = 1L; + + /** + * Bean类 + */ + protected final Class beanClass; + + /** + * 属性Map + */ + protected final Map propMap = new LinkedHashMap<>(); + + /** + * 构造 + * + * @param beanClass Bean类 + */ + public AbstractBeanDesc(final Class beanClass) { + this.beanClass = Assert.notNull(beanClass); + } + + /** + * 获取Bean的全类名 + * + * @return Bean的类名 + */ + public String getName() { + return this.beanClass.getName(); + } + + /** + * 获取Bean的简单类名 + * + * @return Bean的类名 + */ + public String getSimpleName() { + return this.beanClass.getSimpleName(); + } + + /** + * 获取Bean类 + * + * @return Bean类 + */ + public Class getBeanClass() { + return this.beanClass; + } + + @Override + public Map getPropMap(final boolean ignoreCase) { + return ignoreCase ? new CaseInsensitiveMap<>(1, this.propMap) : this.propMap; + } + + @Override + public Collection getProps() { + return this.propMap.values(); + } + + @Override + public PropDesc getProp(final String fieldName) { + return this.propMap.get(fieldName); + } + + /** + * 获得字段名对应的字段对象,如果不存在返回null + * + * @param fieldName 字段名 + * @return 字段值 + */ + public Field getField(final String fieldName) { + final PropDesc desc = this.propMap.get(fieldName); + return null == desc ? null : desc.getField(); + } + + /** + * 获取Getter方法,如果不存在返回null + * + * @param fieldName 字段名 + * @return Getter方法 + */ + public Method getGetter(final String fieldName) { + final PropDesc desc = this.propMap.get(fieldName); + return null == desc ? null : desc.getGetter(); + } + + /** + * 获取Setter方法,如果不存在返回null + * + * @param fieldName 字段名 + * @return Setter方法 + */ + public Method getSetter(final String fieldName) { + final PropDesc desc = this.propMap.get(fieldName); + return null == desc ? null : desc.getSetter(); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/func/PredicateUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/func/PredicateUtil.java index a7b7d699b..4d83ee513 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/func/PredicateUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/func/PredicateUtil.java @@ -15,6 +15,7 @@ package org.dromara.hutool.core.func; import org.dromara.hutool.core.stream.StreamUtil; import java.util.function.Predicate; +import java.util.stream.Stream; /** * 一些{@link Predicate}相关封装 @@ -103,4 +104,16 @@ public class PredicateUtil { public static Predicate or(final Predicate... components) { return StreamUtil.of(components).reduce(Predicate::or).orElseGet(() -> o -> false); } + + /** + * 用于组合多个方法匹配器的方法匹配器,即所有条件都为false时,才返回true,也可理解为,任一条件为true时,返回false + * + * @param 判断条件的对象类型 + * @param components 多个条件 + * @return 复合条件 + */ + @SafeVarargs + public static Predicate none(final Predicate... components){ + return t -> Stream.of(components).noneMatch(matcher -> matcher.test(t)); + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ModifierType.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ModifierType.java new file mode 100644 index 000000000..15c97f6aa --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ModifierType.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.reflect; + +import java.lang.reflect.Modifier; + +/** + * 修饰符枚举 + * + * @author looly + * @since 4.0.5 + */ +public enum ModifierType { + /** + * public修饰符,所有类都能访问 + */ + PUBLIC(Modifier.PUBLIC), + /** + * private修饰符,只能被自己访问和修改 + */ + PRIVATE(Modifier.PRIVATE), + /** + * protected修饰符,自身、子类及同一个包中类可以访问 + */ + PROTECTED(Modifier.PROTECTED), + /** + * static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类 + */ + STATIC(Modifier.STATIC), + /** + * final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载 + */ + FINAL(Modifier.FINAL), + /** + * synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。 + */ + SYNCHRONIZED(Modifier.SYNCHRONIZED), + /** + * (易失修饰符)指定该变量可以同时被几个线程控制和修改 + */ + VOLATILE(Modifier.VOLATILE), + /** + * (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略 + */ + TRANSIENT(Modifier.TRANSIENT), + /** + * native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。 + */ + NATIVE(Modifier.NATIVE), + + /** + * abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。 + */ + ABSTRACT(Modifier.ABSTRACT), + /** + * strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。 + */ + STRICT(Modifier.STRICT); + + /** + * 修饰符枚举对应的int修饰符值 + */ + private final int value; + + /** + * 构造 + * + * @param modifier 修饰符int表示,见{@link Modifier} + */ + ModifierType(final int modifier) { + this.value = modifier; + } + + /** + * 获取修饰符枚举对应的int修饰符值,值见{@link Modifier} + * + * @return 修饰符枚举对应的int修饰符值 + */ + public int getValue() { + return this.value; + } + + /** + * 多个修饰符做“或”操作,表示存在任意一个修饰符 + * + * @param modifierTypes 修饰符列表,元素不能为空 + * @return “或”之后的修饰符 + */ + public static int orToInt(final ModifierType... modifierTypes) { + int modifier = modifierTypes[0].getValue(); + for (int i = 1; i < modifierTypes.length; i++) { + modifier |= modifierTypes[i].getValue(); + } + return modifier; + } + + /** + * 多个修饰符做“或”操作,表示存在任意一个修饰符 + * + * @param modifierTypes 修饰符列表,元素不能为空 + * @return “或”之后的修饰符 + */ + public static int orToInt(final int... modifierTypes) { + int modifier = modifierTypes[0]; + for (int i = 1; i < modifierTypes.length; i++) { + modifier |= modifierTypes[i]; + } + return modifier; + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ModifierUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ModifierUtil.java index 83996e543..9ff6875c6 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ModifierUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ModifierUtil.java @@ -28,83 +28,6 @@ import java.lang.reflect.Modifier; */ public class ModifierUtil { - /** - * 修饰符枚举 - * - * @author looly - * @since 4.0.5 - */ - public enum ModifierType { - /** - * public修饰符,所有类都能访问 - */ - PUBLIC(Modifier.PUBLIC), - /** - * private修饰符,只能被自己访问和修改 - */ - PRIVATE(Modifier.PRIVATE), - /** - * protected修饰符,自身、子类及同一个包中类可以访问 - */ - PROTECTED(Modifier.PROTECTED), - /** - * static修饰符,(静态修饰符)指定变量被所有对象共享,即所有实例都可以使用该变量。变量属于这个类 - */ - STATIC(Modifier.STATIC), - /** - * final修饰符,最终修饰符,指定此变量的值不能变,使用在方法上表示不能被重载 - */ - FINAL(Modifier.FINAL), - /** - * synchronized,同步修饰符,在多个线程中,该修饰符用于在运行前,对他所属的方法加锁,以防止其他线程的访问,运行结束后解锁。 - */ - SYNCHRONIZED(Modifier.SYNCHRONIZED), - /** - * (易失修饰符)指定该变量可以同时被几个线程控制和修改 - */ - VOLATILE(Modifier.VOLATILE), - /** - * (过度修饰符)指定该变量是系统保留,暂无特别作用的临时性变量,序列化时忽略 - */ - TRANSIENT(Modifier.TRANSIENT), - /** - * native,本地修饰符。指定此方法的方法体是用其他语言在程序外部编写的。 - */ - NATIVE(Modifier.NATIVE), - - /** - * abstract,将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现。 - */ - ABSTRACT(Modifier.ABSTRACT), - /** - * strictfp,一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。 - */ - STRICT(Modifier.STRICT); - - /** - * 修饰符枚举对应的int修饰符值 - */ - private final int value; - - /** - * 构造 - * - * @param modifier 修饰符int表示,见{@link Modifier} - */ - ModifierType(final int modifier) { - this.value = modifier; - } - - /** - * 获取修饰符枚举对应的int修饰符值,值见{@link Modifier} - * - * @return 修饰符枚举对应的int修饰符值 - */ - public int getValue() { - return this.value; - } - } - /** * 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true) * @@ -116,7 +39,7 @@ public class ModifierUtil { if (null == clazz || ArrayUtil.isEmpty(modifierTypes)) { return false; } - return 0 != (clazz.getModifiers() & modifiersToInt(modifierTypes)); + return 0 != (clazz.getModifiers() & ModifierType.orToInt(modifierTypes)); } /** @@ -130,7 +53,34 @@ public class ModifierUtil { if (null == member || ArrayUtil.isEmpty(modifierTypes)) { return false; } - return 0 != (member.getModifiers() & modifiersToInt(modifierTypes)); + return 0 != (member.getModifiers() & ModifierType.orToInt(modifierTypes)); + } + + /** + * 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true) + * + * @param modifiers 类、构造、字段或方法的修饰符 + * @param checkedModifiers 需要检查的修饰符,如果为空返回{@code false} + * @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false + */ + public static boolean hasModifier(final int modifiers, final int... checkedModifiers) { + return 0 != (modifiers & ModifierType.orToInt(checkedModifiers)); + } + + /** + * 是否同时存在一个或多个修饰符(可能有多个修饰符,如果有指定的修饰符则返回true) + * + * @param modifiers 类、构造、字段或方法的修饰符 + * @param checkedModifiers 需要检查的修饰符,如果为空返回{@code false} + * @return 是否有指定修饰符,如果有返回true,否则false,如果提供参数为null返回false + */ + public static boolean hasAllModifier(final int modifiers, final int... checkedModifiers) { + for (final int checkedModifier : checkedModifiers) { + if (0 == (modifiers & checkedModifier)) { + return false; + } + } + return true; } /** @@ -318,20 +268,4 @@ public class ModifierUtil { throw new HutoolException(e, "IllegalAccess for [{}.{}]", field.getDeclaringClass(), field.getName()); } } - //-------------------------------------------------------------------------------------------------------- Private method start - - /** - * 多个修饰符做“与”操作,表示同时存在多个修饰符 - * - * @param modifierTypes 修饰符列表,元素不能为空 - * @return “与”之后的修饰符 - */ - private static int modifiersToInt(final ModifierType... modifierTypes) { - int modifier = modifierTypes[0].getValue(); - for (int i = 1; i < modifierTypes.length; i++) { - modifier |= modifierTypes[i].getValue(); - } - return modifier; - } - //-------------------------------------------------------------------------------------------------------- Private method end } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodMatcherUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodMatcherUtil.java index 89186bb58..4094e0050 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodMatcherUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodMatcherUtil.java @@ -14,7 +14,9 @@ package org.dromara.hutool.core.reflect.method; import org.dromara.hutool.core.annotation.AnnotatedElementUtil; import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.func.PredicateUtil; import org.dromara.hutool.core.reflect.ClassUtil; +import org.dromara.hutool.core.reflect.ModifierUtil; import org.dromara.hutool.core.text.CharSequenceUtil; import org.jetbrains.annotations.NotNull; @@ -22,7 +24,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Arrays; import java.util.Objects; import java.util.function.BiPredicate; import java.util.function.Predicate; @@ -45,7 +46,7 @@ public class MethodMatcherUtil { */ @SafeVarargs public static Predicate noneMatch(final Predicate... matchers) { - return method -> Stream.of(matchers).noneMatch(matcher -> matcher.test(method)); + return PredicateUtil.none(matchers); } /** @@ -57,7 +58,7 @@ public class MethodMatcherUtil { */ @SafeVarargs public static Predicate anyMatch(final Predicate... matchers) { - return method -> Stream.of(matchers).anyMatch(matcher -> matcher.test(method)); + return PredicateUtil.or(matchers); } /** @@ -69,7 +70,7 @@ public class MethodMatcherUtil { */ @SafeVarargs public static Predicate allMatch(final Predicate... matchers) { - return method -> Stream.of(matchers).allMatch(matcher -> matcher.test(method)); + return PredicateUtil.and(matchers); } // region ============= 访问修饰符 ============= @@ -108,10 +109,7 @@ public class MethodMatcherUtil { * @return 方法匹配器 */ public static Predicate forModifiers(final int... modifiers) { - return method -> { - final int methodModifiers = method.getModifiers(); - return Arrays.stream(modifiers).allMatch(modifier -> (methodModifiers & modifier) != 0); - }; + return method -> ModifierUtil.hasAllModifier(method.getModifiers(), modifiers); } // endregion diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodUtil.java index 23edda0a9..a7dbcf157 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodUtil.java @@ -54,6 +54,18 @@ public class MethodUtil { // region ----- getMethods + /** + * 通过给定的条件(Predicate)从一个Method数组中查找第一个匹配的方法。 + * + * @param methods Method数组,是被搜索的目标对象。 + * @param predicate 一个Predicate接口实例,用于定义查找方法的条件。 + * @return 返回第一个满足predicate条件的Method对象,如果没有找到匹配的方法则返回null。 + */ + public static Method getMethod(final Method[] methods, final Predicate predicate) { + // 使用ArrayUtil的get方法,通过predicate对methods数组进行搜索 + return ArrayUtil.get(methods, predicate); + } + /** * 获得指定类本类及其父类中的Public方法名
* 去重重载的方法 @@ -81,15 +93,7 @@ public class MethodUtil { if (null == clazz || StrUtil.isBlank(methodName)) { return null; } - - final Method[] methods = getPublicMethods(clazz, method -> - StrUtil.equals(methodName, method.getName(), ignoreCase) - && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes) - //排除桥接方法,pr#1965@Github - //排除协变桥接方法,pr#1965@Github - && (method.getReturnType().isAssignableFrom(method.getReturnType()))); - - return ArrayUtil.isEmpty(methods) ? null : methods[0]; + return getMethod(getPublicMethods(clazz), ignoreCase, methodName, paramTypes); } /** @@ -164,15 +168,40 @@ public class MethodUtil { if (null == clazz || StrUtil.isBlank(methodName)) { return null; } + return getMethod(getMethods(clazz), ignoreCase, methodName, paramTypes); + } - final Method[] methods = getMethods(clazz, method -> - StrUtil.equals(methodName, method.getName(), ignoreCase) - && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes) - //排除桥接方法,pr#1965@Github - //排除协变桥接方法,pr#1965@Github - && (method.getReturnType().isAssignableFrom(method.getReturnType()))); + /** + * 查找指定方法 如果找不到对应的方法则返回{@code null}
+ * 此方法为精准获取方法名,即方法名和参数数量和类型必须一致,否则返回{@code null}。
+ * 如果查找的方法有多个同参数类型重载,查找最后一个非协变桥接方法 + * + * @param methods 方法列表 + * @param ignoreCase 是否忽略大小写 + * @param methodName 方法名,如果为空字符串返回{@code null} + * @param paramTypes 参数类型,指定参数类型如果是方法的子类也算 + * @return 方法 + * @throws SecurityException 无权访问抛出异常 + * @since 3.2.0 + */ + public static Method getMethod(final Method[] methods, final boolean ignoreCase, final String methodName, final Class... paramTypes) throws SecurityException { + if (ArrayUtil.isEmpty(methods) || StrUtil.isBlank(methodName)) { + return null; + } - return ArrayUtil.isEmpty(methods) ? null : methods[0]; + Method res = null; + if (ArrayUtil.isNotEmpty(methods)) { + for (final Method method : methods) { + if (StrUtil.equals(methodName, method.getName(), ignoreCase) + && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes) + //排除协变桥接方法,pr#1965@Github + && (res == null + || res.getReturnType().isAssignableFrom(method.getReturnType()))) { + res = method; + } + } + } + return res; } /** @@ -724,5 +753,4 @@ public class MethodUtil { return actualArgs; } - } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java index 102246eaa..4758ad6ff 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/placeholder/template/NamedPlaceholderStrTemplate.java @@ -13,7 +13,7 @@ package org.dromara.hutool.core.text.placeholder.template; import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.bean.BeanDesc; +import org.dromara.hutool.core.bean.StrictBeanDesc; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.ListUtil; @@ -344,7 +344,7 @@ public class NamedPlaceholderStrTemplate extends StrTemplate { if (beanOrMap instanceof Map) { return format((Map) beanOrMap); } else if (BeanUtil.isReadableBean(beanOrMap.getClass())) { - final BeanDesc beanDesc = BeanUtil.getBeanDesc(beanOrMap.getClass()); + final StrictBeanDesc beanDesc = BeanUtil.getBeanDesc(beanOrMap.getClass()); return format(fieldName -> { final Method getterMethod = beanDesc.getGetter(fieldName); if (getterMethod == null) { @@ -553,7 +553,7 @@ public class NamedPlaceholderStrTemplate extends StrTemplate { @SuppressWarnings("unchecked") final Map map = (Map) obj; matchesByKey(str, map::put); } else if (BeanUtil.isReadableBean(obj.getClass())) { - final BeanDesc beanDesc = BeanUtil.getBeanDesc(obj.getClass()); + final StrictBeanDesc beanDesc = BeanUtil.getBeanDesc(obj.getClass()); matchesByKey(str, (key, value) -> { final Field field = beanDesc.getField(key); final Method setterMethod = beanDesc.getSetter(key); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java index ab754e895..591fcdc26 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanDescTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** - * {@link BeanDesc} 单元测试类 + * {@link StrictBeanDesc} 单元测试类 * * @author looly * @@ -25,7 +25,7 @@ public class BeanDescTest { @Test public void propDescTes() { - final BeanDesc desc = BeanUtil.getBeanDesc(User.class); + final StrictBeanDesc desc = BeanUtil.getBeanDesc(User.class); Assertions.assertEquals("User", desc.getSimpleName()); Assertions.assertEquals("age", desc.getField("age").getName()); @@ -38,7 +38,7 @@ public class BeanDescTest { @Test public void propDescTes2() { - final BeanDesc desc = BeanUtil.getBeanDesc(User.class); + final StrictBeanDesc desc = BeanUtil.getBeanDesc(User.class); final PropDesc prop = desc.getProp("name"); Assertions.assertEquals("name", prop.getFieldName()); @@ -50,7 +50,7 @@ public class BeanDescTest { @Test public void propDescOfBooleanTest() { - final BeanDesc desc = BeanUtil.getBeanDesc(User.class); + final StrictBeanDesc desc = BeanUtil.getBeanDesc(User.class); Assertions.assertEquals("isAdmin", desc.getGetter("isAdmin").getName()); Assertions.assertEquals("setAdmin", desc.getSetter("isAdmin").getName()); @@ -60,7 +60,7 @@ public class BeanDescTest { @Test public void propDescOfBooleanTest2() { - final BeanDesc desc = BeanUtil.getBeanDesc(User.class); + final StrictBeanDesc desc = BeanUtil.getBeanDesc(User.class); Assertions.assertEquals("isIsSuper", desc.getGetter("isSuper").getName()); Assertions.assertEquals("setIsSuper", desc.getSetter("isSuper").getName()); @@ -68,7 +68,7 @@ public class BeanDescTest { @Test public void propDescOfBooleanTest3() { - final BeanDesc desc = BeanUtil.getBeanDesc(User.class); + final StrictBeanDesc desc = BeanUtil.getBeanDesc(User.class); Assertions.assertEquals("setLastPage", desc.getSetter("lastPage").getName()); Assertions.assertEquals("setIsLastPage", desc.getSetter("isLastPage").getName()); @@ -76,7 +76,7 @@ public class BeanDescTest { @Test public void getSetTest() { - final BeanDesc desc = BeanUtil.getBeanDesc(User.class); + final StrictBeanDesc desc = BeanUtil.getBeanDesc(User.class); final User user = new User(); desc.getProp("name").setValue(user, "张三"); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanWithReturnThisTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanWithReturnThisTest.java index cc1b915ae..5d1c30a68 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanWithReturnThisTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/bean/BeanWithReturnThisTest.java @@ -20,7 +20,7 @@ public class BeanWithReturnThisTest { @Test public void setValueTest() { final BeanWithRetuenThis bean = new BeanWithRetuenThis(); - final BeanDesc beanDesc = BeanUtil.getBeanDesc(BeanWithRetuenThis.class); + final StrictBeanDesc beanDesc = BeanUtil.getBeanDesc(BeanWithRetuenThis.class); final PropDesc prop = beanDesc.getProp("a"); prop.setValue(bean, "123"); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/bean/Issue3096Test.java b/hutool-core/src/test/java/org/dromara/hutool/core/bean/Issue3096Test.java index aa1eaa4b4..f0189ab64 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/bean/Issue3096Test.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/bean/Issue3096Test.java @@ -19,7 +19,7 @@ public class Issue3096Test { @Test void beanDescTest() { - final BeanDesc desc = BeanUtil.getBeanDesc(User.class); + final StrictBeanDesc desc = BeanUtil.getBeanDesc(User.class); // https://github.com/dromara/hutool/issues/3096 // 新修改的规则中,isLastPage字段优先匹配setIsLastPage,这个顺序固定。 diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/util/ModifierUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/util/ModifierUtilTest.java index 69723602d..0df650646 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/util/ModifierUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/util/ModifierUtilTest.java @@ -13,6 +13,7 @@ package org.dromara.hutool.core.util; import org.dromara.hutool.core.reflect.FieldUtil; +import org.dromara.hutool.core.reflect.ModifierType; import org.dromara.hutool.core.reflect.ModifierUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -28,10 +29,10 @@ public class ModifierUtilTest { @Test public void hasModifierTest() throws NoSuchMethodException { final Method method = ModifierUtilTest.class.getDeclaredMethod("ddd"); - Assertions.assertTrue(ModifierUtil.hasModifier(method, ModifierUtil.ModifierType.PRIVATE)); + Assertions.assertTrue(ModifierUtil.hasModifier(method, ModifierType.PRIVATE)); Assertions.assertTrue(ModifierUtil.hasModifier(method, - ModifierUtil.ModifierType.PRIVATE, - ModifierUtil.ModifierType.STATIC) + ModifierType.PRIVATE, + ModifierType.STATIC) ); }