|
@@ -6,6 +6,7 @@ import java.beans.Introspector;
|
|
|
import java.beans.PropertyDescriptor;
|
|
|
import java.io.Serializable;
|
|
|
import java.lang.invoke.SerializedLambda;
|
|
|
+import java.lang.ref.WeakReference;
|
|
|
import java.lang.reflect.Array;
|
|
|
import java.lang.reflect.Field;
|
|
|
import java.lang.reflect.GenericArrayType;
|
|
@@ -71,22 +72,24 @@ public final class ObjectUtils {
|
|
|
/**
|
|
|
* 对象字段映射表
|
|
|
*/
|
|
|
- private static final Map<Class<?>, Map<String, Field>> CLASS_FIELD_MAPPING = Maps.newConcurrentMap();
|
|
|
+ private static final Map<Class<?>, WeakReference<Map<String, Field>>> CLASS_FIELD_MAPPING = Maps.newConcurrentMap();
|
|
|
|
|
|
/**
|
|
|
* Protostuff对象类型/Schema映射表
|
|
|
*/
|
|
|
- private static final Map<Class<?>, Schema<?>> PROTOSTUFF_SCHEMA_MAPPING = Maps.newConcurrentMap();
|
|
|
+ private static final Map<Class<?>, WeakReference<Schema<?>>> PROTOSTUFF_SCHEMA_MAPPING = Maps.newConcurrentMap();
|
|
|
|
|
|
/**
|
|
|
* SerializedLambda 反序列化缓存
|
|
|
*/
|
|
|
- private static final Map<Class<?>, SerializedLambda> LAMBDA_FUNCTION_MAPPING = Maps.newConcurrentMap();
|
|
|
+ private static final Map<Class<?>, WeakReference<SerializedLambda>> LAMBDA_FUNCTION_MAPPING =
|
|
|
+ Maps.newConcurrentMap();
|
|
|
|
|
|
/**
|
|
|
* 类对象/属性描述映射表
|
|
|
*/
|
|
|
- private static final Map<Class<?>, List<PropertyDescriptor>> PROPERTY_DESCRIPTOR_MAPPING = Maps.newConcurrentMap();
|
|
|
+ private static final Map<Class<?>, WeakReference<List<PropertyDescriptor>>> PROPERTY_DESCRIPTOR_MAPPING =
|
|
|
+ Maps.newConcurrentMap();
|
|
|
|
|
|
static {
|
|
|
// 注册简单的对象拷贝转换器
|
|
@@ -440,99 +443,95 @@ public final class ObjectUtils {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 查找类对象字段
|
|
|
+ * 获取对象字段
|
|
|
*
|
|
|
- * @param clazz 类对象
|
|
|
- * @param consumer 字段回调函数
|
|
|
+ * @param clazz 对象
|
|
|
+ * @return 字段名称/实例映射表
|
|
|
*/
|
|
|
- public static void lookupClassFields(@NonNull Class<?> clazz, @NonNull Consumer<Field> consumer) {
|
|
|
- filterClassFields(clazz, field -> {
|
|
|
- consumer.accept(field);
|
|
|
- return false;
|
|
|
- });
|
|
|
+ public static Map<String, Field> getClassFields(@NonNull Class<?> clazz) {
|
|
|
+ Map<String, Field> fields = ObjectUtils.ifNull(CLASS_FIELD_MAPPING.get(clazz), WeakReference::get);
|
|
|
+ return fields != null ? fields : CLASS_FIELD_MAPPING.computeIfAbsent(
|
|
|
+ clazz, k -> new WeakReference<>(findClassFields(clazz))
|
|
|
+ ).get();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 过滤类对象字段
|
|
|
+ * 查找对象字段
|
|
|
*
|
|
|
- * @param clazz 类对象
|
|
|
- * @param function 字段回调函数
|
|
|
- * @return true/false
|
|
|
+ * @param clazz 对象
|
|
|
+ * @return 字段名称/实例映射表
|
|
|
*/
|
|
|
- public static boolean filterClassFields(@NonNull Class<?> clazz, @NonNull Function<Field, Boolean> function) {
|
|
|
- if (!Modifier.isInterface(clazz.getModifiers())) {
|
|
|
- do {
|
|
|
- Field[] fields = clazz.getDeclaredFields();
|
|
|
- if (notEmpty(fields)) {
|
|
|
- boolean enumerable = Enum.class.isAssignableFrom(clazz);
|
|
|
- for (Field field : fields) {
|
|
|
- if (!field.isSynthetic() && ((enumerable && field.isEnumConstant())
|
|
|
- || (!enumerable && !Modifier.isStatic(field.getModifiers())))) {
|
|
|
- field.setAccessible(true);
|
|
|
- if (Boolean.TRUE.equals(function.apply(field))) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } while ((clazz = clazz.getSuperclass()) != null && clazz != Enum.class && clazz != Object.class);
|
|
|
- }
|
|
|
- return false;
|
|
|
+ public static Map<String, Field> findClassFields(@NonNull Class<?> clazz) {
|
|
|
+ Map<String, Field> fields = Maps.newHashMap();
|
|
|
+ findClassField(clazz, field -> {
|
|
|
+ fields.put(field.getName(), field);
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ return fields.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(fields);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取对象字段
|
|
|
*
|
|
|
- * @param clazz 目标对象
|
|
|
+ * @param clazz 对象
|
|
|
* @param name 字段名称
|
|
|
* @return 字段实例
|
|
|
*/
|
|
|
- public static Field getField(@NonNull Class<?> clazz, @NonNull String name) {
|
|
|
- Field field = findField(clazz, name);
|
|
|
- return AssertUtils.nonnull(field, () -> "No such field: " + clazz.getName() + "." + name);
|
|
|
+ public static Field getClassField(@NonNull Class<?> clazz, @NonNull String name) {
|
|
|
+ Field field = findClassField(clazz, name);
|
|
|
+ AssertUtils.check(Objects.nonNull(field), () -> "No such field: " + clazz.getName() + "." + name);
|
|
|
+ return field;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查找对象字段
|
|
|
*
|
|
|
- * @param clazz 目标对象
|
|
|
+ * @param clazz 对象
|
|
|
* @param name 字段名称
|
|
|
* @return 字段实例
|
|
|
*/
|
|
|
- public static Field findField(@NonNull Class<?> clazz, @NonNull String name) {
|
|
|
- return ifNull(getAllFields(clazz), fields -> fields.get(name));
|
|
|
+ public static Field findClassField(@NonNull Class<?> clazz, @NonNull String name) {
|
|
|
+ return findClassField(clazz, field -> Objects.equals(field.getName(), name));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取对象所有字段
|
|
|
+ * 查找对象字段
|
|
|
*
|
|
|
- * @param clazz 目标对象
|
|
|
- * @return 字段名称/实例映射表
|
|
|
+ * @param clazz 对象
|
|
|
+ * @param function 字段回调函数
|
|
|
+ * @return 字段实例
|
|
|
*/
|
|
|
- public static Map<String, Field> getAllFields(@NonNull Class<?> clazz) {
|
|
|
- Map<String, Field> cache = CLASS_FIELD_MAPPING.get(clazz);
|
|
|
- return cache != null ? cache : CLASS_FIELD_MAPPING.computeIfAbsent(clazz, k -> {
|
|
|
- // 接口对象类型
|
|
|
- if (Modifier.isInterface(k.getModifiers())) {
|
|
|
- return Collections.emptyMap();
|
|
|
- }
|
|
|
-
|
|
|
- // 普通对象类型
|
|
|
- Map<String, Field> all = Maps.newHashMap();
|
|
|
- boolean enumerable = Enum.class.isAssignableFrom(k);
|
|
|
+ public static Field findClassField(@NonNull Class<?> clazz, @NonNull Function<Field, Boolean> function) {
|
|
|
+ if (!Modifier.isInterface(clazz.getModifiers())) {
|
|
|
do {
|
|
|
- Field[] fields = k.getDeclaredFields();
|
|
|
+ Field[] fields = clazz.getDeclaredFields();
|
|
|
if (notEmpty(fields)) {
|
|
|
+ boolean enumerable = Enum.class.isAssignableFrom(clazz);
|
|
|
for (Field field : fields) {
|
|
|
if (!field.isSynthetic() && ((enumerable && field.isEnumConstant())
|
|
|
|| (!enumerable && !Modifier.isStatic(field.getModifiers())))) {
|
|
|
field.setAccessible(true);
|
|
|
- all.put(field.getName(), field);
|
|
|
+ if (Boolean.TRUE.equals(function.apply(field))) {
|
|
|
+ return field;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- } while ((k = k.getSuperclass()) != null && k != Enum.class && k != Object.class);
|
|
|
- return all.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(Maps.newHashMap(all));
|
|
|
+ } while ((clazz = clazz.getSuperclass()) != null && clazz != Enum.class && clazz != Object.class);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查找对象字段
|
|
|
+ *
|
|
|
+ * @param clazz 对象
|
|
|
+ * @param consumer 字段回调函数
|
|
|
+ */
|
|
|
+ public static void foreachClassFields(@NonNull Class<?> clazz, @NonNull Consumer<Field> consumer) {
|
|
|
+ findClassField(clazz, field -> {
|
|
|
+ consumer.accept(field);
|
|
|
+ return false;
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -543,7 +542,7 @@ public final class ObjectUtils {
|
|
|
* @param field 字段对象
|
|
|
* @return 字段值
|
|
|
*/
|
|
|
- public static Object getValue(Object object, @NonNull Field field) {
|
|
|
+ public static Object getObjectValue(Object object, @NonNull Field field) {
|
|
|
if (object == null) {
|
|
|
return null;
|
|
|
} else if (!field.isAccessible()) {
|
|
@@ -563,20 +562,8 @@ public final class ObjectUtils {
|
|
|
* @param property 属性名
|
|
|
* @return 属性值
|
|
|
*/
|
|
|
- public static Object getValue(Object object, @NonNull String property) {
|
|
|
- return object == null ? null : getValue(object, getField(object.getClass(), property));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 批量设置对象字段值
|
|
|
- *
|
|
|
- * @param object 对象实例
|
|
|
- * @param values 属性名/值映射表
|
|
|
- */
|
|
|
- public static void setValue(Object object, @NonNull Map<String, ?> values) {
|
|
|
- if (object != null && notEmpty(values)) {
|
|
|
- values.forEach((key, value) -> setValue(object, key, value));
|
|
|
- }
|
|
|
+ public static Object getObjectValue(Object object, @NonNull String property) {
|
|
|
+ return object == null ? null : getObjectValue(object, getClassField(object.getClass(), property));
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -586,7 +573,7 @@ public final class ObjectUtils {
|
|
|
* @param field 属性字段对象
|
|
|
* @param value 字段值
|
|
|
*/
|
|
|
- public static void setValue(Object object, @NonNull Field field, Object value) {
|
|
|
+ public static void setObjectValue(Object object, @NonNull Field field, Object value) {
|
|
|
if (object == null) {
|
|
|
return;
|
|
|
} else if (!field.isAccessible()) {
|
|
@@ -606,9 +593,9 @@ public final class ObjectUtils {
|
|
|
* @param property 属性名
|
|
|
* @param value 属性值
|
|
|
*/
|
|
|
- public static void setValue(Object object, @NonNull String property, Object value) {
|
|
|
+ public static void setObjectValue(Object object, @NonNull String property, Object value) {
|
|
|
if (object != null) {
|
|
|
- setValue(object, getField(object.getClass(), property), value);
|
|
|
+ setObjectValue(object, getClassField(object.getClass(), property), value);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -638,12 +625,12 @@ public final class ObjectUtils {
|
|
|
if (object == null) {
|
|
|
return null;
|
|
|
}
|
|
|
- Map<String, Field> fields = getAllFields(object.getClass());
|
|
|
+ Map<String, Field> fields = getClassFields(object.getClass());
|
|
|
if (isEmpty(fields)) {
|
|
|
return Collections.emptyMap();
|
|
|
}
|
|
|
Map<String, Object> map = Maps.newHashMapWithExpectedSize(fields.size());
|
|
|
- fields.forEach((name, field) -> map.put(name, getValue(object, field)));
|
|
|
+ fields.forEach((name, field) -> map.put(name, getObjectValue(object, field)));
|
|
|
return map;
|
|
|
}
|
|
|
|
|
@@ -658,18 +645,18 @@ public final class ObjectUtils {
|
|
|
if (object == null) {
|
|
|
return null;
|
|
|
}
|
|
|
- Map<String, Field> targets = getAllFields(type);
|
|
|
+ Map<String, Field> targets = getClassFields(type);
|
|
|
if (isEmpty(targets)) {
|
|
|
return Collections.emptyMap();
|
|
|
}
|
|
|
- Map<String, Field> fields = getAllFields(object.getClass());
|
|
|
+ Map<String, Field> fields = getClassFields(object.getClass());
|
|
|
if (isEmpty(fields)) {
|
|
|
return Collections.emptyMap();
|
|
|
}
|
|
|
Map<String, Object> map = Maps.newHashMapWithExpectedSize(fields.size());
|
|
|
fields.forEach((name, field) -> {
|
|
|
if (targets.containsKey(name)) {
|
|
|
- map.put(name, getValue(object, field));
|
|
|
+ map.put(name, getObjectValue(object, field));
|
|
|
}
|
|
|
});
|
|
|
return map.isEmpty() ? Collections.emptyMap() : map;
|
|
@@ -689,12 +676,12 @@ public final class ObjectUtils {
|
|
|
}
|
|
|
T instance = instance(type);
|
|
|
if (notEmpty(map)) {
|
|
|
- Map<String, Field> fields = getAllFields(type);
|
|
|
+ Map<String, Field> fields = getClassFields(type);
|
|
|
if (notEmpty(fields)) {
|
|
|
map.forEach((name, value) -> {
|
|
|
Field field = fields.get(name);
|
|
|
if (field != null) {
|
|
|
- setValue(instance, field, value);
|
|
|
+ setObjectValue(instance, field, value);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
@@ -909,8 +896,10 @@ public final class ObjectUtils {
|
|
|
*/
|
|
|
@SuppressWarnings("unchecked")
|
|
|
private static <M, T extends Schema<M>> T getProtostuffSchema(@NonNull Class<M> clazz) {
|
|
|
- T schema = (T) PROTOSTUFF_SCHEMA_MAPPING.get(clazz);
|
|
|
- return schema != null ? schema : (T) PROTOSTUFF_SCHEMA_MAPPING.computeIfAbsent(clazz, RuntimeSchema::getSchema);
|
|
|
+ T schema = (T) ObjectUtils.ifNull(PROTOSTUFF_SCHEMA_MAPPING.get(clazz), WeakReference::get);
|
|
|
+ return schema != null ? schema : (T) PROTOSTUFF_SCHEMA_MAPPING.computeIfAbsent(
|
|
|
+ clazz, k -> new WeakReference<>(RuntimeSchema.getSchema(clazz))
|
|
|
+ ).get();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -957,7 +946,8 @@ public final class ObjectUtils {
|
|
|
* @return 属性描述列表
|
|
|
*/
|
|
|
public static List<PropertyDescriptor> getPropertyDescriptors(@NonNull Class<?> clazz) {
|
|
|
- List<PropertyDescriptor> descriptors = PROPERTY_DESCRIPTOR_MAPPING.get(clazz);
|
|
|
+ List<PropertyDescriptor> descriptors =
|
|
|
+ ObjectUtils.ifNull(PROPERTY_DESCRIPTOR_MAPPING.get(clazz), WeakReference::get);
|
|
|
return descriptors != null ? descriptors : PROPERTY_DESCRIPTOR_MAPPING.computeIfAbsent(clazz, k -> {
|
|
|
BeanInfo bean;
|
|
|
try {
|
|
@@ -965,8 +955,8 @@ public final class ObjectUtils {
|
|
|
} catch (IntrospectionException e) {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
- return Arrays.asList(bean.getPropertyDescriptors());
|
|
|
- });
|
|
|
+ return new WeakReference<>(Arrays.asList(bean.getPropertyDescriptors()));
|
|
|
+ }).get();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -989,16 +979,16 @@ public final class ObjectUtils {
|
|
|
*/
|
|
|
private static SerializedLambda lookupFunctionLambda(Serializable function) {
|
|
|
Class<?> clazz = function.getClass();
|
|
|
- SerializedLambda lambda = LAMBDA_FUNCTION_MAPPING.get(clazz);
|
|
|
+ SerializedLambda lambda = ObjectUtils.ifNull(LAMBDA_FUNCTION_MAPPING.get(clazz), WeakReference::get);
|
|
|
return lambda != null ? lambda : LAMBDA_FUNCTION_MAPPING.computeIfAbsent(clazz, k -> {
|
|
|
try {
|
|
|
Method method = k.getDeclaredMethod("writeReplace");
|
|
|
method.setAccessible(true);
|
|
|
- return (SerializedLambda) method.invoke(function);
|
|
|
+ return new WeakReference<>((SerializedLambda) method.invoke(function));
|
|
|
} catch (ReflectiveOperationException e) {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
- });
|
|
|
+ }).get();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1434,7 +1424,7 @@ public final class ObjectUtils {
|
|
|
if (object == null || other == null) {
|
|
|
return Collections.emptyList();
|
|
|
}
|
|
|
- Map<String, Field> fields = getAllFields(object.getClass());
|
|
|
+ Map<String, Field> fields = getClassFields(object.getClass());
|
|
|
if (isEmpty(fields)) {
|
|
|
return Collections.emptyList();
|
|
|
}
|
|
@@ -1442,8 +1432,8 @@ public final class ObjectUtils {
|
|
|
Set<String> _excludes = excludes.length == 0 ? Collections.emptySet() : Sets.newHashSet(excludes);
|
|
|
fields.forEach((name, field) -> {
|
|
|
if (_excludes.isEmpty() || !_excludes.contains(name)) {
|
|
|
- Object before = getValue(object, field);
|
|
|
- Object after = getValue(other, field);
|
|
|
+ Object before = getObjectValue(object, field);
|
|
|
+ Object after = getObjectValue(other, field);
|
|
|
if (!equals(before, after)) {
|
|
|
modifications.add(Modification.builder().name(name).before(before).after(after).build());
|
|
|
}
|