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