From 8e4b24f8fa0b3038c47ca1ff0f656aa4e8304ae3 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 16 May 2024 12:15:35 +0800 Subject: [PATCH] add FieldReflect --- .../hutool/core/reflect/ConstructorUtil.java | 15 +- .../hutool/core/reflect/FieldReflect.java | 130 ++++++++++++++++++ .../hutool/core/reflect/FieldUtil.java | 112 ++++++++------- .../core/reflect/method/MethodReflect.java | 7 +- 4 files changed, 205 insertions(+), 59 deletions(-) create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldReflect.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ConstructorUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ConstructorUtil.java index 212aa30fc..ae7150552 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ConstructorUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ConstructorUtil.java @@ -30,7 +30,15 @@ public class ConstructorUtil { * 构造对象缓存 */ private static final WeakConcurrentMap, Constructor[]> CONSTRUCTORS_CACHE = new WeakConcurrentMap<>(); - // --------------------------------------------------------------------------------------------------------- Constructor + + /** + * 清除方法缓存 + */ + synchronized static void clearCache() { + CONSTRUCTORS_CACHE.clear(); + } + + // region ----- Constructor /** * 查找类中的指定参数的构造方法,如果找到构造方法,会自动设置可访问为true @@ -84,7 +92,9 @@ public class ConstructorUtil { return beanClass.getDeclaredConstructors(); } - // --------------------------------------------------------------------------------------------------------- newInstance + // endregion + + // region ----- newInstance /** * 实例化对象
@@ -131,4 +141,5 @@ public class ConstructorUtil { public static T newInstanceIfPossible(final Class type) { return PossibleObjectCreator.of(type).create(); } + // endregion } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldReflect.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldReflect.java new file mode 100644 index 000000000..148dba573 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldReflect.java @@ -0,0 +1,130 @@ +/* + * 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 org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.lang.Assert; + +import java.lang.reflect.Field; +import java.util.function.Predicate; + +/** + * 字段反射类
+ * 此类持有类中字段的缓存,如果字段在类中修改,则需要手动调用clearCaches方法清除缓存。 + * + * @author Looly + * @since 6.0.0 + */ +public class FieldReflect { + + /** + * 创建FieldReflect + * + * @param clazz 类 + * @return FieldReflect + */ + public static FieldReflect of(final Class clazz) { + return new FieldReflect(clazz); + } + + private final Class clazz; + private volatile Field[] declaredFields; + private volatile Field[] allFields; + + /** + * 构造 + * + * @param clazz 类 + */ + public FieldReflect(final Class clazz) { + this.clazz = Assert.notNull(clazz); + } + + /** + * 获取当前类 + * + * @return 当前类 + */ + public Class getClazz() { + return clazz; + } + + /** + * 清空缓存 + */ + synchronized public void clearCaches() { + declaredFields = null; + allFields = null; + } + + /** + * 获得当前类声明的所有字段(包括非public字段),但不包括父类的字段 + * + * @param predicate 过滤器 + * @return 字段数组 + * @throws SecurityException 安全检查异常 + */ + public Field[] getDeclaredFields(final Predicate predicate) { + if (null == declaredFields) { + synchronized (FieldReflect.class) { + if (null == declaredFields) { + declaredFields = clazz.getDeclaredFields(); + } + } + } + return ArrayUtil.filter(declaredFields, predicate); + } + + /** + * 获得当前类和父类声明的所有字段(包括非public字段) + * + * @param predicate 过滤器 + * @return 字段数组 + * @throws SecurityException 安全检查异常 + */ + public Field[] getAllFields(final Predicate predicate) { + if (null == allFields) { + synchronized (FieldReflect.class) { + if (null == allFields) { + allFields = getFieldsDirectly(true); + } + } + } + return ArrayUtil.filter(allFields, predicate); + } + + /** + * 获得一个类中所有字段列表,直接反射获取,无缓存
+ * 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。 + * + * @param withSuperClassFields 是否包括父类的字段列表 + * @return 字段列表 + * @throws SecurityException 安全检查异常 + */ + public Field[] getFieldsDirectly(final boolean withSuperClassFields) throws SecurityException { + Field[] allFields = null; + Class searchType = this.clazz; + Field[] declaredFields; + while (searchType != null) { + declaredFields = searchType.getDeclaredFields(); + if (null == allFields) { + allFields = declaredFields; + } else { + allFields = ArrayUtil.append(allFields, declaredFields); + } + searchType = withSuperClassFields ? searchType.getSuperclass() : null; + } + + return allFields; + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldUtil.java index 1ecf5febe..0d80111ea 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/FieldUtil.java @@ -36,9 +36,29 @@ public class FieldUtil { /** * 字段缓存 */ - private static final WeakConcurrentMap, Field[]> FIELDS_CACHE = new WeakConcurrentMap<>(); + private static final WeakConcurrentMap, FieldReflect> FIELDS_CACHE = new WeakConcurrentMap<>(); - // --------------------------------------------------------------------------------------------------------- Field + /** + * 清除方法缓存 + */ + synchronized static void clearCache() { + FIELDS_CACHE.clear(); + } + + /** + * 是否为父类引用字段
+ * 当字段所在类是对象子类时(对象中定义的非static的class),会自动生成一个以"this$0"为名称的字段,指向父类对象 + * + * @param field 字段 + * @return 是否为父类引用字段 + * @since 5.7.20 + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isOuterClassField(final Field field) { + return "this$0".equals(field.getName()); + } + + // region ----- Field /** * 查找指定类中是否包含指定名称对应的字段,包括所有字段(包括非public字段),也包括父类和Object类的字段 @@ -85,6 +105,18 @@ public class FieldUtil { return field.getName(); } + /** + * 获取本类定义的指定名称的字段,包括私有字段,但是不包括父类字段 + * + * @param beanClass Bean的Class + * @param name 字段名称 + * @return 字段对象,如果未找到返回{@code null} + */ + public static Field getDeclearField(final Class beanClass, final String name) { + final Field[] fields = getDeclaredFields(beanClass, (field -> StrUtil.equals(name, field.getName()))); + return ArrayUtil.isEmpty(fields) ? null : fields[0]; + } + /** * 查找指定类中的指定name的字段(包括非public字段),也包括父类和Object类的字段, 字段不存在则返回{@code null} * @@ -94,28 +126,13 @@ public class FieldUtil { * @throws SecurityException 安全异常 */ public static Field getField(final Class beanClass, final String name) throws SecurityException { - final Field[] fields = getFields(beanClass); - return ArrayUtil.firstMatch((field) -> name.equals(getFieldName(field)), fields); - } - - /** - * 获取本类定义的指定名称的字段,包括私有字段,但是不包括父类字段 - * - * @param beanClass Bean的Class - * @param name 字段名称 - * @return 字段对象,如果未找到返回{@code null} - */ - public static Field getDeclearField(final Class beanClass, final String name) { - try { - return beanClass.getDeclaredField(name); - } catch (final NoSuchFieldException e) { - return null; - } + final Field[] fields = getFields(beanClass, (field -> StrUtil.equals(name, field.getName()))); + return ArrayUtil.isEmpty(fields) ? null : fields[0]; } /** * 获取指定类中字段名和字段对应的有序Map,包括其父类中的字段
- * 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。 + * 如果子类与父类中存在同名字段,则父类字段忽略 * * @param beanClass 类 * @return 字段名和字段对应的Map,有序 @@ -125,7 +142,7 @@ public class FieldUtil { final Field[] fields = getFields(beanClass); final HashMap map = MapUtil.newHashMap(fields.length, true); for (final Field field : fields) { - map.put(field.getName(), field); + map.putIfAbsent(field.getName(), field); } return map; } @@ -139,8 +156,7 @@ public class FieldUtil { * @throws SecurityException 安全检查异常 */ public static Field[] getFields(final Class beanClass) throws SecurityException { - Assert.notNull(beanClass); - return FIELDS_CACHE.computeIfAbsent(beanClass, (key) -> getFieldsDirectly(beanClass, true)); + return getFields(beanClass, null); } @@ -155,7 +171,22 @@ public class FieldUtil { * @since 5.7.14 */ public static Field[] getFields(final Class beanClass, final Predicate fieldPredicate) throws SecurityException { - return ArrayUtil.filter(getFields(beanClass), fieldPredicate); + Assert.notNull(beanClass); + return FIELDS_CACHE.computeIfAbsent(beanClass, FieldReflect::of).getAllFields(fieldPredicate); + } + + /** + * 获得当前类声明的所有字段(包括非public字段),但不包括父类的字段
+ * + * @param beanClass 类 + * @param fieldPredicate field过滤器,过滤掉不需要的field,{@link Predicate#test(Object)}为{@code true}保留,null表示全部保留 + * @return 字段列表 + * @throws SecurityException 安全检查异常 + * @since 6.0.0 + */ + public static Field[] getDeclaredFields(final Class beanClass, final Predicate fieldPredicate) throws SecurityException { + Assert.notNull(beanClass); + return FIELDS_CACHE.computeIfAbsent(beanClass, FieldReflect::of).getDeclaredFields(fieldPredicate); } /** @@ -168,24 +199,13 @@ public class FieldUtil { * @throws SecurityException 安全检查异常 */ public static Field[] getFieldsDirectly(final Class beanClass, final boolean withSuperClassFields) throws SecurityException { - Assert.notNull(beanClass); - - Field[] allFields = null; - Class searchType = beanClass; - Field[] declaredFields; - while (searchType != null) { - declaredFields = searchType.getDeclaredFields(); - if (null == allFields) { - allFields = declaredFields; - } else { - allFields = ArrayUtil.append(allFields, declaredFields); - } - searchType = withSuperClassFields ? searchType.getSuperclass() : null; - } - - return allFields; + return FieldReflect.of(beanClass).getFieldsDirectly(withSuperClassFields); } + // endregion + + // region ----- Field Value + /** * 获取字段值 * @@ -343,15 +363,5 @@ public class FieldUtil { } } - /** - * 是否为父类引用字段
- * 当字段所在类是对象子类时(对象中定义的非static的class),会自动生成一个以"this$0"为名称的字段,指向父类对象 - * - * @param field 字段 - * @return 是否为父类引用字段 - * @since 5.7.20 - */ - public static boolean isOuterClassField(final Field field) { - return "this$0".equals(field.getName()); - } + // endregion } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodReflect.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodReflect.java index a56d5bf01..ba7e4fd63 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodReflect.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/method/MethodReflect.java @@ -73,13 +73,8 @@ public class MethodReflect { allMethods = null; } - // region ----- getMathod + // region ----- getMethods - - - // endregion - - // region ----- getMathods /** * 获取当前类及父类的所有公共方法,等同于{@link Class#getMethods()} *